aboutsummaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--.editorconfig8
-rw-r--r--LICENSES/LicenseRef-EPL-Steward.txt1
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts3
-rw-r--r--gradle/libs.versions.toml4
-rw-r--r--settings.gradle.kts7
-rw-r--r--subprojects/language-semantics/build.gradle.kts4
-rw-r--r--subprojects/store-query-viatra/build.gradle.kts8
-rw-r--r--subprojects/store-query-viatra/src/main/java/org/eclipse/viatra/query/runtime/rete/network/RefineryConnectionFactory.java34
-rw-r--r--subprojects/store-query-viatra/src/main/java/org/eclipse/viatra/query/runtime/rete/network/RefineryNodeFactory.java37
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java6
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java2
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java8
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java81
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java16
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java6
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java10
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java6
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java8
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java12
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java8
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java18
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java12
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java18
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java22
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java16
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java24
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/AbstractViatraMatcher.java4
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java4
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java16
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java53
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java6
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java6
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java2
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java16
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java2
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/CheckEvaluator.java2
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java68
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java22
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java16
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java2
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java2
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SymbolViewWrapper.java2
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java4
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java2
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefineryPBodyCopier.java35
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefineryPBodyNormalizer.java38
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefinerySurrogateQueryRewriter.java58
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteBackendFactory.java90
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteEngine.java134
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteRecipeCompiler.java228
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RefineryConnectionFactoryExtensions.java47
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RefineryNodeFactoryExtensions.java44
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesFactory.java22
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesFactoryImpl.java74
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesPackage.java41
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesPackageImpl.java96
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RepresentativeElectionRecipe.java17
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RepresentativeElectionRecipeImpl.java105
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java6
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java8
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/SymbolViewUpdateListener.java8
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingViewUpdateListener.java2
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingViewUpdateListener.java2
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java2
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java2
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java2
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java2
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtilsTest.java2
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java2
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java2
-rw-r--r--subprojects/viatra-runtime-base-itc/about.html26
-rw-r--r--subprojects/viatra-runtime-base-itc/build.gradle.kts13
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java226
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java259
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java308
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java223
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java110
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java36
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java645
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java146
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java38
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java173
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java67
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java31
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java60
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java151
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java93
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java179
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java146
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java37
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java81
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java77
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RepresentativeElectionAlgorithm.java)19
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java12
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/StronglyConnectedComponentAlgorithm.java)10
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/WeaklyConnectedComponentAlgorithm.java)4
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java64
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java160
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java185
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java37
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java110
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java70
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java55
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java82
-rw-r--r--subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java39
-rw-r--r--subprojects/viatra-runtime-base/about.html26
-rw-r--r--subprojects/viatra-runtime-base/build.gradle.kts19
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/ViatraBasePlugin.java46
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/BaseIndexOptions.java395
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/DataTypeListener.java44
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/EMFBaseIndexChangeListener.java33
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/FeatureListener.java48
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IEClassifierProcessor.java25
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IEMFIndexingErrorListener.java22
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IQueryResultSetter.java63
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IQueryResultUpdateListener.java45
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IStructuralFeatureInstanceProcessor.java19
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IndexingLevel.java113
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/InstanceListener.java41
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/LightweightEObjectObserver.java32
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/LightweightEObjectObserverAdapter.java74
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/NavigationHelper.java885
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/QueryResultAssociativeStore.java322
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/QueryResultMap.java210
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/TransitiveClosureHelper.java26
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/ViatraBaseFactory.java180
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/IBaseIndexFeatureFilter.java38
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/IBaseIndexObjectFilter.java30
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/IBaseIndexResourceFilter.java27
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/SimpleBaseIndexFilter.java46
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/profiler/BaseIndexProfiler.java79
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/profiler/ProfilerMode.java22
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFModelComprehension.java356
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFVisitor.java145
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/WellbehavingDerivedFeatureRegistry.java154
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/AbstractBaseIndexStore.java28
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFBaseIndexInstanceStore.java451
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFBaseIndexMetaStore.java380
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFBaseIndexStatisticsStore.java71
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFDataSource.java137
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperContentAdapter.java750
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperImpl.java1702
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperSetting.java73
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperType.java14
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperVisitor.java441
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/TransitiveClosureHelperImpl.java153
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/profiler/ProfilingNavigationHelperContentAdapter.java155
-rw-r--r--subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/exception/ViatraBaseException.java25
-rw-r--r--subprojects/viatra-runtime-localsearch/about.html26
-rw-r--r--subprojects/viatra-runtime-localsearch/build.gradle.kts15
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/ExecutionLoggerAdapter.java85
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/MatchingFrame.java122
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/exceptions/LocalSearchException.java33
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/CallWithAdornment.java55
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/ILocalSearchAdaptable.java29
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/ILocalSearchAdapter.java120
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/ISearchContext.java143
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java301
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/MatcherReference.java97
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/AbstractLocalSearchResultProvider.java532
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/AllValidAdornments.java37
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/DontFlattenDisjunctive.java29
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/DontFlattenIncrementalPredicate.java44
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/GenericLocalSearchResultProvider.java49
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/IAdornmentProvider.java72
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LazyPlanningAdornments.java41
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchBackend.java259
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchBackendFactoryProvider.java29
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchEMFBackendFactory.java65
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchGenericBackendFactory.java65
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchGenericBackendFactoryProvider.java24
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchHintOptions.java70
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchHints.java357
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchResultProvider.java56
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/CheckOperationExecutor.java50
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/ExtendOperationExecutor.java72
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/IIteratingSearchOperation.java27
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/IPatternMatcherOperation.java26
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/ISearchOperation.java88
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/MatchingFrameValueProvider.java41
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/AggregatorCheck.java116
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/BinaryTransitiveClosureCheck.java135
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/CheckConstant.java70
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/CheckPositivePatternCall.java96
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/ContainmentCheck.java85
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/CountCheck.java92
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/ExpressionCheck.java78
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/ExpressionEvalCheck.java95
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InequalityCheck.java77
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InstanceOfClassCheck.java75
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InstanceOfDataTypeCheck.java71
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InstanceOfJavaClassCheck.java71
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/NACOperation.java89
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/StructuralFeatureCheck.java91
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/nobase/ScopeCheck.java91
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/AggregatorExtend.java106
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/CountOperation.java88
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExpressionEval.java104
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendBinaryTransitiveClosure.java185
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendConstant.java75
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendPositivePatternCall.java118
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendToEStructuralFeatureSource.java112
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendToEStructuralFeatureTarget.java102
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverChildren.java90
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverContainers.java126
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverEClassInstances.java97
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverEDatatypeInstances.java96
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverEStructuralFeatureInstances.java115
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/SingleValueExtendOperationExecutor.java40
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/AbstractIteratingExtendOperationExecutor.java54
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/ExtendToEStructuralFeatureSource.java118
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/IterateOverEClassInstances.java93
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/IterateOverEDatatypeInstances.java123
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/generic/GenericTypeCheck.java96
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/generic/GenericTypeExtend.java145
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/generic/GenericTypeExtendSingleValue.java114
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/util/CallInformation.java186
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/IPlanDescriptor.java49
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/IPlanProvider.java33
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/PlanDescriptor.java84
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SearchPlan.java99
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SearchPlanExecutor.java210
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SearchPlanForBody.java115
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SimplePlanProvider.java49
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ILocalSearchPlanner.java38
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ISearchPlanCodeGenerator.java23
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchPlanner.java140
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchRuntimeBasedStrategy.java257
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintCategory.java41
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfo.java142
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfoInferrer.java326
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PlanState.java283
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/AbstractOperationCompiler.java430
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/EMFOperationCompiler.java198
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/GenericOperationCompiler.java102
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/IOperationCompiler.java53
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/IConstraintEvaluationContext.java63
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/ICostFunction.java22
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/HybridMatcherConstraintCostFunction.java91
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/IndexerBasedConstraintCostFunction.java49
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/StatisticsBasedConstraintCostFunction.java413
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/VariableBindingBasedCostFunction.java95
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/CompilerHelper.java209
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/OperationCostComparator.java26
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/profiler/LocalSearchProfilerAdapter.java83
-rw-r--r--subprojects/viatra-runtime-matchers/about.html26
-rw-r--r--subprojects/viatra-runtime-matchers/build.gradle.kts15
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java42
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java24
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java82
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java62
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java135
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java82
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java61
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java82
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java61
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java39
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java33
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java44
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java44
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java39
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java83
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java214
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java32
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java36
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java89
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java26
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java68
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java52
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java46
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java32
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java202
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java27
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java241
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java122
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java74
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java69
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java21
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java45
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java35
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java49
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java39
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java98
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java31
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java281
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java27
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java36
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java103
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java55
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java165
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java153
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java58
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java127
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java77
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java385
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java85
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java143
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java228
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java100
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java98
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java106
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java108
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java133
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java108
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java29
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java102
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java240
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java33
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java165
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java143
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java62
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java217
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java94
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java76
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java64
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java52
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java109
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java90
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java108
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java31
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java59
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java42
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java26
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java33
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java47
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java65
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java28
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java27
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java56
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java39
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java289
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java70
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java16
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java203
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java153
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java40
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java31
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java49
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java61
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java40
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java106
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java194
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java94
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java30
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java98
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java99
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java96
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java108
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java80
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java151
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java52
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java118
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java70
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java57
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java105
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java44
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java57
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java33
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java11
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java57
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java76
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RepresentativeElectionConstraint.java)14
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java79
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java231
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java104
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java105
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java35
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java68
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java110
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java154
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java101
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java37
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java35
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java53
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java23
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java23
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java129
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java48
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java19
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java50
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java55
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java33
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java59
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java135
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java26
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java68
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java306
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java310
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java27
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java64
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java253
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java31
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java63
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java88
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/IStorageBackend.java53
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleLocalStorageBackend.java51
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleRuntimeContext.java132
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/TabularRuntimeContext.java119
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/AbstractIndexTable.java266
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DefaultIndexTable.java143
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DisjointUnionTable.java192
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/IIndexTable.java212
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableContext.java29
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterBinary.java53
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterGeneric.java52
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterUnary.java52
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleBinaryTable.java320
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleUnaryTable.java140
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java136
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java20
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java65
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java60
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java46
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java44
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java51
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java55
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java59
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java27
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java64
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java172
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple1.java83
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple2.java73
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple3.java81
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple4.java88
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/MaskedTuple.java48
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuple.java69
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java560
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java56
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java51
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java48
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java157
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java50
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java47
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java47
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java48
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java23
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java188
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java61
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java86
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java41
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java159
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java150
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java212
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java226
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java93
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java94
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java93
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java32
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java26
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java81
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java205
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java216
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java485
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java30
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java30
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java37
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java102
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java21
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java117
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java208
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java44
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java90
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java60
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java29
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java105
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java517
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java36
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java27
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java36
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java111
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java55
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java73
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java146
-rw-r--r--subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java46
-rw-r--r--subprojects/viatra-runtime-rete-recipes/META-INF/MANIFEST.MF18
-rw-r--r--subprojects/viatra-runtime-rete-recipes/META-INF/MANIFEST.MF.license4
-rw-r--r--subprojects/viatra-runtime-rete-recipes/about.html26
-rw-r--r--subprojects/viatra-runtime-rete-recipes/build.gradle.kts62
-rw-r--r--subprojects/viatra-runtime-rete-recipes/build.properties15
-rw-r--r--subprojects/viatra-runtime-rete-recipes/plugin.properties9
-rw-r--r--subprojects/viatra-runtime-rete-recipes/plugin.xml23
-rw-r--r--subprojects/viatra-runtime-rete-recipes/src/main/java/tools/refinery/viatra/runtime/rete/recipes/GenerateReteRecipes.mwe225
-rw-r--r--subprojects/viatra-runtime-rete-recipes/src/main/java/tools/refinery/viatra/runtime/rete/recipes/helper/RecipeRecognizer.java204
-rw-r--r--subprojects/viatra-runtime-rete-recipes/src/main/java/tools/refinery/viatra/runtime/rete/recipes/helper/RecipesHelper.java81
-rw-r--r--subprojects/viatra-runtime-rete-recipes/src/main/resources/model/recipes.ecore400
-rw-r--r--subprojects/viatra-runtime-rete-recipes/src/main/resources/model/recipes.ecore.license4
-rw-r--r--subprojects/viatra-runtime-rete-recipes/src/main/resources/model/rete-recipes.genmodel171
-rw-r--r--subprojects/viatra-runtime-rete-recipes/src/main/resources/model/rete-recipes.genmodel.license4
-rw-r--r--subprojects/viatra-runtime-rete/about.html26
-rw-r--r--subprojects/viatra-runtime-rete/build.gradle.kts15
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/AbstractColumnAggregatorNode.java474
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/ColumnAggregatorNode.java369
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/CountNode.java38
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedMap.java120
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedSet.java114
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IAggregatorNode.java26
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IndexerBasedAggregatorNode.java278
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java217
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulSequentialTimelyColumnAggregatorNode.java280
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulTimelyColumnAggregatorNode.java247
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyParallelTimelyColumnAggregatorNode.java106
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlySequentialTimelyColumnAggregatorNode.java117
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyTimelyColumnAggregatorNode.java212
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/Disconnectable.java26
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputEnumeratorNode.java209
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputStatelessFilterNode.java68
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/InputConnector.java208
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ReteBoundary.java551
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/RetePatternBuildException.java50
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/BasicLinearLayout.java171
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/OrderingHeuristics.java90
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java390
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/RecursionCutoffPoint.java86
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java948
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinCandidate.java162
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinOrderingHeuristics.java49
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/QuasiTreeLayout.java205
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/TieBreaker.java30
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/AbstractEvaluatorNode.java65
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/EvaluatorCore.java180
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/IEvaluatorNode.java25
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/MemorylessEvaluatorNode.java75
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/OutputCachingEvaluatorNode.java311
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/RelationEvaluatorNode.java183
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DefaultIndexerListener.java28
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DualInputNode.java348
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ExistenceNode.java199
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/GenericProjectionIndexer.java76
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IdentityIndexer.java76
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/Indexer.java71
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerListener.java41
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerWithMemory.java284
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IterableIndexer.java34
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/JoinNode.java193
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryIdentityIndexer.java55
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryNullIndexer.java54
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/NullIndexer.java88
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/OnetimeIndexer.java47
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ProjectionIndexer.java21
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/SpecializedProjectionIndexer.java176
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/StandardIndexer.java127
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java121
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryIdentityIndexer.java51
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryNullIndexer.java49
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/DRedReteBackendFactory.java49
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/HintConfigurator.java46
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/IncrementalMatcherCapability.java30
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactory.java100
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactoryProvider.java35
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java579
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java462
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyConfiguration.java61
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyReteBackendFactory.java64
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/Bag.java43
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/ConstantNode.java50
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DefaultDeltaMonitor.java43
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DeltaMonitor.java111
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/SimpleReceiver.java109
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/BaseNode.java108
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java170
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/IGroupable.java31
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Network.java408
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NetworkStructureChangeSensitiveNode.java30
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Node.java62
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java375
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java346
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/PosetAwareReceiver.java39
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ProductionNode.java28
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Receiver.java85
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/RederivableNode.java34
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReinitializedNode.java14
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java729
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java121
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Supplier.java82
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Tunnel.java19
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/UpdateMessage.java31
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationGroup.java103
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java467
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/MessageSelector.java19
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/NodeComparator.java32
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/PhasedSelector.java34
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/Timestamp.java124
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/RecursiveCommunicationGroup.java164
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/SingletonCommunicationGroup.java86
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/TimelessCommunicationTracker.java149
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/ResumableNode.java36
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationGroup.java171
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java216
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyIndexerListenerProxy.java81
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyMailboxProxy.java102
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimestampTransformation.java48
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedCommand.java81
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedConnectCommand.java27
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedDisconnectCommand.java30
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/DefaultMessageIndexer.java74
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/GroupBasedMessageIndexer.java95
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/MessageIndexer.java33
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/AdaptableMailbox.java32
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/FallThroughCapableMailbox.java30
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/Mailbox.java78
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/MessageIndexerFactory.java23
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/AbstractUpdateSplittingMailbox.java109
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/BehaviorChangingMailbox.java117
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/DefaultMailbox.java164
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/PosetAwareMailbox.java218
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/UpdateSplittingMailbox.java135
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timely/TimelyMailbox.java150
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/Address.java125
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteReceiver.java63
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteSupplier.java54
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/AbstractUniquenessEnforcerNode.java138
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/CallbackNode.java37
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DefaultProductionNode.java79
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorBucketNode.java85
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorDispatcherNode.java154
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/EqualityFilterNode.java41
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/FilterNode.java69
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/InequalityFilterNode.java52
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RepresentativeElectionNode.java)27
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/SingleInputNode.java126
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyProductionNode.java63
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyUniquenessEnforcerNode.java161
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransformerNode.java49
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java147
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransparentNode.java48
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TrimmerNode.java61
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/UniquenessEnforcerNode.java321
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/ValueBinderFilterNode.java44
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ActiveNodeConflictTrace.java24
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledQuery.java54
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledSubPlan.java51
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ParameterProjectionTrace.java42
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PatternTraceInfo.java17
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PlanningTrace.java80
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/RecipeTraceInfo.java81
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/TraceInfo.java33
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/UserRequestTrace.java36
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/LexicographicComparator.java58
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/Options.java111
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/OrderingCompareAgent.java92
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/ReteHintOptions.java60
-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
736 files changed, 74363 insertions, 1473 deletions
diff --git a/.editorconfig b/.editorconfig
index 7e5e9f16..7bf4c4d5 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -25,4 +25,10 @@ indent_style = space
25indent_size = 2 25indent_size = 2
26 26
27[libs.versions.toml] 27[libs.versions.toml]
28max_line_length = 999 \ No newline at end of file 28max_line_length = 999
29
30[*.{ecore,genmodel}]
31max_line_length = 999
32
33[*.mwe2]
34max_line_length = 999
diff --git a/LICENSES/LicenseRef-EPL-Steward.txt b/LICENSES/LicenseRef-EPL-Steward.txt
new file mode 100644
index 00000000..4b81ef82
--- /dev/null
+++ b/LICENSES/LicenseRef-EPL-Steward.txt
@@ -0,0 +1 @@
Everyone is permitted to copy and distribute copies of the Agreement; however, in order to avoid inconsistency, the agreement is copyrighted and may only be modified by the Agreement Steward who reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify the Agreement.
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts
index 2d41af11..20c404a0 100644
--- a/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts
@@ -17,9 +17,6 @@ plugins {
17 17
18repositories { 18repositories {
19 mavenCentral() 19 mavenCentral()
20 maven {
21 url = uri("https://repo.eclipse.org/content/groups/releases/")
22 }
23} 20}
24 21
25// Use log4j-over-slf4j instead of log4j 1.x in the tests. 22// Use log4j-over-slf4j instead of log4j 1.x in the tests.
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index ffd8e356..0b93663c 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -15,11 +15,13 @@ xtext = "2.32.0.M2"
15 15
16[libraries] 16[libraries]
17jetbrainsAnnotations = { group = "org.jetbrains", name = "annotations", version = "24.0.1" } 17jetbrainsAnnotations = { group = "org.jetbrains", name = "annotations", version = "24.0.1" }
18eclipse = { group = "org.eclipse.platform", name = "org.eclipse.core.runtime", version = "3.27.0" }
18eclipseCollections = { group = "org.eclipse.collections", name = "eclipse-collections", version.ref = "eclipseCollections" } 19eclipseCollections = { group = "org.eclipse.collections", name = "eclipse-collections", version.ref = "eclipseCollections" }
19eclipseCollections-api = { group = "org.eclipse.collections", name = "eclipse-collections-api", version.ref = "eclipseCollections" } 20eclipseCollections-api = { group = "org.eclipse.collections", name = "eclipse-collections-api", version.ref = "eclipseCollections" }
20ecore = { group = "org.eclipse.emf", name = "org.eclipse.emf.ecore", version.ref = "ecore" } 21ecore = { group = "org.eclipse.emf", name = "org.eclipse.emf.ecore", version.ref = "ecore" }
21ecore-xmi = { group = "org.eclipse.emf", name = "org.eclipse.emf.ecore.xmi", version = "2.18.0" } 22ecore-xmi = { group = "org.eclipse.emf", name = "org.eclipse.emf.ecore.xmi", version = "2.18.0" }
22ecore-codegen = { group = "org.eclipse.emf", name = "org.eclipse.emf.codegen.ecore", version.ref = "ecore" } 23ecore-codegen = { group = "org.eclipse.emf", name = "org.eclipse.emf.codegen.ecore", version.ref = "ecore" }
24emf = { group = "org.eclipse.emf", name = "org.eclipse.emf.common", version = "2.28.0" }
23gradlePlugin-frontend = { group = "org.siouan", name = "frontend-gradle-plugin-jdk11", version = "6.0.0" } 25gradlePlugin-frontend = { group = "org.siouan", name = "frontend-gradle-plugin-jdk11", version = "6.0.0" }
24gradlePlugin-shadow = { group = "com.github.johnrengelman", name = "shadow", version = "8.1.1" } 26gradlePlugin-shadow = { group = "com.github.johnrengelman", name = "shadow", version = "8.1.1" }
25gradlePlugin-sonarqube = { group = "org.sonarsource.scanner.gradle", name = "sonarqube-gradle-plugin", version = "4.3.0.3225" } 27gradlePlugin-sonarqube = { group = "org.sonarsource.scanner.gradle", name = "sonarqube-gradle-plugin", version = "4.3.0.3225" }
@@ -39,10 +41,10 @@ mockito-junit = { group = "org.mockito", name = "mockito-junit-jupiter", version
39mwe-utils = { group = "org.eclipse.emf", name = "org.eclipse.emf.mwe.utils", version = "1.9.0.M2" } 41mwe-utils = { group = "org.eclipse.emf", name = "org.eclipse.emf.mwe.utils", version = "1.9.0.M2" }
40mwe2-launch = { group = "org.eclipse.emf", name = "org.eclipse.emf.mwe2.launch", version.ref = "mwe2" } 42mwe2-launch = { group = "org.eclipse.emf", name = "org.eclipse.emf.mwe2.launch", version.ref = "mwe2" }
41mwe2-lib = { group = "org.eclipse.emf", name = "org.eclipse.emf.mwe2.lib", version.ref = "mwe2" } 43mwe2-lib = { group = "org.eclipse.emf", name = "org.eclipse.emf.mwe2.lib", version.ref = "mwe2" }
44osgi = { group = "org.osgi", name = "org.osgi.framework", version = "1.10.0" }
42slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" } 45slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" }
43slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" } 46slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
44slf4j-log4j = { group = "org.slf4j", name = "log4j-over-slf4j", version.ref = "slf4j" } 47slf4j-log4j = { group = "org.slf4j", name = "log4j-over-slf4j", version.ref = "slf4j" }
45viatra = { group = "org.eclipse.viatra", name = "viatra-query-runtime", version = "2.7.0" }
46xtext-bom = { group = "org.eclipse.xtext", name = "xtext-dev-bom", version.ref = "xtext" } 48xtext-bom = { group = "org.eclipse.xtext", name = "xtext-dev-bom", version.ref = "xtext" }
47xtext-core = { group = "org.eclipse.xtext", name = "org.eclipse.xtext", version.ref = "xtext" } 49xtext-core = { group = "org.eclipse.xtext", name = "org.eclipse.xtext", version.ref = "xtext" }
48xtext-generator-antlr = { group = "org.eclipse.xtext", name = "xtext-antlr-generator", version = "2.1.1" } 50xtext-generator-antlr = { group = "org.eclipse.xtext", name = "xtext-antlr-generator", version = "2.1.1" }
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 2b1c179d..cf31fc7e 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -17,6 +17,13 @@ include(
17 "store-query", 17 "store-query",
18 "store-query-viatra", 18 "store-query-viatra",
19 "store-reasoning", 19 "store-reasoning",
20 "viatra-runtime",
21 "viatra-runtime-base",
22 "viatra-runtime-base-itc",
23 "viatra-runtime-localsearch",
24 "viatra-runtime-matchers",
25 "viatra-runtime-rete",
26 "viatra-runtime-rete-recipes",
20) 27)
21 28
22for (project in rootProject.children) { 29for (project in rootProject.children) {
diff --git a/subprojects/language-semantics/build.gradle.kts b/subprojects/language-semantics/build.gradle.kts
index 23668f30..22c03985 100644
--- a/subprojects/language-semantics/build.gradle.kts
+++ b/subprojects/language-semantics/build.gradle.kts
@@ -9,11 +9,11 @@ plugins {
9} 9}
10 10
11dependencies { 11dependencies {
12 implementation(libs.eclipseCollections) 12 api(libs.eclipseCollections.api)
13 implementation(libs.eclipseCollections.api)
14 api(project(":refinery-language")) 13 api(project(":refinery-language"))
15 api(project(":refinery-store")) 14 api(project(":refinery-store"))
16 api(project(":refinery-store-query")) 15 api(project(":refinery-store-query"))
17 api(project(":refinery-store-reasoning")) 16 api(project(":refinery-store-reasoning"))
17 implementation(libs.eclipseCollections)
18 testImplementation(testFixtures(project(":refinery-language"))) 18 testImplementation(testFixtures(project(":refinery-language")))
19} 19}
diff --git a/subprojects/store-query-viatra/build.gradle.kts b/subprojects/store-query-viatra/build.gradle.kts
index e3a22145..fa1c1da3 100644
--- a/subprojects/store-query-viatra/build.gradle.kts
+++ b/subprojects/store-query-viatra/build.gradle.kts
@@ -9,7 +9,11 @@ plugins {
9} 9}
10 10
11dependencies { 11dependencies {
12 implementation(libs.ecore) 12 api(project(":refinery-viatra-runtime"))
13 api(libs.viatra) 13 api(project(":refinery-viatra-runtime-localsearch"))
14 api(project(":refinery-viatra-runtime-rete"))
15 api(project(":refinery-viatra-runtime-rete-recipes"))
14 api(project(":refinery-store-query")) 16 api(project(":refinery-store-query"))
17 implementation(libs.ecore)
18 implementation(libs.slf4j.log4j)
15} 19}
diff --git a/subprojects/store-query-viatra/src/main/java/org/eclipse/viatra/query/runtime/rete/network/RefineryConnectionFactory.java b/subprojects/store-query-viatra/src/main/java/org/eclipse/viatra/query/runtime/rete/network/RefineryConnectionFactory.java
deleted file mode 100644
index 241c5b58..00000000
--- a/subprojects/store-query-viatra/src/main/java/org/eclipse/viatra/query/runtime/rete/network/RefineryConnectionFactory.java
+++ /dev/null
@@ -1,34 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package org.eclipse.viatra.query.runtime.rete.network;
7
8import org.eclipse.viatra.query.runtime.rete.traceability.RecipeTraceInfo;
9import tools.refinery.store.query.viatra.internal.rete.network.RefineryConnectionFactoryExtensions;
10
11/**
12 * This class overrides some RETE connection types from {@link ConnectionFactory}.
13 * <p>
14 * Since {@link ConnectionFactory} is package-private, this class has to be in the
15 * {@code org.eclipse.viatra.query.runtime.rete.network} package as well.
16 * However, due to JAR signature verification errors, <b>this class cannot be loaded directly</b>
17 * and has to be loaded at runtime as a byte array instead.
18 */
19@SuppressWarnings("unused")
20public class RefineryConnectionFactory extends ConnectionFactory {
21 private final RefineryConnectionFactoryExtensions extensions;
22
23 public RefineryConnectionFactory(ReteContainer reteContainer) {
24 super(reteContainer);
25 extensions = new RefineryConnectionFactoryExtensions(reteContainer);
26 }
27
28 @Override
29 public void connectToParents(RecipeTraceInfo recipeTrace, Node freshNode) {
30 if (!extensions.connectToParents(recipeTrace, freshNode)) {
31 super.connectToParents(recipeTrace, freshNode);
32 }
33 }
34}
diff --git a/subprojects/store-query-viatra/src/main/java/org/eclipse/viatra/query/runtime/rete/network/RefineryNodeFactory.java b/subprojects/store-query-viatra/src/main/java/org/eclipse/viatra/query/runtime/rete/network/RefineryNodeFactory.java
deleted file mode 100644
index 1a76e22a..00000000
--- a/subprojects/store-query-viatra/src/main/java/org/eclipse/viatra/query/runtime/rete/network/RefineryNodeFactory.java
+++ /dev/null
@@ -1,37 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package org.eclipse.viatra.query.runtime.rete.network;
7
8import org.apache.log4j.Logger;
9import org.eclipse.viatra.query.runtime.rete.recipes.ReteNodeRecipe;
10import org.eclipse.viatra.query.runtime.rete.traceability.TraceInfo;
11import tools.refinery.store.query.viatra.internal.rete.network.RefineryNodeFactoryExtensions;
12
13/**
14 * This class overrides some RETE node types from {@link NodeFactory}.
15 * <p>
16 * Since {@link NodeFactory} is package-private, this class has to be in the
17 * {@code org.eclipse.viatra.query.runtime.rete.network} package as well.
18 * However, due to JAR signature verification errors, <b>this class cannot be loaded directly</b>
19 * and has to be loaded at runtime as a byte array instead.
20 */
21@SuppressWarnings("unused")
22class RefineryNodeFactory extends NodeFactory {
23 private final RefineryNodeFactoryExtensions extensions = new RefineryNodeFactoryExtensions();
24
25 public RefineryNodeFactory(Logger logger) {
26 super(logger);
27 }
28
29 @Override
30 public Supplier createNode(ReteContainer reteContainer, ReteNodeRecipe recipe, TraceInfo... traces) {
31 var extendedResult = extensions.createNode(reteContainer, recipe, traces);
32 if (extendedResult != null) {
33 return extendedResult;
34 }
35 return super.createNode(reteContainer, recipe, traces);
36 }
37}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
index 66279c94..d31325f1 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
@@ -5,9 +5,9 @@
5 */ 5 */
6package tools.refinery.store.query.viatra; 6package tools.refinery.store.query.viatra;
7 7
8import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; 8import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions;
9import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; 9import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
10import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 10import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
11import tools.refinery.store.model.ModelStore; 11import tools.refinery.store.model.ModelStore;
12import tools.refinery.store.query.ModelQueryBuilder; 12import tools.refinery.store.query.ModelQueryBuilder;
13import tools.refinery.store.query.dnf.AnyQuery; 13import tools.refinery.store.query.dnf.AnyQuery;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java
index da6d7bd5..588c00d4 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra; 6package tools.refinery.store.query.viatra;
7 7
8import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; 8import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions;
9import tools.refinery.store.model.Model; 9import tools.refinery.store.model.Model;
10import tools.refinery.store.query.ModelQueryStoreAdapter; 10import tools.refinery.store.query.ModelQueryStoreAdapter;
11 11
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java
index d1a65a89..9303cae6 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java
@@ -6,10 +6,10 @@
6package tools.refinery.store.query.viatra.internal; 6package tools.refinery.store.query.viatra.internal;
7 7
8import org.apache.log4j.Logger; 8import org.apache.log4j.Logger;
9import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; 9import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
10import org.eclipse.viatra.query.runtime.api.scope.IEngineContext; 10import tools.refinery.viatra.runtime.api.scope.IEngineContext;
11import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener; 11import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener;
12import org.eclipse.viatra.query.runtime.api.scope.QueryScope; 12import tools.refinery.viatra.runtime.api.scope.QueryScope;
13import tools.refinery.store.query.viatra.internal.context.RelationalEngineContext; 13import tools.refinery.store.query.viatra.internal.context.RelationalEngineContext;
14 14
15public class RelationalScope extends QueryScope { 15public class RelationalScope extends QueryScope {
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
index e17386e1..f1209f69 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
@@ -5,69 +5,46 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal; 6package tools.refinery.store.query.viatra.internal;
7 7
8import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine;
9import org.eclipse.viatra.query.runtime.api.GenericQueryGroup;
10import org.eclipse.viatra.query.runtime.api.IQuerySpecification;
11import org.eclipse.viatra.query.runtime.internal.apiimpl.ViatraQueryEngineImpl;
12import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackend;
13import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
14import tools.refinery.store.model.Model; 8import tools.refinery.store.model.Model;
15import tools.refinery.store.model.ModelListener; 9import tools.refinery.store.model.ModelListener;
16import tools.refinery.store.query.resultset.AnyResultSet;
17import tools.refinery.store.query.resultset.EmptyResultSet;
18import tools.refinery.store.query.resultset.ResultSet;
19import tools.refinery.store.query.dnf.AnyQuery; 10import tools.refinery.store.query.dnf.AnyQuery;
20import tools.refinery.store.query.dnf.FunctionalQuery; 11import tools.refinery.store.query.dnf.FunctionalQuery;
21import tools.refinery.store.query.dnf.Query; 12import tools.refinery.store.query.dnf.Query;
22import tools.refinery.store.query.dnf.RelationalQuery; 13import tools.refinery.store.query.dnf.RelationalQuery;
14import tools.refinery.store.query.resultset.AnyResultSet;
15import tools.refinery.store.query.resultset.EmptyResultSet;
16import tools.refinery.store.query.resultset.ResultSet;
23import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; 17import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
24import tools.refinery.store.query.viatra.internal.matcher.FunctionalViatraMatcher; 18import tools.refinery.store.query.viatra.internal.matcher.FunctionalViatraMatcher;
25import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; 19import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
26import tools.refinery.store.query.viatra.internal.matcher.RelationalViatraMatcher; 20import tools.refinery.store.query.viatra.internal.matcher.RelationalViatraMatcher;
21import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine;
22import tools.refinery.viatra.runtime.api.GenericQueryGroup;
23import tools.refinery.viatra.runtime.api.IQuerySpecification;
27 24
28import java.lang.invoke.MethodHandle;
29import java.lang.invoke.MethodHandles;
30import java.util.Collection;
31import java.util.Collections; 25import java.util.Collections;
32import java.util.LinkedHashMap; 26import java.util.LinkedHashMap;
33import java.util.Map; 27import java.util.Map;
34 28
35public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter, ModelListener { 29public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter, ModelListener {
36 private static final String DELAY_MESSAGE_DELIVERY_FIELD_NAME = "delayMessageDelivery";
37 private static final MethodHandle SET_UPDATE_PROPAGATION_DELAYED_HANDLE;
38 private static final String QUERY_BACKENDS_FIELD_NAME = "queryBackends";
39 private static final MethodHandle GET_QUERY_BACKENDS_HANDLE;
40
41 private final Model model; 30 private final Model model;
42 private final ViatraModelQueryStoreAdapterImpl storeAdapter; 31 private final ViatraModelQueryStoreAdapterImpl storeAdapter;
43 private final ViatraQueryEngineImpl queryEngine; 32 private final AdvancedViatraQueryEngine queryEngine;
44 private final Map<AnyQuery, AnyResultSet> resultSets; 33 private final Map<AnyQuery, AnyResultSet> resultSets;
45 private boolean pendingChanges; 34 private boolean pendingChanges;
46 35
47 static {
48 try {
49 var lookup = MethodHandles.privateLookupIn(ViatraQueryEngineImpl.class, MethodHandles.lookup());
50 SET_UPDATE_PROPAGATION_DELAYED_HANDLE = lookup.findSetter(ViatraQueryEngineImpl.class,
51 DELAY_MESSAGE_DELIVERY_FIELD_NAME, Boolean.TYPE);
52 GET_QUERY_BACKENDS_HANDLE = lookup.findGetter(ViatraQueryEngineImpl.class, QUERY_BACKENDS_FIELD_NAME,
53 Map.class);
54 } catch (IllegalAccessException | NoSuchFieldException e) {
55 throw new IllegalStateException("Cannot access private members of %s"
56 .formatted(ViatraQueryEngineImpl.class.getName()), e);
57 }
58 }
59
60 ViatraModelQueryAdapterImpl(Model model, ViatraModelQueryStoreAdapterImpl storeAdapter) { 36 ViatraModelQueryAdapterImpl(Model model, ViatraModelQueryStoreAdapterImpl storeAdapter) {
61 this.model = model; 37 this.model = model;
62 this.storeAdapter = storeAdapter; 38 this.storeAdapter = storeAdapter;
63 var scope = new RelationalScope(this); 39 var scope = new RelationalScope(this);
64 queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope, 40 queryEngine = AdvancedViatraQueryEngine.createUnmanagedEngine(scope,
65 storeAdapter.getEngineOptions()); 41 storeAdapter.getEngineOptions());
66 42
67 var querySpecifications = storeAdapter.getQuerySpecifications(); 43 var querySpecifications = storeAdapter.getQuerySpecifications();
68 GenericQueryGroup.of( 44 GenericQueryGroup.of(
69 Collections.<IQuerySpecification<?>>unmodifiableCollection(querySpecifications.values()).stream() 45 Collections.<IQuerySpecification<?>>unmodifiableCollection(querySpecifications.values()).stream()
70 ).prepare(queryEngine); 46 ).prepare(queryEngine);
47 queryEngine.flushChanges();
71 var vacuousQueries = storeAdapter.getVacuousQueries(); 48 var vacuousQueries = storeAdapter.getVacuousQueries();
72 resultSets = new LinkedHashMap<>(querySpecifications.size() + vacuousQueries.size()); 49 resultSets = new LinkedHashMap<>(querySpecifications.size() + vacuousQueries.size());
73 for (var entry : querySpecifications.entrySet()) { 50 for (var entry : querySpecifications.entrySet()) {
@@ -79,7 +56,6 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter, Mod
79 resultSets.put(vacuousQuery, new EmptyResultSet<>(this, (Query<?>) vacuousQuery)); 56 resultSets.put(vacuousQuery, new EmptyResultSet<>(this, (Query<?>) vacuousQuery));
80 } 57 }
81 58
82 setUpdatePropagationDelayed(true);
83 model.addListener(this); 59 model.addListener(this);
84 } 60 }
85 61
@@ -95,30 +71,6 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter, Mod
95 } 71 }
96 } 72 }
97 73
98 private void setUpdatePropagationDelayed(boolean value) {
99 try {
100 SET_UPDATE_PROPAGATION_DELAYED_HANDLE.invokeExact(queryEngine, value);
101 } catch (Error e) {
102 // Fatal JVM errors should not be wrapped.
103 throw e;
104 } catch (Throwable e) {
105 throw new IllegalStateException("Cannot set %s".formatted(DELAY_MESSAGE_DELIVERY_FIELD_NAME), e);
106 }
107 }
108
109 private Collection<IQueryBackend> getQueryBackends() {
110 try {
111 @SuppressWarnings("unchecked")
112 var backendMap = (Map<IQueryBackendFactory, IQueryBackend>) GET_QUERY_BACKENDS_HANDLE.invokeExact(queryEngine);
113 return backendMap.values();
114 } catch (Error e) {
115 // Fatal JVM errors should not be wrapped.
116 throw e;
117 } catch (Throwable e) {
118 throw new IllegalStateException("Cannot get %s".formatted(QUERY_BACKENDS_FIELD_NAME), e);
119 }
120 }
121
122 @Override 74 @Override
123 public Model getModel() { 75 public Model getModel() {
124 return model; 76 return model;
@@ -154,20 +106,7 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter, Mod
154 106
155 @Override 107 @Override
156 public void flushChanges() { 108 public void flushChanges() {
157 if (!queryEngine.isUpdatePropagationDelayed()) { 109 queryEngine.flushChanges();
158 throw new IllegalStateException("Trying to flush changes while changes are already being flushed");
159 }
160 if (!pendingChanges) {
161 return;
162 }
163 setUpdatePropagationDelayed(false);
164 try {
165 for (var queryBackend : getQueryBackends()) {
166 queryBackend.flushUpdates();
167 }
168 } finally {
169 setUpdatePropagationDelayed(true);
170 }
171 pendingChanges = false; 110 pendingChanges = false;
172 } 111 }
173 112
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
index bf0708bf..c8aa067c 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
@@ -5,11 +5,6 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal; 6package tools.refinery.store.query.viatra.internal;
7 7
8import org.eclipse.viatra.query.runtime.api.IQuerySpecification;
9import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
10import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHintOptions;
11import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
12import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
13import tools.refinery.store.adapter.AbstractModelAdapterBuilder; 8import tools.refinery.store.adapter.AbstractModelAdapterBuilder;
14import tools.refinery.store.model.ModelStore; 9import tools.refinery.store.model.ModelStore;
15import tools.refinery.store.query.dnf.AnyQuery; 10import tools.refinery.store.query.dnf.AnyQuery;
@@ -23,7 +18,12 @@ import tools.refinery.store.query.viatra.internal.localsearch.FlatCostFunction;
23import tools.refinery.store.query.viatra.internal.localsearch.RelationalLocalSearchBackendFactory; 18import tools.refinery.store.query.viatra.internal.localsearch.RelationalLocalSearchBackendFactory;
24import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; 19import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
25import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery; 20import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery;
26import tools.refinery.store.query.viatra.internal.rete.RefineryReteBackendFactory; 21import tools.refinery.viatra.runtime.api.IQuerySpecification;
22import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions;
23import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHintOptions;
24import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
25import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
26import tools.refinery.viatra.runtime.rete.matcher.ReteBackendFactory;
27 27
28import java.util.*; 28import java.util.*;
29import java.util.function.Function; 29import java.util.function.Function;
@@ -41,8 +41,8 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder<Via
41 41
42 public ViatraModelQueryBuilderImpl() { 42 public ViatraModelQueryBuilderImpl() {
43 engineOptionsBuilder = new ViatraQueryEngineOptions.Builder() 43 engineOptionsBuilder = new ViatraQueryEngineOptions.Builder()
44 .withDefaultBackend(RefineryReteBackendFactory.INSTANCE) 44 .withDefaultBackend(ReteBackendFactory.INSTANCE)
45 .withDefaultCachingBackend(RefineryReteBackendFactory.INSTANCE) 45 .withDefaultCachingBackend(ReteBackendFactory.INSTANCE)
46 .withDefaultSearchBackend(RelationalLocalSearchBackendFactory.INSTANCE); 46 .withDefaultSearchBackend(RelationalLocalSearchBackendFactory.INSTANCE);
47 rewriter = new CompositeRewriter(); 47 rewriter = new CompositeRewriter();
48 rewriter.addFirst(new DuplicateDnfRemover()); 48 rewriter.addFirst(new DuplicateDnfRemover());
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
index 25f1cd02..5407ad01 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
@@ -5,9 +5,9 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal; 6package tools.refinery.store.query.viatra.internal;
7 7
8import org.eclipse.viatra.query.runtime.api.IQuerySpecification; 8import tools.refinery.viatra.runtime.api.IQuerySpecification;
9import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; 9import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions;
10import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 10import tools.refinery.viatra.runtime.matchers.context.IInputKey;
11import tools.refinery.store.model.Model; 11import tools.refinery.store.model.Model;
12import tools.refinery.store.model.ModelStore; 12import tools.refinery.store.model.ModelStore;
13import tools.refinery.store.query.dnf.AnyQuery; 13import tools.refinery.store.query.dnf.AnyQuery;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java
index 8cb199d2..64d98258 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java
@@ -5,16 +5,16 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.context; 6package tools.refinery.store.query.viatra.internal.context;
7 7
8import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex; 8import tools.refinery.viatra.runtime.api.scope.IBaseIndex;
9import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener; 9import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener;
10import org.eclipse.viatra.query.runtime.api.scope.IInstanceObserver; 10import tools.refinery.viatra.runtime.api.scope.IInstanceObserver;
11import org.eclipse.viatra.query.runtime.api.scope.ViatraBaseIndexChangeListener; 11import tools.refinery.viatra.runtime.api.scope.ViatraBaseIndexChangeListener;
12 12
13import java.lang.reflect.InvocationTargetException; 13import java.lang.reflect.InvocationTargetException;
14import java.util.concurrent.Callable; 14import java.util.concurrent.Callable;
15 15
16/** 16/**
17 * Copied from <code>org.eclipse.viatra.query.runtime.tabular.TabularEngineContext</code> 17 * Copied from <code>tools.refinery.viatra.runtime.tabular.TabularEngineContext</code>
18 */ 18 */
19public class DummyBaseIndexer implements IBaseIndex { 19public class DummyBaseIndexer implements IBaseIndex {
20 DummyBaseIndexer() { 20 DummyBaseIndexer() {
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java
index 7220f8ca..f7d323ff 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java
@@ -5,9 +5,9 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.context; 6package tools.refinery.store.query.viatra.internal.context;
7 7
8import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex; 8import tools.refinery.viatra.runtime.api.scope.IBaseIndex;
9import org.eclipse.viatra.query.runtime.api.scope.IEngineContext; 9import tools.refinery.viatra.runtime.api.scope.IEngineContext;
10import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext; 10import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
11import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 11import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
12 12
13public class RelationalEngineContext implements IEngineContext { 13public class RelationalEngineContext implements IEngineContext {
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
index 211eacb4..77d86a1c 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
@@ -5,10 +5,10 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.context; 6package tools.refinery.store.query.viatra.internal.context;
7 7
8import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext; 8import tools.refinery.viatra.runtime.matchers.context.AbstractQueryMetaContext;
9import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 9import tools.refinery.viatra.runtime.matchers.context.IInputKey;
10import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication; 10import tools.refinery.viatra.runtime.matchers.context.InputKeyImplication;
11import org.eclipse.viatra.query.runtime.matchers.context.common.JavaTransitiveInstancesKey; 11import tools.refinery.viatra.runtime.matchers.context.common.JavaTransitiveInstancesKey;
12import tools.refinery.store.query.viatra.internal.pquery.SymbolViewWrapper; 12import tools.refinery.store.query.viatra.internal.pquery.SymbolViewWrapper;
13import tools.refinery.store.query.view.AnySymbolView; 13import tools.refinery.store.query.view.AnySymbolView;
14 14
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
index df80a33e..d1fa5239 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
@@ -5,12 +5,12 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.context; 6package tools.refinery.store.query.viatra.internal.context;
7 7
8import org.eclipse.viatra.query.runtime.matchers.context.*; 8import tools.refinery.viatra.runtime.matchers.context.*;
9import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 9import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
10import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; 10import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
11import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; 11import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
12import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 12import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
13import org.eclipse.viatra.query.runtime.matchers.util.Accuracy; 13import tools.refinery.viatra.runtime.matchers.util.Accuracy;
14import tools.refinery.store.model.Model; 14import tools.refinery.store.model.Model;
15import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 15import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
16import tools.refinery.store.query.viatra.internal.pquery.SymbolViewWrapper; 16import tools.refinery.store.query.viatra.internal.pquery.SymbolViewWrapper;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java
index 37177cbf..fc75198c 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java
@@ -8,9 +8,9 @@
8 *******************************************************************************/ 8 *******************************************************************************/
9package tools.refinery.store.query.viatra.internal.localsearch; 9package tools.refinery.store.query.viatra.internal.localsearch;
10 10
11import org.eclipse.viatra.query.runtime.localsearch.MatchingFrame; 11import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
12import org.eclipse.viatra.query.runtime.localsearch.matcher.ISearchContext; 12import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
13import org.eclipse.viatra.query.runtime.localsearch.operations.ISearchOperation.ISearchOperationExecutor; 13import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation.ISearchOperationExecutor;
14 14
15import java.util.Iterator; 15import java.util.Iterator;
16 16
@@ -57,7 +57,7 @@ abstract class ExtendOperationExecutor<T> implements ISearchOperationExecutor {
57 } 57 }
58 58
59 /** 59 /**
60 * Fixed version of {@link org.eclipse.viatra.query.runtime.localsearch.operations.ExtendOperationExecutor#execute} 60 * Fixed version of {@link tools.refinery.viatra.runtime.localsearch.operations.ExtendOperationExecutor#execute}
61 * that handles failed unification of variables correctly. 61 * that handles failed unification of variables correctly.
62 * @param frame The matching frame to extend. 62 * @param frame The matching frame to extend.
63 * @param context The search context. 63 * @param context The search context.
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java
index 9d48c785..b8b6b159 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java
@@ -8,15 +8,15 @@
8 *******************************************************************************/ 8 *******************************************************************************/
9package tools.refinery.store.query.viatra.internal.localsearch; 9package tools.refinery.store.query.viatra.internal.localsearch;
10 10
11import org.eclipse.viatra.query.runtime.localsearch.MatchingFrame; 11import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
12import org.eclipse.viatra.query.runtime.localsearch.matcher.ISearchContext; 12import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
13import org.eclipse.viatra.query.runtime.localsearch.operations.IPatternMatcherOperation; 13import tools.refinery.viatra.runtime.localsearch.operations.IPatternMatcherOperation;
14import org.eclipse.viatra.query.runtime.localsearch.operations.ISearchOperation; 14import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
15import org.eclipse.viatra.query.runtime.localsearch.operations.util.CallInformation; 15import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
16import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider; 16import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
17import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; 17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; 18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import org.eclipse.viatra.query.runtime.matchers.tuple.VolatileModifiableMaskedTuple; 19import tools.refinery.viatra.runtime.matchers.tuple.VolatileModifiableMaskedTuple;
20 20
21import java.util.Iterator; 21import java.util.Iterator;
22import java.util.List; 22import java.util.List;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java
index cc906f22..ce2a75bd 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java
@@ -5,12 +5,12 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.localsearch; 6package tools.refinery.store.query.viatra.internal.localsearch;
7 7
8import org.eclipse.viatra.query.runtime.localsearch.planner.cost.IConstraintEvaluationContext; 8import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
9import org.eclipse.viatra.query.runtime.localsearch.planner.cost.impl.StatisticsBasedConstraintCostFunction; 9import tools.refinery.viatra.runtime.localsearch.planner.cost.impl.StatisticsBasedConstraintCostFunction;
10import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 10import tools.refinery.viatra.runtime.matchers.context.IInputKey;
11import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; 11import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
12import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; 12import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
13import org.eclipse.viatra.query.runtime.matchers.util.Accuracy; 13import tools.refinery.viatra.runtime.matchers.util.Accuracy;
14 14
15import java.util.Optional; 15import java.util.Optional;
16 16
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java
index 96ac4a72..0ee69b88 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java
@@ -8,15 +8,15 @@
8 *******************************************************************************/ 8 *******************************************************************************/
9package tools.refinery.store.query.viatra.internal.localsearch; 9package tools.refinery.store.query.viatra.internal.localsearch;
10 10
11import org.eclipse.viatra.query.runtime.localsearch.MatchingFrame; 11import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
12import org.eclipse.viatra.query.runtime.localsearch.matcher.ISearchContext; 12import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
13import org.eclipse.viatra.query.runtime.localsearch.operations.IIteratingSearchOperation; 13import tools.refinery.viatra.runtime.localsearch.operations.IIteratingSearchOperation;
14import org.eclipse.viatra.query.runtime.localsearch.operations.ISearchOperation; 14import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
15import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 15import tools.refinery.viatra.runtime.matchers.context.IInputKey;
16import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; 16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; 17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import org.eclipse.viatra.query.runtime.matchers.tuple.VolatileMaskedTuple; 18import tools.refinery.viatra.runtime.matchers.tuple.VolatileMaskedTuple;
19import org.eclipse.viatra.query.runtime.matchers.util.Preconditions; 19import tools.refinery.viatra.runtime.matchers.util.Preconditions;
20 20
21import java.util.*; 21import java.util.*;
22import java.util.function.Function; 22import java.util.function.Function;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java
index 0c77f587..7d5401c6 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java
@@ -5,17 +5,17 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.localsearch; 6package tools.refinery.store.query.viatra.internal.localsearch;
7 7
8import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.AbstractLocalSearchResultProvider; 8import tools.refinery.viatra.runtime.localsearch.matcher.integration.AbstractLocalSearchResultProvider;
9import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchBackend; 9import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchBackend;
10import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHints; 10import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHints;
11import org.eclipse.viatra.query.runtime.localsearch.plan.IPlanProvider; 11import tools.refinery.viatra.runtime.localsearch.plan.IPlanProvider;
12import org.eclipse.viatra.query.runtime.localsearch.plan.SimplePlanProvider; 12import tools.refinery.viatra.runtime.localsearch.plan.SimplePlanProvider;
13import org.eclipse.viatra.query.runtime.matchers.backend.IMatcherCapability; 13import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
14import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackend; 14import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
15import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; 15import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
16import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 16import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
17import org.eclipse.viatra.query.runtime.matchers.context.IQueryBackendContext; 17import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
18import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery; 18import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
19 19
20public class RelationalLocalSearchBackendFactory implements IQueryBackendFactory { 20public class RelationalLocalSearchBackendFactory implements IQueryBackendFactory {
21 public static final RelationalLocalSearchBackendFactory INSTANCE = new RelationalLocalSearchBackendFactory(); 21 public static final RelationalLocalSearchBackendFactory INSTANCE = new RelationalLocalSearchBackendFactory();
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java
index da37be14..37d06864 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java
@@ -5,14 +5,14 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.localsearch; 6package tools.refinery.store.query.viatra.internal.localsearch;
7 7
8import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.AbstractLocalSearchResultProvider; 8import tools.refinery.viatra.runtime.localsearch.matcher.integration.AbstractLocalSearchResultProvider;
9import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchBackend; 9import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchBackend;
10import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHints; 10import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHints;
11import org.eclipse.viatra.query.runtime.localsearch.plan.IPlanProvider; 11import tools.refinery.viatra.runtime.localsearch.plan.IPlanProvider;
12import org.eclipse.viatra.query.runtime.localsearch.planner.compiler.IOperationCompiler; 12import tools.refinery.viatra.runtime.localsearch.planner.compiler.IOperationCompiler;
13import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 13import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
14import org.eclipse.viatra.query.runtime.matchers.context.IQueryBackendContext; 14import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
15import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery; 15import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
16 16
17class RelationalLocalSearchResultProvider extends AbstractLocalSearchResultProvider { 17class RelationalLocalSearchResultProvider extends AbstractLocalSearchResultProvider {
18 public RelationalLocalSearchResultProvider(LocalSearchBackend backend, IQueryBackendContext context, PQuery query, 18 public RelationalLocalSearchResultProvider(LocalSearchBackend backend, IQueryBackendContext context, PQuery query,
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java
index f76ef486..37f264ae 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java
@@ -5,16 +5,16 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.localsearch; 6package tools.refinery.store.query.viatra.internal.localsearch;
7 7
8import org.eclipse.viatra.query.runtime.localsearch.operations.generic.GenericTypeExtendSingleValue; 8import tools.refinery.viatra.runtime.localsearch.operations.generic.GenericTypeExtendSingleValue;
9import org.eclipse.viatra.query.runtime.localsearch.operations.util.CallInformation; 9import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
10import org.eclipse.viatra.query.runtime.localsearch.planner.compiler.GenericOperationCompiler; 10import tools.refinery.viatra.runtime.localsearch.planner.compiler.GenericOperationCompiler;
11import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 11import tools.refinery.viatra.runtime.matchers.context.IInputKey;
12import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext; 12import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
13import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; 13import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
14import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; 14import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
15import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; 15import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
16import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; 16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; 17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18 18
19import java.util.*; 19import java.util.*;
20 20
@@ -52,7 +52,7 @@ public class RelationalOperationCompiler extends GenericOperationCompiler {
52 unboundVariables.iterator().next())); 52 unboundVariables.iterator().next()));
53 } else { 53 } else {
54 // Use a fixed version of 54 // Use a fixed version of
55 // {@code org.eclipse.viatra.query.runtime.localsearch.operations.generic.GenericTypeExtend} that handles 55 // {@code tools.refinery.viatra.runtime.localsearch.operations.generic.GenericTypeExtend} that handles
56 // failed unification of variables correctly. 56 // failed unification of variables correctly.
57 operations.add(new GenericTypeExtend(inputKey, positions, callMask, indexerMask, unboundVariables)); 57 operations.add(new GenericTypeExtend(inputKey, positions, callMask, indexerMask, unboundVariables));
58 } 58 }
@@ -62,7 +62,7 @@ public class RelationalOperationCompiler extends GenericOperationCompiler {
62 protected void createExtend(PositivePatternCall pCall, Map<PVariable, Integer> variableMapping) { 62 protected void createExtend(PositivePatternCall pCall, Map<PVariable, Integer> variableMapping) {
63 CallInformation information = CallInformation.create(pCall, variableMapping, variableBindings.get(pCall)); 63 CallInformation information = CallInformation.create(pCall, variableMapping, variableBindings.get(pCall));
64 // Use a fixed version of 64 // Use a fixed version of
65 // {@code org.eclipse.viatra.query.runtime.localsearch.operations.extend.ExtendPositivePatternCall} that handles 65 // {@code tools.refinery.viatra.runtime.localsearch.operations.extend.ExtendPositivePatternCall} that handles
66 // failed unification of variables correctly. 66 // failed unification of variables correctly.
67 operations.add(new ExtendPositivePatternCall(information)); 67 operations.add(new ExtendPositivePatternCall(information));
68 dependencies.add(information.getCallWithAdornment()); 68 dependencies.add(information.getCallWithAdornment());
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/AbstractViatraMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/AbstractViatraMatcher.java
index 99b0a3d8..c4a5a236 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/AbstractViatraMatcher.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/AbstractViatraMatcher.java
@@ -5,8 +5,8 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.matcher; 6package tools.refinery.store.query.viatra.internal.matcher;
7 7
8import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider; 8import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
9import org.eclipse.viatra.query.runtime.matchers.backend.IUpdateable; 9import tools.refinery.viatra.runtime.matchers.backend.IUpdateable;
10import tools.refinery.store.query.dnf.Query; 10import tools.refinery.store.query.dnf.Query;
11import tools.refinery.store.query.resultset.AbstractResultSet; 11import tools.refinery.store.query.resultset.AbstractResultSet;
12import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 12import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java
index 47efb2aa..44038669 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.matcher; 6package tools.refinery.store.query.viatra.internal.matcher;
7 7
8import org.eclipse.viatra.query.runtime.rete.index.IterableIndexer; 8import tools.refinery.viatra.runtime.rete.index.IterableIndexer;
9import tools.refinery.store.map.Cursor; 9import tools.refinery.store.map.Cursor;
10import tools.refinery.store.tuple.Tuple; 10import tools.refinery.store.tuple.Tuple;
11 11
@@ -13,7 +13,7 @@ import java.util.Iterator;
13 13
14class FunctionalCursor<T> implements Cursor<Tuple, T> { 14class FunctionalCursor<T> implements Cursor<Tuple, T> {
15 private final IterableIndexer indexer; 15 private final IterableIndexer indexer;
16 private final Iterator<org.eclipse.viatra.query.runtime.matchers.tuple.Tuple> iterator; 16 private final Iterator<tools.refinery.viatra.runtime.matchers.tuple.Tuple> iterator;
17 private boolean terminated; 17 private boolean terminated;
18 private Tuple key; 18 private Tuple key;
19 private T value; 19 private T value;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java
index db4740cd..fb9e4df6 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java
@@ -5,10 +5,10 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.matcher; 6package tools.refinery.store.query.viatra.internal.matcher;
7 7
8import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; 8import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
9import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 9import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
10import org.eclipse.viatra.query.runtime.rete.index.IterableIndexer; 10import tools.refinery.viatra.runtime.rete.index.IterableIndexer;
11import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher; 11import tools.refinery.viatra.runtime.rete.matcher.RetePatternMatcher;
12import tools.refinery.store.map.Cursor; 12import tools.refinery.store.map.Cursor;
13import tools.refinery.store.query.dnf.FunctionalQuery; 13import tools.refinery.store.query.dnf.FunctionalQuery;
14import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 14import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
@@ -17,9 +17,9 @@ import tools.refinery.store.tuple.Tuple;
17/** 17/**
18 * Directly access the tuples inside a VIATRA pattern matcher.<p> 18 * Directly access the tuples inside a VIATRA pattern matcher.<p>
19 * This class neglects calling 19 * This class neglects calling
20 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#wrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)} 20 * {@link tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext#wrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)}
21 * and 21 * and
22 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#unwrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)}, 22 * {@link tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext#unwrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)},
23 * because {@link tools.refinery.store.query.viatra.internal.context.RelationalRuntimeContext} provides a trivial 23 * because {@link tools.refinery.store.query.viatra.internal.context.RelationalRuntimeContext} provides a trivial
24 * implementation for these methods. 24 * implementation for these methods.
25 * Using this class with any other runtime context may lead to undefined behavior. 25 * Using this class with any other runtime context may lead to undefined behavior.
@@ -37,7 +37,7 @@ public class FunctionalViatraMatcher<T> extends AbstractViatraMatcher<T> {
37 emptyMask = TupleMask.empty(arityWithOutput); 37 emptyMask = TupleMask.empty(arityWithOutput);
38 omitOutputMask = TupleMask.omit(arity, arityWithOutput); 38 omitOutputMask = TupleMask.omit(arity, arityWithOutput);
39 if (backend instanceof RetePatternMatcher reteBackend) { 39 if (backend instanceof RetePatternMatcher reteBackend) {
40 var maybeIterableOmitOutputIndexer = IndexerUtils.getIndexer(reteBackend, omitOutputMask); 40 var maybeIterableOmitOutputIndexer = reteBackend.getInternalIndexer(omitOutputMask);
41 if (maybeIterableOmitOutputIndexer instanceof IterableIndexer iterableOmitOutputIndexer) { 41 if (maybeIterableOmitOutputIndexer instanceof IterableIndexer iterableOmitOutputIndexer) {
42 omitOutputIndexer = iterableOmitOutputIndexer; 42 omitOutputIndexer = iterableOmitOutputIndexer;
43 } else { 43 } else {
@@ -76,7 +76,7 @@ public class FunctionalViatraMatcher<T> extends AbstractViatraMatcher<T> {
76 } 76 }
77 77
78 @Override 78 @Override
79 public void update(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple updateElement, boolean isInsertion) { 79 public void update(tools.refinery.viatra.runtime.matchers.tuple.Tuple updateElement, boolean isInsertion) {
80 var key = MatcherUtils.keyToRefineryTuple(updateElement); 80 var key = MatcherUtils.keyToRefineryTuple(updateElement);
81 var value = MatcherUtils.<T>getValue(updateElement); 81 var value = MatcherUtils.<T>getValue(updateElement);
82 if (isInsertion) { 82 if (isInsertion) {
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java
deleted file mode 100644
index 15f00b2d..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java
+++ /dev/null
@@ -1,53 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.matcher;
7
8import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
9import org.eclipse.viatra.query.runtime.rete.index.Indexer;
10import org.eclipse.viatra.query.runtime.rete.matcher.ReteEngine;
11import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher;
12import org.eclipse.viatra.query.runtime.rete.traceability.RecipeTraceInfo;
13
14import java.lang.invoke.MethodHandle;
15import java.lang.invoke.MethodHandles;
16import java.lang.invoke.MethodType;
17
18final class IndexerUtils {
19 private static final MethodHandle GET_ENGINE_HANDLE;
20 private static final MethodHandle GET_PRODUCTION_NODE_TRACE_HANDLE;
21 private static final MethodHandle ACCESS_PROJECTION_HANDLE;
22
23 static {
24 try {
25 var lookup = MethodHandles.privateLookupIn(RetePatternMatcher.class, MethodHandles.lookup());
26 GET_ENGINE_HANDLE = lookup.findGetter(RetePatternMatcher.class, "engine", ReteEngine.class);
27 GET_PRODUCTION_NODE_TRACE_HANDLE = lookup.findGetter(RetePatternMatcher.class, "productionNodeTrace",
28 RecipeTraceInfo.class);
29 ACCESS_PROJECTION_HANDLE = lookup.findVirtual(ReteEngine.class, "accessProjection",
30 MethodType.methodType(Indexer.class, RecipeTraceInfo.class, TupleMask.class));
31 } catch (IllegalAccessException | NoSuchFieldException | NoSuchMethodException e) {
32 throw new IllegalStateException("Cannot access private members of %s"
33 .formatted(RetePatternMatcher.class.getPackageName()), e);
34 }
35 }
36
37 private IndexerUtils() {
38 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
39 }
40
41 public static Indexer getIndexer(RetePatternMatcher backend, TupleMask mask) {
42 try {
43 var engine = (ReteEngine) GET_ENGINE_HANDLE.invokeExact(backend);
44 var trace = (RecipeTraceInfo) GET_PRODUCTION_NODE_TRACE_HANDLE.invokeExact(backend);
45 return (Indexer) ACCESS_PROJECTION_HANDLE.invokeExact(engine, trace, mask);
46 } catch (Error e) {
47 // Fatal JVM errors should not be wrapped.
48 throw e;
49 } catch (Throwable e) {
50 throw new IllegalStateException("Cannot access matcher for mask " + mask, e);
51 }
52 }
53}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java
index 6e24812a..6cda23cd 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java
@@ -5,8 +5,8 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.matcher; 6package tools.refinery.store.query.viatra.internal.matcher;
7 7
8import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 8import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
9import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 9import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
10import org.jetbrains.annotations.Nullable; 10import org.jetbrains.annotations.Nullable;
11import tools.refinery.store.tuple.*; 11import tools.refinery.store.tuple.*;
12 12
@@ -17,7 +17,7 @@ final class MatcherUtils {
17 throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); 17 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
18 } 18 }
19 19
20 public static org.eclipse.viatra.query.runtime.matchers.tuple.Tuple toViatraTuple(Tuple refineryTuple) { 20 public static tools.refinery.viatra.runtime.matchers.tuple.Tuple toViatraTuple(Tuple refineryTuple) {
21 if (refineryTuple instanceof Tuple0) { 21 if (refineryTuple instanceof Tuple0) {
22 return Tuples.staticArityFlatTupleOf(); 22 return Tuples.staticArityFlatTupleOf();
23 } else if (refineryTuple instanceof Tuple1) { 23 } else if (refineryTuple instanceof Tuple1) {
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java
index 5b82c4b7..c0be70ba 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java
@@ -5,9 +5,9 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.matcher; 6package tools.refinery.store.query.viatra.internal.matcher;
7 7
8import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher; 8import tools.refinery.viatra.runtime.api.GenericPatternMatcher;
9import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; 9import tools.refinery.viatra.runtime.api.GenericQuerySpecification;
10import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider; 10import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
11 11
12public class RawPatternMatcher extends GenericPatternMatcher { 12public class RawPatternMatcher extends GenericPatternMatcher {
13 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) { 13 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) {
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java
index 1dc8f5db..53475218 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.matcher; 6package tools.refinery.store.query.viatra.internal.matcher;
7 7
8import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 8import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
9import tools.refinery.store.map.Cursor; 9import tools.refinery.store.map.Cursor;
10import tools.refinery.store.tuple.Tuple; 10import tools.refinery.store.tuple.Tuple;
11 11
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java
index ac95dcc0..da75e864 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java
@@ -5,10 +5,10 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.matcher; 6package tools.refinery.store.query.viatra.internal.matcher;
7 7
8import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; 8import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
9import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 9import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
10import org.eclipse.viatra.query.runtime.rete.index.Indexer; 10import tools.refinery.viatra.runtime.rete.index.Indexer;
11import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher; 11import tools.refinery.viatra.runtime.rete.matcher.RetePatternMatcher;
12import tools.refinery.store.map.Cursor; 12import tools.refinery.store.map.Cursor;
13import tools.refinery.store.map.Cursors; 13import tools.refinery.store.map.Cursors;
14import tools.refinery.store.query.dnf.RelationalQuery; 14import tools.refinery.store.query.dnf.RelationalQuery;
@@ -18,9 +18,9 @@ import tools.refinery.store.tuple.Tuple;
18/** 18/**
19 * Directly access the tuples inside a VIATRA pattern matcher.<p> 19 * Directly access the tuples inside a VIATRA pattern matcher.<p>
20 * This class neglects calling 20 * This class neglects calling
21 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#wrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)} 21 * {@link tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext#wrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)}
22 * and 22 * and
23 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#unwrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)}, 23 * {@link tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext#unwrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)},
24 * because {@link tools.refinery.store.query.viatra.internal.context.RelationalRuntimeContext} provides a trivial 24 * because {@link tools.refinery.store.query.viatra.internal.context.RelationalRuntimeContext} provides a trivial
25 * implementation for these methods. 25 * implementation for these methods.
26 * Using this class with any other runtime context may lead to undefined behavior. 26 * Using this class with any other runtime context may lead to undefined behavior.
@@ -37,7 +37,7 @@ public class RelationalViatraMatcher extends AbstractViatraMatcher<Boolean> {
37 emptyMask = TupleMask.empty(arity); 37 emptyMask = TupleMask.empty(arity);
38 identityMask = TupleMask.identity(arity); 38 identityMask = TupleMask.identity(arity);
39 if (backend instanceof RetePatternMatcher reteBackend) { 39 if (backend instanceof RetePatternMatcher reteBackend) {
40 emptyMaskIndexer = IndexerUtils.getIndexer(reteBackend, emptyMask); 40 emptyMaskIndexer = reteBackend.getInternalIndexer(emptyMask);
41 } else { 41 } else {
42 emptyMaskIndexer = null; 42 emptyMaskIndexer = null;
43 } 43 }
@@ -73,7 +73,7 @@ public class RelationalViatraMatcher extends AbstractViatraMatcher<Boolean> {
73 } 73 }
74 74
75 @Override 75 @Override
76 public void update(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple updateElement, boolean isInsertion) { 76 public void update(tools.refinery.viatra.runtime.matchers.tuple.Tuple updateElement, boolean isInsertion) {
77 var key = MatcherUtils.toRefineryTuple(updateElement); 77 var key = MatcherUtils.toRefineryTuple(updateElement);
78 notifyChange(key, !isInsertion, isInsertion); 78 notifyChange(key, !isInsertion, isInsertion);
79 } 79 }
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java
index b0b507fe..093ade96 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.matcher; 6package tools.refinery.store.query.viatra.internal.matcher;
7 7
8import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 8import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
9import tools.refinery.store.map.Cursor; 9import tools.refinery.store.map.Cursor;
10import tools.refinery.store.tuple.Tuple; 10import tools.refinery.store.tuple.Tuple;
11 11
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/CheckEvaluator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/CheckEvaluator.java
index 5dde41be..ada154d4 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/CheckEvaluator.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/CheckEvaluator.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.pquery; 6package tools.refinery.store.query.viatra.internal.pquery;
7 7
8import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider; 8import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider;
9import tools.refinery.store.query.term.Term; 9import tools.refinery.store.query.term.Term;
10 10
11class CheckEvaluator extends TermEvaluator<Boolean> { 11class CheckEvaluator extends TermEvaluator<Boolean> {
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java
index af3bf32e..492bd054 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java
@@ -5,24 +5,6 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.pquery; 6package tools.refinery.store.query.viatra.internal.pquery;
7 7
8import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
9import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
10import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
11import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
12import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
13import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.BoundAggregator;
14import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
15import org.eclipse.viatra.query.runtime.matchers.psystem.annotations.PAnnotation;
16import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.*;
17import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
18import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.ConstantValue;
19import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
20import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
21import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
22import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameterDirection;
23import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
24import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
25import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
26import tools.refinery.store.query.Constraint; 8import tools.refinery.store.query.Constraint;
27import tools.refinery.store.query.dnf.Dnf; 9import tools.refinery.store.query.dnf.Dnf;
28import tools.refinery.store.query.dnf.DnfClause; 10import tools.refinery.store.query.dnf.DnfClause;
@@ -34,6 +16,22 @@ import tools.refinery.store.query.term.StatelessAggregator;
34import tools.refinery.store.query.term.Variable; 16import tools.refinery.store.query.term.Variable;
35import tools.refinery.store.query.view.AnySymbolView; 17import tools.refinery.store.query.view.AnySymbolView;
36import tools.refinery.store.util.CycleDetectingMapper; 18import tools.refinery.store.util.CycleDetectingMapper;
19import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
20import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
21import tools.refinery.viatra.runtime.matchers.context.IInputKey;
22import tools.refinery.viatra.runtime.matchers.psystem.PBody;
23import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
24import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator;
25import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
26import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation;
27import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.*;
28import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.*;
29import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.Connectivity;
30import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
31import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameterDirection;
32import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
33import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
34import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
37 35
38import java.util.ArrayList; 36import java.util.ArrayList;
39import java.util.HashMap; 37import java.util.HashMap;
@@ -42,7 +40,6 @@ import java.util.Map;
42import java.util.function.Function; 40import java.util.function.Function;
43 41
44public class Dnf2PQuery { 42public class Dnf2PQuery {
45 private static final Object P_CONSTRAINT_LOCK = new Object();
46 private final CycleDetectingMapper<Dnf, RawPQuery> mapper = new CycleDetectingMapper<>(Dnf::name, 43 private final CycleDetectingMapper<Dnf, RawPQuery> mapper = new CycleDetectingMapper<>(Dnf::name,
47 this::doTranslate); 44 this::doTranslate);
48 private final QueryWrapperFactory wrapperFactory = new QueryWrapperFactory(this); 45 private final QueryWrapperFactory wrapperFactory = new QueryWrapperFactory(this);
@@ -91,22 +88,17 @@ public class Dnf2PQuery {
91 pQuery.addAnnotation(functionalDependencyAnnotation); 88 pQuery.addAnnotation(functionalDependencyAnnotation);
92 } 89 }
93 90
94 // The constructor of {@link org.eclipse.viatra.query.runtime.matchers.psystem.BasePConstraint} mutates 91 for (DnfClause clause : dnfQuery.getClauses()) {
95 // global static state (<code>nextID</code>) without locking. Therefore, we need to synchronize before creating 92 PBody body = new PBody(pQuery);
96 // any query literals to avoid a data race. 93 List<ExportedParameter> parameterExports = new ArrayList<>();
97 synchronized (P_CONSTRAINT_LOCK) { 94 for (var parameter : dnfQuery.getSymbolicParameters()) {
98 for (DnfClause clause : dnfQuery.getClauses()) { 95 PVariable pVar = body.getOrCreateVariableByName(parameter.getVariable().getUniqueName());
99 PBody body = new PBody(pQuery); 96 parameterExports.add(new ExportedParameter(body, pVar, parameters.get(parameter)));
100 List<ExportedParameter> parameterExports = new ArrayList<>(); 97 }
101 for (var parameter : dnfQuery.getSymbolicParameters()) { 98 body.setSymbolicParameters(parameterExports);
102 PVariable pVar = body.getOrCreateVariableByName(parameter.getVariable().getUniqueName()); 99 pQuery.addBody(body);
103 parameterExports.add(new ExportedParameter(body, pVar, parameters.get(parameter))); 100 for (Literal literal : clause.literals()) {
104 } 101 translateLiteral(literal, body);
105 body.setSymbolicParameters(parameterExports);
106 pQuery.addBody(body);
107 for (Literal literal : clause.literals()) {
108 translateLiteral(literal, body);
109 }
110 } 102 }
111 } 103 }
112 104
@@ -252,6 +244,10 @@ public class Dnf2PQuery {
252 private void translateRepresentativeElectionLiteral(RepresentativeElectionLiteral literal, PBody body) { 244 private void translateRepresentativeElectionLiteral(RepresentativeElectionLiteral literal, PBody body) {
253 var substitution = translateSubstitution(literal.getArguments(), body); 245 var substitution = translateSubstitution(literal.getArguments(), body);
254 var pattern = wrapConstraintWithIdentityArguments(literal.getTarget()); 246 var pattern = wrapConstraintWithIdentityArguments(literal.getTarget());
255 new RepresentativeElectionConstraint(body, substitution, pattern, literal.getConnectivity()); 247 var connectivity = switch (literal.getConnectivity()) {
248 case WEAK -> Connectivity.WEAK;
249 case STRONG -> Connectivity.STRONG;
250 };
251 new RepresentativeElectionConstraint(body, substitution, pattern, connectivity);
256 } 252 }
257} 253}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java
index 502813e1..d21131e5 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java
@@ -5,17 +5,17 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.pquery; 6package tools.refinery.store.query.viatra.internal.pquery;
7 7
8import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 8import tools.refinery.viatra.runtime.matchers.context.IInputKey;
9import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; 9import tools.refinery.viatra.runtime.matchers.psystem.PBody;
10import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; 10import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
11import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter; 11import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
12import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; 12import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
13import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; 13import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
14import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; 14import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
15import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery; 15import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
16import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; 16import tools.refinery.viatra.runtime.matchers.psystem.queries.PVisibility;
17import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; 17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 18import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
19import tools.refinery.store.query.Constraint; 19import tools.refinery.store.query.Constraint;
20import tools.refinery.store.query.dnf.Dnf; 20import tools.refinery.store.query.dnf.Dnf;
21import tools.refinery.store.query.dnf.DnfUtils; 21import tools.refinery.store.query.dnf.DnfUtils;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
index 255738c5..06644bf2 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
@@ -5,14 +5,14 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.pquery; 6package tools.refinery.store.query.viatra.internal.pquery;
7 7
8import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; 8import tools.refinery.viatra.runtime.api.GenericQuerySpecification;
9import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; 9import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
10import org.eclipse.viatra.query.runtime.api.scope.QueryScope; 10import tools.refinery.viatra.runtime.api.scope.QueryScope;
11import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; 11import tools.refinery.viatra.runtime.matchers.psystem.PBody;
12import org.eclipse.viatra.query.runtime.matchers.psystem.annotations.PAnnotation; 12import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation;
13import org.eclipse.viatra.query.runtime.matchers.psystem.queries.BasePQuery; 13import tools.refinery.viatra.runtime.matchers.psystem.queries.BasePQuery;
14import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; 14import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
15import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; 15import tools.refinery.viatra.runtime.matchers.psystem.queries.PVisibility;
16import tools.refinery.store.query.viatra.internal.RelationalScope; 16import tools.refinery.store.query.viatra.internal.RelationalScope;
17import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; 17import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
18 18
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java
index 461416f7..ba99cf9a 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.pquery; 6package tools.refinery.store.query.viatra.internal.pquery;
7 7
8import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; 8import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
9import tools.refinery.store.query.term.StatefulAggregate; 9import tools.refinery.store.query.term.StatefulAggregate;
10import tools.refinery.store.query.term.StatefulAggregator; 10import tools.refinery.store.query.term.StatefulAggregator;
11 11
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java
index 49175d75..bf2c2f4f 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.pquery; 6package tools.refinery.store.query.viatra.internal.pquery;
7 7
8import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; 8import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
9import tools.refinery.store.query.term.StatelessAggregator; 9import tools.refinery.store.query.term.StatelessAggregator;
10 10
11import java.util.stream.Stream; 11import java.util.stream.Stream;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SymbolViewWrapper.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SymbolViewWrapper.java
index a777613e..a774404e 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SymbolViewWrapper.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SymbolViewWrapper.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.pquery; 6package tools.refinery.store.query.viatra.internal.pquery;
7 7
8import org.eclipse.viatra.query.runtime.matchers.context.common.BaseInputKeyWrapper; 8import tools.refinery.viatra.runtime.matchers.context.common.BaseInputKeyWrapper;
9import tools.refinery.store.query.view.AnySymbolView; 9import tools.refinery.store.query.view.AnySymbolView;
10 10
11public class SymbolViewWrapper extends BaseInputKeyWrapper<AnySymbolView> { 11public class SymbolViewWrapper extends BaseInputKeyWrapper<AnySymbolView> {
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java
index 1187f57a..5df861a6 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java
@@ -5,8 +5,8 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.pquery; 6package tools.refinery.store.query.viatra.internal.pquery;
7 7
8import org.eclipse.viatra.query.runtime.matchers.psystem.IExpressionEvaluator; 8import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator;
9import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider; 9import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider;
10import tools.refinery.store.query.term.Term; 10import tools.refinery.store.query.term.Term;
11import tools.refinery.store.query.term.Variable; 11import tools.refinery.store.query.term.Variable;
12 12
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java
index 62cb8b3a..b9ae8ab2 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.pquery; 6package tools.refinery.store.query.viatra.internal.pquery;
7 7
8import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider; 8import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider;
9import tools.refinery.store.query.term.DataVariable; 9import tools.refinery.store.query.term.DataVariable;
10import tools.refinery.store.query.valuation.Valuation; 10import tools.refinery.store.query.valuation.Valuation;
11 11
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefineryPBodyCopier.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefineryPBodyCopier.java
deleted file mode 100644
index a833a37b..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefineryPBodyCopier.java
+++ /dev/null
@@ -1,35 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.pquery.rewriter;
7
8import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
9import org.eclipse.viatra.query.runtime.matchers.psystem.PConstraint;
10import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.IRewriterTraceCollector;
11import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PBodyCopier;
12import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
13import tools.refinery.store.query.viatra.internal.pquery.RepresentativeElectionConstraint;
14
15public class RefineryPBodyCopier extends PBodyCopier {
16 public RefineryPBodyCopier(PBody body, IRewriterTraceCollector traceCollector) {
17 super(body, traceCollector);
18 }
19
20 @Override
21 protected void copyConstraint(PConstraint constraint) {
22 if (constraint instanceof RepresentativeElectionConstraint representativeElectionConstraint) {
23 copyRepresentativeElectionConstraint(representativeElectionConstraint);
24 } else {
25 super.copyConstraint(constraint);
26 }
27 }
28
29 private void copyRepresentativeElectionConstraint(RepresentativeElectionConstraint constraint) {
30 var mappedVariables = extractMappedVariables(constraint);
31 var variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables);
32 addTrace(constraint, new RepresentativeElectionConstraint(body, variablesTuple, constraint.getReferredQuery(),
33 constraint.getConnectivity()));
34 }
35}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefineryPBodyNormalizer.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefineryPBodyNormalizer.java
deleted file mode 100644
index ed85a843..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefineryPBodyNormalizer.java
+++ /dev/null
@@ -1,38 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.store.query.viatra.internal.pquery.rewriter;
10
11import org.eclipse.viatra.query.runtime.matchers.context.IQueryMetaContext;
12import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
13import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PDisjunction;
14import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
15import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PBodyCopier;
16import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PBodyNormalizer;
17
18import java.util.LinkedHashSet;
19import java.util.Set;
20
21public class RefineryPBodyNormalizer extends PBodyNormalizer {
22 public RefineryPBodyNormalizer(IQueryMetaContext context) {
23 super(context);
24 }
25
26 @Override
27 public PDisjunction rewrite(PDisjunction disjunction) {
28 Set<PBody> normalizedBodies = new LinkedHashSet<>();
29 for (PBody body : disjunction.getBodies()) {
30 PBodyCopier copier = new RefineryPBodyCopier(body, getTraceCollector());
31 PBody modifiedBody = copier.getCopiedBody();
32 normalizeBody(modifiedBody);
33 normalizedBodies.add(modifiedBody);
34 modifiedBody.setStatus(PQuery.PQueryStatus.OK);
35 }
36 return new PDisjunction(normalizedBodies);
37 }
38}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefinerySurrogateQueryRewriter.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefinerySurrogateQueryRewriter.java
deleted file mode 100644
index dc288ba0..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefinerySurrogateQueryRewriter.java
+++ /dev/null
@@ -1,58 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Zoltan Ujhelyi, Istvan Rath and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.store.query.viatra.internal.pquery.rewriter;
10
11import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
12import org.eclipse.viatra.query.runtime.matchers.context.surrogate.SurrogateQueryRegistry;
13import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
14import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
15import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
16import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
17import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PDisjunction;
18import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
19import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PBodyCopier;
20import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PDisjunctionRewriter;
21import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
22import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
23
24import java.util.LinkedHashSet;
25import java.util.Set;
26
27public class RefinerySurrogateQueryRewriter extends PDisjunctionRewriter {
28 @Override
29 public PDisjunction rewrite(PDisjunction disjunction) {
30 Set<PBody> replacedBodies = new LinkedHashSet<>();
31 for (PBody body : disjunction.getBodies()) {
32 PBodyCopier copier = new RefineryPBodyCopier(body, getTraceCollector()) {
33
34 @Override
35 protected void copyTypeConstraint(TypeConstraint typeConstraint) {
36 PVariable[] mappedVariables = extractMappedVariables(typeConstraint);
37 Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables);
38 final IInputKey supplierKey = typeConstraint.getSupplierKey();
39 if (SurrogateQueryRegistry.instance().hasSurrogateQueryFQN(supplierKey)) {
40 PQuery surrogateQuery = SurrogateQueryRegistry.instance().getSurrogateQuery(supplierKey);
41 if (surrogateQuery == null) {
42 throw new IllegalStateException("Surrogate query for feature %s not found"
43 .formatted(supplierKey.getPrettyPrintableName()));
44 }
45 addTrace(typeConstraint, new PositivePatternCall(getCopiedBody(), variablesTuple,
46 surrogateQuery));
47 } else {
48 addTrace(typeConstraint, new TypeConstraint(getCopiedBody(), variablesTuple, supplierKey));
49 }
50 }
51 };
52 PBody modifiedBody = copier.getCopiedBody();
53 replacedBodies.add(modifiedBody);
54 modifiedBody.setStatus(PQuery.PQueryStatus.OK);
55 }
56 return new PDisjunction(replacedBodies);
57 }
58}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteBackendFactory.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteBackendFactory.java
deleted file mode 100644
index 517e511a..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteBackendFactory.java
+++ /dev/null
@@ -1,90 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.store.query.viatra.internal.rete;
10
11import org.eclipse.viatra.query.runtime.matchers.backend.*;
12import org.eclipse.viatra.query.runtime.matchers.context.IQueryBackendContext;
13import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
14import org.eclipse.viatra.query.runtime.rete.construction.plancompiler.ReteRecipeCompiler;
15import org.eclipse.viatra.query.runtime.rete.matcher.IncrementalMatcherCapability;
16import org.eclipse.viatra.query.runtime.rete.matcher.ReteEngine;
17import org.eclipse.viatra.query.runtime.rete.matcher.TimelyConfiguration;
18import org.eclipse.viatra.query.runtime.rete.util.Options;
19
20// Singleton implementations follows the VIATRA implementation.
21@SuppressWarnings("squid:S6548")
22public class RefineryReteBackendFactory implements IQueryBackendFactory {
23 /**
24 * EXPERIMENTAL
25 */
26 protected static final int RETE_THREADS = 0;
27
28 /**
29 * @since 2.0
30 */
31 public static final RefineryReteBackendFactory INSTANCE = new RefineryReteBackendFactory();
32
33 /**
34 * @since 1.5
35 */
36 @Override
37 public IQueryBackend create(IQueryBackendContext context) {
38 return create(context, false, null);
39 }
40
41 /**
42 * @since 2.4
43 */
44 public IQueryBackend create(IQueryBackendContext context, boolean deleteAndReDeriveEvaluation,
45 TimelyConfiguration timelyConfiguration) {
46 ReteEngine engine;
47 engine = new RefineryReteEngine(context, RETE_THREADS, deleteAndReDeriveEvaluation, timelyConfiguration);
48 IQueryBackendHintProvider hintConfiguration = engine.getHintConfiguration();
49 ReteRecipeCompiler compiler = new RefineryReteRecipeCompiler(
50 Options.builderMethod.layoutStrategy(context, hintConfiguration), context.getLogger(),
51 context.getRuntimeContext().getMetaContext(), context.getQueryCacheContext(), hintConfiguration,
52 context.getQueryAnalyzer(), deleteAndReDeriveEvaluation, timelyConfiguration);
53 engine.setCompiler(compiler);
54 return engine;
55 }
56
57 @Override
58 public Class<? extends IQueryBackend> getBackendClass() {
59 return ReteEngine.class;
60 }
61
62 @Override
63 public int hashCode() {
64 return RefineryReteBackendFactory.class.hashCode();
65 }
66
67 @Override
68 public boolean equals(Object obj) {
69 if (this == obj) {
70 return true;
71 }
72 if (obj == null) {
73 return false;
74 }
75 return obj instanceof RefineryReteBackendFactory;
76 }
77
78 /**
79 * @since 1.4
80 */
81 @Override
82 public IMatcherCapability calculateRequiredCapability(PQuery query, QueryEvaluationHint hint) {
83 return new IncrementalMatcherCapability();
84 }
85
86 @Override
87 public boolean isCaching() {
88 return true;
89 }
90}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteEngine.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteEngine.java
deleted file mode 100644
index c088219b..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteEngine.java
+++ /dev/null
@@ -1,134 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.rete;
7
8import org.apache.log4j.Logger;
9import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
10import org.eclipse.viatra.query.runtime.matchers.context.IQueryBackendContext;
11import org.eclipse.viatra.query.runtime.rete.matcher.ReteEngine;
12import org.eclipse.viatra.query.runtime.rete.matcher.TimelyConfiguration;
13import org.eclipse.viatra.query.runtime.rete.network.Network;
14import org.eclipse.viatra.query.runtime.rete.network.NodeProvisioner;
15import org.eclipse.viatra.query.runtime.rete.network.ReteContainer;
16
17import java.io.IOException;
18import java.lang.invoke.MethodHandle;
19import java.lang.invoke.MethodHandles;
20import java.lang.invoke.MethodType;
21
22public class RefineryReteEngine extends ReteEngine {
23 private static final MethodHandle REFINERY_NODE_FACTORY_CONSTRUCTOR;
24 private static final MethodHandle REFINERY_CONNECTION_FACTORY_CONSTRUCTOR;
25 private static final MethodHandle NETWORK_NODE_FACTORY_SETTER;
26 private static final MethodHandle RETE_CONTAINER_CONNECTION_FACTORY_SETTER;
27 private static final MethodHandle NODE_PROVISIONER_NODE_FACTORY_SETTER;
28 private static final MethodHandle NODE_PROVISIONER_CONNECTION_FACTORY_SETTER;
29
30 static {
31 MethodHandles.Lookup lookup;
32 try {
33 lookup = MethodHandles.privateLookupIn(Network.class, MethodHandles.lookup());
34 } catch (IllegalAccessException e) {
35 throw new IllegalStateException("Cannot create private lookup", e);
36 }
37 var refineryNodeFactoryClass = defineClassFromFile(lookup, "RefineryNodeFactory");
38 var refinaryConnectionFactoryClass = defineClassFromFile(lookup, "RefineryConnectionFactory");
39 try {
40 REFINERY_NODE_FACTORY_CONSTRUCTOR = lookup.findConstructor(refineryNodeFactoryClass,
41 MethodType.methodType(Void.TYPE, Logger.class));
42 REFINERY_CONNECTION_FACTORY_CONSTRUCTOR = lookup.findConstructor(refinaryConnectionFactoryClass,
43 MethodType.methodType(Void.TYPE, ReteContainer.class));
44 } catch (NoSuchMethodException | IllegalAccessException e) {
45 throw new IllegalStateException("Cannot get constructor", e);
46 }
47 var nodeFactoryClass = refineryNodeFactoryClass.getSuperclass();
48 var connectionFactoryClass = refinaryConnectionFactoryClass.getSuperclass();
49 try {
50 NETWORK_NODE_FACTORY_SETTER = lookup.findSetter(Network.class, "nodeFactory", nodeFactoryClass);
51 RETE_CONTAINER_CONNECTION_FACTORY_SETTER = lookup.findSetter(ReteContainer.class, "connectionFactory",
52 connectionFactoryClass);
53 NODE_PROVISIONER_NODE_FACTORY_SETTER = lookup.findSetter(NodeProvisioner.class, "nodeFactory",
54 nodeFactoryClass);
55 NODE_PROVISIONER_CONNECTION_FACTORY_SETTER = lookup.findSetter(NodeProvisioner.class, "connectionFactory",
56 connectionFactoryClass);
57 } catch (NoSuchFieldException | IllegalAccessException e) {
58 throw new IllegalStateException("Cannot get field setter", e);
59 }
60 }
61
62 private static Class<?> defineClassFromFile(MethodHandles.Lookup lookup, String name) {
63 byte[] classBytes;
64 try (var resource = Network.class.getResourceAsStream(name + ".class")) {
65 if (resource == null) {
66 throw new IllegalStateException("Cannot find %s class file".formatted(name));
67 }
68 classBytes = resource.readAllBytes();
69 } catch (IOException e) {
70 throw new IllegalStateException("Cannot read %s class file".formatted(name), e);
71 }
72 Class<?> clazz;
73 try {
74 clazz = lookup.defineClass(classBytes);
75 } catch (IllegalAccessException e) {
76 throw new IllegalStateException("Cannot define %s class".formatted(name), e);
77 }
78 return clazz;
79 }
80
81 public RefineryReteEngine(IQueryBackendContext context, int reteThreads, boolean deleteAndReDeriveEvaluation,
82 TimelyConfiguration timelyConfiguration) {
83 super(context, reteThreads, deleteAndReDeriveEvaluation, timelyConfiguration);
84 installFactories();
85 }
86
87 private void installFactories() {
88 var logger = getLogger();
89 Object nodeFactory;
90 try {
91 nodeFactory = REFINERY_NODE_FACTORY_CONSTRUCTOR.invoke(logger);
92 } catch (Error e) {
93 // Fatal JVM errors should not be wrapped.
94 throw e;
95 } catch (Throwable e) {
96 throw new IllegalStateException("Cannot construct node factory", e);
97 }
98 try {
99 NETWORK_NODE_FACTORY_SETTER.invoke(reteNet, nodeFactory);
100 } catch (Error e) {
101 // Fatal JVM errors should not be wrapped.
102 throw e;
103 } catch (Throwable e) {
104 throw new IllegalStateException("Cannot set factory", e);
105 }
106 for (var container : reteNet.getContainers()) {
107 Object connectionFactory;
108 try {
109 connectionFactory = REFINERY_CONNECTION_FACTORY_CONSTRUCTOR.invoke(container);
110 } catch (Error e) {
111 // Fatal JVM errors should not be wrapped.
112 throw e;
113 } catch (Throwable e) {
114 throw new IllegalStateException("Cannot construct connection factory", e);
115 }
116 var provisioner = container.getProvisioner();
117 try {
118 RETE_CONTAINER_CONNECTION_FACTORY_SETTER.invoke(container, connectionFactory);
119 NODE_PROVISIONER_NODE_FACTORY_SETTER.invoke(provisioner, nodeFactory);
120 NODE_PROVISIONER_CONNECTION_FACTORY_SETTER.invoke(provisioner, connectionFactory);
121 } catch (Error e) {
122 // Fatal JVM errors should not be wrapped.
123 throw e;
124 } catch (Throwable e) {
125 throw new IllegalStateException("Cannot set factory", e);
126 }
127 }
128 }
129
130 @Override
131 public IQueryBackendFactory getFactory() {
132 return RefineryReteBackendFactory.INSTANCE;
133 }
134}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteRecipeCompiler.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteRecipeCompiler.java
deleted file mode 100644
index fd1b48d8..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteRecipeCompiler.java
+++ /dev/null
@@ -1,228 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.store.query.viatra.internal.rete;
10
11import org.apache.log4j.Logger;
12import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendHintProvider;
13import org.eclipse.viatra.query.runtime.matchers.context.IQueryCacheContext;
14import org.eclipse.viatra.query.runtime.matchers.context.IQueryMetaContext;
15import org.eclipse.viatra.query.runtime.matchers.planning.IQueryPlannerStrategy;
16import org.eclipse.viatra.query.runtime.matchers.planning.SubPlan;
17import org.eclipse.viatra.query.runtime.matchers.planning.operations.PApply;
18import org.eclipse.viatra.query.runtime.matchers.planning.operations.PEnumerate;
19import org.eclipse.viatra.query.runtime.matchers.psystem.EnumerablePConstraint;
20import org.eclipse.viatra.query.runtime.matchers.psystem.analysis.QueryAnalyzer;
21import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
22import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PDisjunctionRewriterCacher;
23import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
24import org.eclipse.viatra.query.runtime.rete.construction.plancompiler.CompilerHelper;
25import org.eclipse.viatra.query.runtime.rete.construction.plancompiler.ReteRecipeCompiler;
26import org.eclipse.viatra.query.runtime.rete.matcher.TimelyConfiguration;
27import org.eclipse.viatra.query.runtime.rete.recipes.ReteNodeRecipe;
28import org.eclipse.viatra.query.runtime.rete.traceability.CompiledSubPlan;
29import org.eclipse.viatra.query.runtime.rete.traceability.PlanningTrace;
30import org.eclipse.viatra.query.runtime.rete.util.ReteHintOptions;
31import org.jetbrains.annotations.Nullable;
32import tools.refinery.store.query.viatra.internal.pquery.RepresentativeElectionConstraint;
33import tools.refinery.store.query.viatra.internal.rete.recipe.RefineryRecipesFactory;
34import tools.refinery.store.query.viatra.internal.pquery.rewriter.RefineryPBodyNormalizer;
35import tools.refinery.store.query.viatra.internal.pquery.rewriter.RefinerySurrogateQueryRewriter;
36
37import java.lang.invoke.MethodHandle;
38import java.lang.invoke.MethodHandles;
39import java.lang.invoke.MethodType;
40import java.lang.reflect.Field;
41import java.util.Map;
42
43// Since we don't modify VIATRA code, this is our last resort.
44@SuppressWarnings("squid:S3011")
45public class RefineryReteRecipeCompiler extends ReteRecipeCompiler {
46 private static final MethodHandle GET_SUB_PLAN_COMPILER_CACHE;
47 private static final MethodHandle GET_COMPILER_BACK_TRACE;
48 private static final Field NORMALIZER_FIELD;
49 private static final MethodHandle DO_COMPILE_DISPATCH;
50 private static final MethodHandle COMPILE_TO_NATURAL_JOIN;
51 private static final MethodHandle REFER_QUERY;
52
53 static {
54 MethodHandles.Lookup lookup;
55 try {
56 lookup = MethodHandles.privateLookupIn(ReteRecipeCompiler.class, MethodHandles.lookup());
57 } catch (IllegalAccessException e) {
58 throw new IllegalStateException("Failed to create lookup", e);
59 }
60 try {
61 GET_SUB_PLAN_COMPILER_CACHE = lookup.findGetter(ReteRecipeCompiler.class, "subPlanCompilerCache",
62 Map.class);
63 GET_COMPILER_BACK_TRACE = lookup.findGetter(ReteRecipeCompiler.class, "compilerBackTrace", Map.class);
64 } catch (NoSuchFieldException | IllegalAccessException e) {
65 throw new IllegalStateException("Failed to find getter", e);
66 }
67
68 try {
69 NORMALIZER_FIELD = ReteRecipeCompiler.class.getDeclaredField("normalizer");
70 } catch (NoSuchFieldException e) {
71 throw new IllegalStateException("Failed to find field", e);
72 }
73 NORMALIZER_FIELD.setAccessible(true);
74
75 try {
76 DO_COMPILE_DISPATCH = lookup.findVirtual(ReteRecipeCompiler.class, "doCompileDispatch",
77 MethodType.methodType(CompiledSubPlan.class, SubPlan.class));
78 COMPILE_TO_NATURAL_JOIN = lookup.findVirtual(ReteRecipeCompiler.class, "compileToNaturalJoin",
79 MethodType.methodType(CompiledSubPlan.class, SubPlan.class, PlanningTrace.class,
80 PlanningTrace.class));
81 REFER_QUERY = lookup.findVirtual(ReteRecipeCompiler.class, "referQuery",
82 MethodType.methodType(PlanningTrace.class, PQuery.class, SubPlan.class, Tuple.class));
83 } catch (NoSuchMethodException | IllegalAccessException e) {
84 throw new IllegalStateException("Failed to find method", e);
85 }
86 }
87
88 private final Map<SubPlan, CompiledSubPlan> subPlanCompilerCache;
89 private final Map<ReteNodeRecipe, SubPlan> compilerBackTrace;
90
91 public RefineryReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger,
92 IQueryMetaContext metaContext, IQueryCacheContext queryCacheContext,
93 IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer,
94 boolean deleteAndReDeriveEvaluation, TimelyConfiguration timelyEvaluation) {
95 super(plannerStrategy, logger, metaContext, queryCacheContext, hintProvider, queryAnalyzer,
96 deleteAndReDeriveEvaluation, timelyEvaluation);
97
98 var normalizer = new PDisjunctionRewriterCacher(new RefinerySurrogateQueryRewriter(),
99 new RefineryPBodyNormalizer(metaContext) {
100
101 @Override
102 protected boolean shouldExpandWeakenedAlternatives(PQuery query) {
103 var hint = hintProvider.getQueryEvaluationHint(query);
104 return ReteHintOptions.expandWeakenedAlternativeConstraints.getValueOrDefault(hint);
105 }
106
107 });
108 try {
109 // https://docs.oracle.com/javase/specs/jls/se17/html/jls-17.html#jls-17.5.3
110 // "The object should not be made visible to other threads, nor should the final fields be read,
111 // until all updates to the final fields of the object are complete."
112 // The {@code super} constructor only sets but doesn't read the {@code normalizer} field,
113 // therefore this is fine.
114 NORMALIZER_FIELD.set(this, normalizer);
115 } catch (IllegalAccessException e) {
116 throw new IllegalStateException("Failed to set private final field", e);
117 }
118
119 try {
120 @SuppressWarnings("unchecked")
121 var cache = (Map<SubPlan, CompiledSubPlan>) GET_SUB_PLAN_COMPILER_CACHE.invokeExact(
122 (ReteRecipeCompiler) this);
123 subPlanCompilerCache = cache;
124 @SuppressWarnings("unchecked")
125 var backTrace = (Map<ReteNodeRecipe, SubPlan>) GET_COMPILER_BACK_TRACE.invokeExact(
126 (ReteRecipeCompiler) this);
127 compilerBackTrace = backTrace;
128 } catch (Error e) {
129 // Fatal JVM errors should not be wrapped.
130 throw e;
131 } catch (Throwable e) {
132 throw new IllegalStateException("Failed to access private fields", e);
133 }
134 }
135
136 @Override
137 public CompiledSubPlan getCompiledForm(SubPlan plan) {
138 CompiledSubPlan compiled = subPlanCompilerCache.get(plan);
139 if (compiled == null) {
140 compiled = doCompileDispatchExtension(plan);
141 if (compiled == null) {
142 compiled = superDoCompileDispatch(plan);
143 }
144 subPlanCompilerCache.put(plan, compiled);
145 compilerBackTrace.put(compiled.getRecipe(), plan);
146 }
147 return compiled;
148 }
149
150 @Nullable
151 private CompiledSubPlan doCompileDispatchExtension(SubPlan plan) {
152 var operation = plan.getOperation();
153 if (operation instanceof PEnumerate enumerateOperation) {
154 return doCompileEnumerateExtension(enumerateOperation.getEnumerablePConstraint(), plan);
155 } else if (operation instanceof PApply applyOperation &&
156 applyOperation.getPConstraint() instanceof EnumerablePConstraint constraint) {
157 var secondaryParent = doEnumerateDispatchExtension(plan, constraint);
158 if (secondaryParent != null) {
159 var primaryParent = getCompiledForm(plan.getParentPlans().get(0));
160 return superCompileToNaturalJoin(plan, primaryParent, secondaryParent);
161 }
162 }
163 return null;
164 }
165
166 @Nullable
167 private CompiledSubPlan doCompileEnumerateExtension(EnumerablePConstraint constraint, SubPlan plan) {
168 var coreTrace = doEnumerateDispatchExtension(plan, constraint);
169 if (coreTrace == null) {
170 return null;
171 }
172 var trimmedTrace = CompilerHelper.checkAndTrimEqualVariables(plan, coreTrace);
173 return trimmedTrace.cloneFor(plan);
174 }
175
176 @Nullable
177 private PlanningTrace doEnumerateDispatchExtension(SubPlan plan, EnumerablePConstraint constraint) {
178 if (constraint instanceof RepresentativeElectionConstraint representativeElectionConstraint) {
179 return compileEnumerableExtension(plan, representativeElectionConstraint);
180 }
181 return null;
182 }
183
184 private PlanningTrace compileEnumerableExtension(SubPlan plan, RepresentativeElectionConstraint constraint) {
185 var referredQuery = constraint.getSupplierKey();
186 var callTrace = superReferQuery(referredQuery, plan, constraint.getVariablesTuple());
187 var recipe = RefineryRecipesFactory.eINSTANCE.createRepresentativeElectionRecipe();
188 recipe.setParent(callTrace.getRecipe());
189 recipe.setConnectivity(constraint.getConnectivity());
190 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace);
191 }
192
193 private CompiledSubPlan superDoCompileDispatch(SubPlan plan) {
194 try {
195 return (CompiledSubPlan) DO_COMPILE_DISPATCH.invokeExact((ReteRecipeCompiler) this, plan);
196 } catch (Error | RuntimeException e) {
197 // Fatal JVM errors and runtime exceptions should not be wrapped.
198 throw e;
199 } catch (Throwable e) {
200 throw new IllegalStateException("Failed to call doCompileDispatch", e);
201 }
202 }
203
204 private CompiledSubPlan superCompileToNaturalJoin(SubPlan plan, PlanningTrace leftCompiled,
205 PlanningTrace rightCompiled) {
206 try {
207 return (CompiledSubPlan) COMPILE_TO_NATURAL_JOIN.invokeExact((ReteRecipeCompiler) this, plan,
208 leftCompiled, rightCompiled);
209 } catch (Error | RuntimeException e) {
210 // Fatal JVM errors and runtime exceptions should not be wrapped.
211 throw e;
212 } catch (Throwable e) {
213 throw new IllegalStateException("Failed to call compileToNaturalJoin", e);
214 }
215 }
216
217 private PlanningTrace superReferQuery(PQuery query, SubPlan plan, Tuple actualParametersTuple) {
218 try {
219 return (PlanningTrace) REFER_QUERY.invokeExact((ReteRecipeCompiler) this, query, plan,
220 actualParametersTuple);
221 } catch (Error | RuntimeException e) {
222 // Fatal JVM errors and runtime exceptions should not be wrapped.
223 throw e;
224 } catch (Throwable e) {
225 throw new IllegalStateException("Failed to call referQuery", e);
226 }
227 }
228}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RefineryConnectionFactoryExtensions.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RefineryConnectionFactoryExtensions.java
deleted file mode 100644
index 0fe5ee27..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RefineryConnectionFactoryExtensions.java
+++ /dev/null
@@ -1,47 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.rete.network;
7
8import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
9import org.eclipse.viatra.query.runtime.rete.network.Node;
10import org.eclipse.viatra.query.runtime.rete.network.ReteContainer;
11import org.eclipse.viatra.query.runtime.rete.network.Supplier;
12import org.eclipse.viatra.query.runtime.rete.remote.Address;
13import org.eclipse.viatra.query.runtime.rete.traceability.RecipeTraceInfo;
14import tools.refinery.store.query.viatra.internal.rete.recipe.RepresentativeElectionRecipe;
15
16import java.util.ArrayList;
17
18public class RefineryConnectionFactoryExtensions {
19 private final ReteContainer reteContainer;
20
21 public RefineryConnectionFactoryExtensions(ReteContainer reteContainer) {
22 this.reteContainer = reteContainer;
23 }
24
25 public boolean connectToParents(RecipeTraceInfo recipeTrace, Node freshNode) {
26 var recipe = recipeTrace.getRecipe();
27 if (recipe instanceof RepresentativeElectionRecipe representativeElectionRecipe) {
28 connectToParents(representativeElectionRecipe, (RepresentativeElectionNode) freshNode);
29 return true;
30 }
31 return false;
32 }
33
34 private void connectToParents(RepresentativeElectionRecipe recipe, RepresentativeElectionNode freshNode) {
35 var parentRecipe = recipe.getParent();
36 // Apparently VIATRA ensures that this cast is safe, see
37 // {@link org.eclipse.viatra.query.runtime.rete.network.ConnectionFactory.connectToParent}.
38 @SuppressWarnings("unchecked")
39 var parentAddress = (Address<? extends Supplier>) reteContainer.getNetwork()
40 .getExistingNodeByRecipe(parentRecipe);
41 var parentSupplier = reteContainer.getProvisioner().asSupplier(parentAddress);
42 var tuples = new ArrayList<Tuple>();
43 parentSupplier.pullInto(tuples, true);
44 freshNode.reinitializeWith(tuples);
45 reteContainer.connect(parentSupplier, freshNode);
46 }
47}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RefineryNodeFactoryExtensions.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RefineryNodeFactoryExtensions.java
deleted file mode 100644
index 82b63a55..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RefineryNodeFactoryExtensions.java
+++ /dev/null
@@ -1,44 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.rete.network;
7
8import org.eclipse.viatra.query.runtime.rete.network.ReteContainer;
9import org.eclipse.viatra.query.runtime.rete.network.Supplier;
10import org.eclipse.viatra.query.runtime.rete.recipes.ReteNodeRecipe;
11import org.eclipse.viatra.query.runtime.rete.traceability.TraceInfo;
12import org.jetbrains.annotations.Nullable;
13import tools.refinery.store.query.viatra.internal.rete.recipe.RepresentativeElectionRecipe;
14
15public class RefineryNodeFactoryExtensions {
16 @Nullable
17 public Supplier createNode(ReteContainer reteContainer, ReteNodeRecipe recipe, TraceInfo... traces) {
18 var result = instantiateNode(reteContainer, recipe);
19 if (result == null) {
20 return null;
21 }
22 for (var traceInfo : traces) {
23 result.assignTraceInfo(traceInfo);
24 }
25 return result;
26 }
27
28 @Nullable
29 private Supplier instantiateNode(ReteContainer reteContainer, ReteNodeRecipe recipe) {
30 if (recipe instanceof RepresentativeElectionRecipe representativeElectionRecipe) {
31 return instantiateRepresentativeElectionNode(reteContainer, representativeElectionRecipe);
32 }
33 return null;
34 }
35
36 private Supplier instantiateRepresentativeElectionNode(ReteContainer reteContainer,
37 RepresentativeElectionRecipe recipe) {
38 RepresentativeElectionAlgorithm.Factory algorithmFactory = switch (recipe.getConnectivity()) {
39 case STRONG -> StronglyConnectedComponentAlgorithm::new;
40 case WEAK -> WeaklyConnectedComponentAlgorithm::new;
41 };
42 return new RepresentativeElectionNode(reteContainer, algorithmFactory);
43 }
44}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesFactory.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesFactory.java
deleted file mode 100644
index 1f8b3034..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesFactory.java
+++ /dev/null
@@ -1,22 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.rete.recipe;
7
8import org.eclipse.emf.ecore.EDataType;
9import org.eclipse.emf.ecore.EFactory;
10import tools.refinery.store.query.literal.Connectivity;
11
12// Naming and index computation follows EMF conventions.
13@SuppressWarnings("squid:S115")
14public interface RefineryRecipesFactory extends EFactory {
15 RefineryRecipesFactory eINSTANCE = RefineryRecipesFactoryImpl.init();
16
17 RepresentativeElectionRecipe createRepresentativeElectionRecipe();
18
19 Connectivity createConnectivityFromString(EDataType eDataType, String initialValue);
20
21 String convertConnectivityToString(EDataType eDataType, Object instanceValue);
22}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesFactoryImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesFactoryImpl.java
deleted file mode 100644
index 4e2a695c..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesFactoryImpl.java
+++ /dev/null
@@ -1,74 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.rete.recipe;
7
8import org.eclipse.emf.ecore.EClass;
9import org.eclipse.emf.ecore.EDataType;
10import org.eclipse.emf.ecore.EObject;
11import org.eclipse.emf.ecore.EPackage;
12import org.eclipse.emf.ecore.impl.EFactoryImpl;
13import org.eclipse.emf.ecore.plugin.EcorePlugin;
14import tools.refinery.store.query.literal.Connectivity;
15
16public class RefineryRecipesFactoryImpl extends EFactoryImpl implements RefineryRecipesFactory {
17 public static RefineryRecipesFactory init() {
18 try {
19 var factory = (RefineryRecipesFactory) EPackage.Registry.INSTANCE.getEFactory(
20 RefineryRecipesPackage.eNS_URI);
21 if (factory != null) {
22 return factory;
23 }
24 }
25 catch (Exception exception) {
26 EcorePlugin.INSTANCE.log(exception);
27 }
28 return new RefineryRecipesFactoryImpl();
29 }
30
31 @Override
32 public EObject create(EClass eClass) {
33 if (eClass.getClassifierID() == RefineryRecipesPackage.REPRESENTATIVE_ELECTION_RECIPE) {
34 return createRepresentativeElectionRecipe();
35 } else {
36 throw new IllegalArgumentException("The class '%s' is not a valid classifier".formatted(eClass.getName()));
37 }
38 }
39
40 @Override
41 public Object createFromString(EDataType eDataType, String stringValue) {
42 if (eDataType.getClassifierID() == RefineryRecipesPackage.CONNECTIVITY) {
43 return createConnectivityFromString(eDataType, stringValue);
44 } else {
45 throw new IllegalArgumentException("The datatype '%s' is not a valid classifier"
46 .formatted(eDataType.getName()));
47 }
48 }
49
50 @Override
51 public String convertToString(EDataType eDataType, Object objectValue) {
52 if (eDataType.getClassifierID() == RefineryRecipesPackage.CONNECTIVITY) {
53 return convertConnectivityToString(eDataType, objectValue);
54 } else {
55 throw new IllegalArgumentException("The datatype '%s' is not a valid classifier"
56 .formatted(eDataType.getName()));
57 }
58 }
59
60 @Override
61 public RepresentativeElectionRecipe createRepresentativeElectionRecipe() {
62 return new RepresentativeElectionRecipeImpl();
63 }
64
65 @Override
66 public Connectivity createConnectivityFromString(EDataType eDataType, String initialValue) {
67 return (Connectivity) super.createFromString(eDataType, initialValue);
68 }
69
70 @Override
71 public String convertConnectivityToString(EDataType eDataType, Object instanceValue) {
72 return super.convertToString(eDataType, instanceValue);
73 }
74}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesPackage.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesPackage.java
deleted file mode 100644
index 6c933c45..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesPackage.java
+++ /dev/null
@@ -1,41 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.rete.recipe;
7
8import org.eclipse.emf.ecore.*;
9import org.eclipse.viatra.query.runtime.rete.recipes.RecipesPackage;
10
11// Naming and index computation follows EMF conventions.
12@SuppressWarnings({"squid:S100", "squid:S115", "PointlessArithmeticExpression"})
13public interface RefineryRecipesPackage extends EPackage {
14 String eNAME = "refineryReteRecipes";
15
16 String eNS_URI = "https://refinery.tools/emf/2021/RefineryReteRecipes";
17
18 String eNS_PREFIX = "refineryReteRecipes";
19
20 RefineryRecipesPackage eINSTANCE = RefineryRecipesPackageImpl.init();
21
22 int REPRESENTATIVE_ELECTION_RECIPE = 0;
23
24 int REPRESENTATIVE_ELECTION_RECIPE__CONNECTIVITY = RecipesPackage.ALPHA_RECIPE_FEATURE_COUNT + 0;
25
26 int REPRESENTATIVE_ELECTION_RECIPE_FEATURE_COUNT = RecipesPackage.ALPHA_RECIPE_FEATURE_COUNT + 1;
27
28 int REPRESENTATIVE_ELECTION_RECIPE__GET_ARITY = RecipesPackage.ALPHA_RECIPE_OPERATION_COUNT + 0;
29
30 int REPRESENTATIVE_ELECTION_RECIPE_OPERATION_COUNT = RecipesPackage.ALPHA_RECIPE_OPERATION_COUNT + 1;
31
32 int CONNECTIVITY = 1;
33
34 EClass getRepresentativeElectionRecipe();
35
36 EAttribute getRepresentativeElectionRecipe_Connectivity();
37
38 EOperation getRepresentativeElectionRecipe_GetArity();
39
40 EDataType getConnectivity();
41}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesPackageImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesPackageImpl.java
deleted file mode 100644
index d5073402..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesPackageImpl.java
+++ /dev/null
@@ -1,96 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.rete.recipe;
7
8import org.eclipse.emf.ecore.*;
9import org.eclipse.emf.ecore.impl.EPackageImpl;
10import org.eclipse.viatra.query.runtime.rete.recipes.RecipesPackage;
11import tools.refinery.store.query.literal.Connectivity;
12
13public class RefineryRecipesPackageImpl extends EPackageImpl implements RefineryRecipesPackage {
14 private static boolean isInstanceInitialized;
15 private boolean isCreated;
16 private boolean isInitialized;
17 private EClass representativeElectionRecipe;
18 private EDataType connectivity;
19
20 public static RefineryRecipesPackage init() {
21 if (isInstanceInitialized) {
22 return (RefineryRecipesPackage) Registry.INSTANCE.getEPackage(eNS_URI);
23 }
24 var thePackage = Registry.INSTANCE.get(eNS_URI) instanceof RefineryRecipesPackageImpl impl ? impl :
25 new RefineryRecipesPackageImpl();
26 isInstanceInitialized = true;
27 thePackage.createPackageContents();
28 thePackage.initializePackageContents();
29 thePackage.freeze();
30 Registry.INSTANCE.put(eNS_URI, thePackage);
31 return thePackage;
32 }
33
34 private RefineryRecipesPackageImpl() {
35 super(eNS_URI, RefineryRecipesFactory.eINSTANCE);
36 }
37
38 @Override
39 public EClass getRepresentativeElectionRecipe() {
40 return representativeElectionRecipe;
41 }
42
43 @Override
44 public EAttribute getRepresentativeElectionRecipe_Connectivity() {
45 return (EAttribute) representativeElectionRecipe.getEStructuralFeatures().get(0);
46 }
47
48 @Override
49 public EOperation getRepresentativeElectionRecipe_GetArity() {
50 return representativeElectionRecipe.getEOperations().get(0);
51 }
52
53 @Override
54 public EDataType getConnectivity() {
55 return connectivity;
56 }
57
58 public void createPackageContents() {
59 if (isCreated) {
60 return;
61 }
62 isCreated = true;
63
64 representativeElectionRecipe = createEClass(REPRESENTATIVE_ELECTION_RECIPE);
65 createEAttribute(representativeElectionRecipe, REPRESENTATIVE_ELECTION_RECIPE__CONNECTIVITY);
66 createEOperation(representativeElectionRecipe, REPRESENTATIVE_ELECTION_RECIPE__GET_ARITY);
67
68 connectivity = createEDataType(CONNECTIVITY);
69 }
70
71 public void initializePackageContents() {
72 if (isInitialized) {
73 return;
74 }
75 isInitialized = true;
76
77 setName(eNAME);
78 setNsPrefix(eNS_PREFIX);
79 setNsURI(eNS_URI);
80
81 representativeElectionRecipe.getESuperTypes().add(RecipesPackage.Literals.ALPHA_RECIPE);
82
83 initEClass(representativeElectionRecipe, RepresentativeElectionRecipe.class,
84 "RepresentativeElectionRecipe", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);
85 initEAttribute(getRepresentativeElectionRecipe_Connectivity(), getConnectivity(), "connectivity", null, 0, 1,
86 RepresentativeElectionRecipe.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID,
87 IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
88 initEOperation(getRepresentativeElectionRecipe_GetArity(), EcorePackage.Literals.EINT, "getArity", 0, 1,
89 !IS_UNIQUE, IS_ORDERED);
90
91 initEDataType(connectivity, Connectivity.class, "Connectivity", IS_SERIALIZABLE,
92 !IS_GENERATED_INSTANCE_CLASS);
93
94 createResource(eNS_URI);
95 }
96}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RepresentativeElectionRecipe.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RepresentativeElectionRecipe.java
deleted file mode 100644
index def825c2..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RepresentativeElectionRecipe.java
+++ /dev/null
@@ -1,17 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.rete.recipe;
7
8import org.eclipse.viatra.query.runtime.rete.recipes.AlphaRecipe;
9import tools.refinery.store.query.literal.Connectivity;
10
11public interface RepresentativeElectionRecipe extends AlphaRecipe {
12 Connectivity getConnectivity();
13
14 void setConnectivity(Connectivity connectivity);
15
16 int getArity();
17}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RepresentativeElectionRecipeImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RepresentativeElectionRecipeImpl.java
deleted file mode 100644
index 959836d2..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RepresentativeElectionRecipeImpl.java
+++ /dev/null
@@ -1,105 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.rete.recipe;
7
8import org.eclipse.emf.common.notify.Notification;
9import org.eclipse.emf.common.util.EList;
10import org.eclipse.emf.ecore.EClass;
11import org.eclipse.emf.ecore.impl.ENotificationImpl;
12import org.eclipse.viatra.query.runtime.rete.recipes.RecipesPackage;
13import org.eclipse.viatra.query.runtime.rete.recipes.ReteNodeRecipe;
14import org.eclipse.viatra.query.runtime.rete.recipes.impl.AlphaRecipeImpl;
15import tools.refinery.store.query.literal.Connectivity;
16
17import java.lang.reflect.InvocationTargetException;
18
19public class RepresentativeElectionRecipeImpl extends AlphaRecipeImpl implements RepresentativeElectionRecipe {
20 private Connectivity connectivity;
21
22 @Override
23 protected EClass eStaticClass() {
24 return RefineryRecipesPackage.eINSTANCE.getRepresentativeElectionRecipe();
25 }
26
27 @Override
28 public Connectivity getConnectivity() {
29 return connectivity;
30 }
31
32 @Override
33 public void setConnectivity(Connectivity newStrong) {
34 var oldConnectivity = connectivity;
35 connectivity = newStrong;
36 if (eNotificationRequired()) {
37 eNotify(new ENotificationImpl(this, Notification.SET,
38 RefineryRecipesPackage.REPRESENTATIVE_ELECTION_RECIPE__CONNECTIVITY, oldConnectivity,
39 connectivity));
40 }
41 }
42
43 @Override
44 public int getArity() {
45 return 2;
46 }
47
48 @Override
49 public Object eGet(int featureID, boolean resolve, boolean coreType) {
50 if (featureID == RefineryRecipesPackage.REPRESENTATIVE_ELECTION_RECIPE__CONNECTIVITY) {
51 return getConnectivity();
52 }
53 return super.eGet(featureID, resolve, coreType);
54 }
55
56 @Override
57 public void eSet(int featureID, Object newValue) {
58 if (featureID == RefineryRecipesPackage.REPRESENTATIVE_ELECTION_RECIPE__CONNECTIVITY) {
59 setConnectivity((Connectivity) newValue);
60 } else {
61 super.eSet(featureID, newValue);
62 }
63 }
64
65 @Override
66 public void eUnset(int featureID) {
67 if (featureID == RefineryRecipesPackage.REPRESENTATIVE_ELECTION_RECIPE__CONNECTIVITY) {
68 setConnectivity(null);
69 } else {
70 super.eUnset(featureID);
71 }
72 }
73
74 @Override
75 public boolean eIsSet(int featureID) {
76 if (featureID == RefineryRecipesPackage.REPRESENTATIVE_ELECTION_RECIPE__CONNECTIVITY) {
77 return connectivity != null;
78 }
79 return super.eIsSet(featureID);
80 }
81
82 @Override
83 public int eDerivedOperationID(int baseOperationID, Class<?> baseClass) {
84 if (baseClass == ReteNodeRecipe.class && baseOperationID == RecipesPackage.RETE_NODE_RECIPE___GET_ARITY) {
85 return RefineryRecipesPackage.REPRESENTATIVE_ELECTION_RECIPE__GET_ARITY;
86 }
87 return super.eDerivedOperationID(baseOperationID, baseClass);
88 }
89
90 @Override
91 public Object eInvoke(int operationID, EList<?> arguments) throws InvocationTargetException {
92 if (operationID == RefineryRecipesPackage.REPRESENTATIVE_ELECTION_RECIPE__GET_ARITY) {
93 return getArity();
94 }
95 return super.eInvoke(operationID, arguments);
96 }
97
98 @Override
99 public String toString() {
100 if (eIsProxy()) {
101 return super.toString();
102 }
103 return "%s (connectivity: %s)".formatted(super.toString(), connectivity);
104 }
105}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java
index 986bb0b1..e1bc9efc 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java
@@ -5,9 +5,9 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.update; 6package tools.refinery.store.query.viatra.internal.update;
7 7
8import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 8import tools.refinery.viatra.runtime.matchers.context.IInputKey;
9import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; 9import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener;
10import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 10import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
11import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 11import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
12import tools.refinery.store.query.view.AnySymbolView; 12import tools.refinery.store.query.view.AnySymbolView;
13import tools.refinery.store.query.view.SymbolView; 13import tools.refinery.store.query.view.SymbolView;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java
index efdbfcbe..73c4a3f9 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java
@@ -5,10 +5,10 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.update; 6package tools.refinery.store.query.viatra.internal.update;
7 7
8import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 8import tools.refinery.viatra.runtime.matchers.context.IInputKey;
9import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; 9import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener;
10import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 10import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
11import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; 11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12 12
13import java.util.Arrays; 13import java.util.Arrays;
14import java.util.Objects; 14import java.util.Objects;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/SymbolViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/SymbolViewUpdateListener.java
index f1a2ac7c..d0cdda72 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/SymbolViewUpdateListener.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/SymbolViewUpdateListener.java
@@ -5,10 +5,10 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.update; 6package tools.refinery.store.query.viatra.internal.update;
7 7
8import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 8import tools.refinery.viatra.runtime.matchers.context.IInputKey;
9import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; 9import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener;
10import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 10import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
11import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; 11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import tools.refinery.store.model.Interpretation; 12import tools.refinery.store.model.Interpretation;
13import tools.refinery.store.model.InterpretationListener; 13import tools.refinery.store.model.InterpretationListener;
14import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 14import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingViewUpdateListener.java
index 45d35571..9dc739f1 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingViewUpdateListener.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingViewUpdateListener.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.update; 6package tools.refinery.store.query.viatra.internal.update;
7 7
8import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 8import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
9import tools.refinery.store.model.Interpretation; 9import tools.refinery.store.model.Interpretation;
10import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 10import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
11import tools.refinery.store.query.view.SymbolView; 11import tools.refinery.store.query.view.SymbolView;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingViewUpdateListener.java
index c18dbafb..7dbd50b3 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingViewUpdateListener.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingViewUpdateListener.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.update; 6package tools.refinery.store.query.viatra.internal.update;
7 7
8import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 8import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
9import tools.refinery.store.model.Interpretation; 9import tools.refinery.store.model.Interpretation;
10import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 10import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
11import tools.refinery.store.query.view.TuplePreservingView; 11import tools.refinery.store.query.view.TuplePreservingView;
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java
index 3d2d5f83..85bdc204 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra; 6package tools.refinery.store.query.viatra;
7 7
8import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 8import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
9import tools.refinery.store.model.ModelStore; 9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.query.ModelQueryAdapter; 10import tools.refinery.store.query.ModelQueryAdapter;
11import tools.refinery.store.query.dnf.Dnf; 11import tools.refinery.store.query.dnf.Dnf;
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java
index 7f5c24fe..7190d8f1 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra; 6package tools.refinery.store.query.viatra;
7 7
8import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 8import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
9import tools.refinery.store.map.Cursor; 9import tools.refinery.store.map.Cursor;
10import tools.refinery.store.model.ModelStore; 10import tools.refinery.store.model.ModelStore;
11import tools.refinery.store.query.ModelQueryAdapter; 11import tools.refinery.store.query.ModelQueryAdapter;
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
index 8e945731..ce403e3a 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra; 6package tools.refinery.store.query.viatra;
7 7
8import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 8import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
9import org.junit.jupiter.api.Test; 9import org.junit.jupiter.api.Test;
10import tools.refinery.store.model.ModelStore; 10import tools.refinery.store.model.ModelStore;
11import tools.refinery.store.query.ModelQueryAdapter; 11import tools.refinery.store.query.ModelQueryAdapter;
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java
index 66f043c6..5a484119 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra; 6package tools.refinery.store.query.viatra;
7 7
8import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 8import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
9import org.junit.jupiter.api.Disabled; 9import org.junit.jupiter.api.Disabled;
10import org.junit.jupiter.api.Test; 10import org.junit.jupiter.api.Test;
11import tools.refinery.store.model.ModelStore; 11import tools.refinery.store.model.ModelStore;
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtilsTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtilsTest.java
index 968c6c5e..319797a0 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtilsTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtilsTest.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.matcher; 6package tools.refinery.store.query.viatra.internal.matcher;
7 7
8import org.eclipse.viatra.query.runtime.matchers.tuple.*; 8import tools.refinery.viatra.runtime.matchers.tuple.*;
9import org.junit.jupiter.api.Test; 9import org.junit.jupiter.api.Test;
10import tools.refinery.store.tuple.Tuple; 10import tools.refinery.store.tuple.Tuple;
11import tools.refinery.store.tuple.*; 11import tools.refinery.store.tuple.*;
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java
index dc0e92c8..5f88e04b 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.tests; 6package tools.refinery.store.query.viatra.tests;
7 7
8import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 8import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
9 9
10/** 10/**
11 * Overrides {@link QueryEvaluationHint#toString()} for pretty names in parametric test names. 11 * Overrides {@link QueryEvaluationHint#toString()} for pretty names in parametric test names.
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java
index 9e75d5f3..ed356eeb 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java
@@ -5,7 +5,7 @@
5 */ 5 */
6package tools.refinery.store.query.viatra.tests; 6package tools.refinery.store.query.viatra.tests;
7 7
8import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 8import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
9import org.junit.jupiter.api.extension.ExtensionContext; 9import org.junit.jupiter.api.extension.ExtensionContext;
10import org.junit.jupiter.params.provider.Arguments; 10import org.junit.jupiter.params.provider.Arguments;
11import org.junit.jupiter.params.provider.ArgumentsProvider; 11import org.junit.jupiter.params.provider.ArgumentsProvider;
diff --git a/subprojects/viatra-runtime-base-itc/about.html b/subprojects/viatra-runtime-base-itc/about.html
new file mode 100644
index 00000000..d1d5593a
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/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-base-itc/build.gradle.kts b/subprojects/viatra-runtime-base-itc/build.gradle.kts
new file mode 100644
index 00000000..2fcdf5c4
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/build.gradle.kts
@@ -0,0 +1,13 @@
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 implementation(project(":refinery-viatra-runtime-matchers"))
13}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java
new file mode 100644
index 00000000..d0367cde
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java
@@ -0,0 +1,226 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.counting;
11
12import java.util.List;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder;
16import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder;
17import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation;
18import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource;
19import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper;
20import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
21import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver;
22import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource;
23import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver;
24import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
25import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
26
27/**
28 * This class is the optimized implementation of the Counting algorithm.
29 *
30 * @author Tamas Szabo
31 *
32 * @param <V>
33 * the type parameter of the nodes in the graph data source
34 */
35public class CountingAlg<V> implements IGraphObserver<V>, ITcDataSource<V> {
36
37 private CountingTcRelation<V> tc = null;
38 private IBiDirectionalGraphDataSource<V> gds = null;
39 private List<ITcObserver<V>> observers;
40
41 /**
42 * Constructs a new Counting algorithm and initializes the transitive closure relation with the given graph data
43 * source. Attach itself on the graph data source as an observer.
44 *
45 * @param gds
46 * the graph data source instance
47 */
48 public CountingAlg(IGraphDataSource<V> gds) {
49
50 if (gds instanceof IBiDirectionalGraphDataSource<?>) {
51 this.gds = (IBiDirectionalGraphDataSource<V>) gds;
52 } else {
53 this.gds = new IBiDirectionalWrapper<V>(gds);
54 }
55
56 observers = CollectionsFactory.<ITcObserver<V>>createObserverList();
57 tc = new CountingTcRelation<V>(true);
58
59 initTc();
60 gds.attachObserver(this);
61 }
62
63 /**
64 * Initializes the transitive closure relation.
65 */
66 private void initTc() {
67 this.setTcRelation(CountingTcRelation.createFrom(gds));
68 }
69
70 @Override
71 public void edgeInserted(V source, V target) {
72 if (!source.equals(target)) {
73 deriveTc(source, target, true);
74 }
75 }
76
77 @Override
78 public void edgeDeleted(V source, V target) {
79 if (!source.equals(target)) {
80 deriveTc(source, target, false);
81 }
82 }
83
84 @Override
85 public void nodeInserted(V n) {
86
87 }
88
89 @Override
90 public void nodeDeleted(V n) {
91 this.tc.deleteTupleEnd(n);
92 }
93
94 /**
95 * Derives the transitive closure relation when an edge is inserted or deleted.
96 *
97 * @param source
98 * the source of the edge
99 * @param target
100 * the target of the edge
101 * @param dCount
102 * the value is -1 if an edge was deleted and +1 if an edge was inserted
103 */
104 private void deriveTc(V source, V target, boolean isInsertion) {
105
106 // if (dCount == 1 && isReachable(target, source)) {
107 // System.out.println("The graph contains cycle with (" + source + ","+ target + ") edge!");
108 // }
109
110 CountingTcRelation<V> dtc = new CountingTcRelation<V>(false);
111 Set<V> tupEnds = null;
112
113 // 1. d(tc(x,y)) :- d(l(x,y))
114 if (tc.updateTuple(source, target, isInsertion)) {
115 dtc.updateTuple(source, target, true /* deltas implicitly have the same sign as isInsertion*/);
116 notifyTcObservers(source, target, isInsertion);
117 }
118
119 // 2. d(tc(x,y)) :- d(l(x,z)) & tc(z,y)
120 tupEnds = tc.getTupleEnds(target);
121 if (tupEnds != null) {
122 for (V tupEnd : tupEnds) {
123 if (!tupEnd.equals(source)) {
124 if (tc.updateTuple(source, tupEnd, isInsertion)) {
125 dtc.updateTuple(source, tupEnd, true /* deltas implicitly have the same sign as isInsertion*/);
126 notifyTcObservers(source, tupEnd, isInsertion);
127 }
128 }
129 }
130 }
131
132 // 3. d(tc(x,y)) :- lv(x,z) & d(tc(z,y))
133 CountingTcRelation<V> newTuples = dtc;
134 CountingTcRelation<V> tmp = null;
135 dtc = new CountingTcRelation<V>(false);
136
137 IMemoryView<V> nodes = null;
138
139 while (!newTuples.isEmpty()) {
140
141 tmp = dtc;
142 dtc = newTuples;
143 newTuples = tmp;
144 newTuples.clear();
145
146 for (V tS : dtc.getTupleStarts()) {
147 nodes = gds.getSourceNodes(tS);
148 for (V nS : nodes.distinctValues()) {
149 int count = nodes.getCount(nS);
150 for (int i = 0; i < count; i++) {
151 tupEnds = dtc.getTupleEnds(tS);
152 if (tupEnds != null) {
153 for (V tT : tupEnds) {
154 if (!nS.equals(tT)) {
155 if (tc.updateTuple(nS, tT, isInsertion)) {
156 newTuples.updateTuple(nS, tT, true /* deltas implicitly have the same sign as isInsertion*/);
157 notifyTcObservers(nS, tT, isInsertion);
158 }
159 }
160 }
161 }
162 }
163 }
164 }
165 }
166
167 // System.out.println(tc);
168 }
169
170 public ITcRelation<V> getTcRelation() {
171 return this.tc;
172 }
173
174 public void setTcRelation(CountingTcRelation<V> tc) {
175 this.tc = tc;
176 }
177
178 @Override
179 public boolean isReachable(V source, V target) {
180 return tc.containsTuple(source, target);
181 }
182
183 @Override
184 public void attachObserver(ITcObserver<V> to) {
185 this.observers.add(to);
186
187 }
188
189 @Override
190 public void detachObserver(ITcObserver<V> to) {
191 this.observers.remove(to);
192 }
193
194 @Override
195 public Set<V> getAllReachableTargets(V source) {
196 return tc.getTupleEnds(source);
197 }
198
199 @Override
200 public Set<V> getAllReachableSources(V target) {
201 return tc.getTupleStarts(target);
202 }
203
204 private void notifyTcObservers(V source, V target, boolean isInsertion) {
205 if (isInsertion) {
206 for (ITcObserver<V> o : observers) {
207 o.tupleInserted(source, target);
208 }
209 } else {
210 for (ITcObserver<V> o : observers) {
211 o.tupleDeleted(source, target);
212 }
213 }
214 }
215
216 @Override
217 public void dispose() {
218 tc.clear();
219 this.gds.detachObserver(this);
220 }
221
222 @Override
223 public IGraphPathFinder<V> getPathFinder() {
224 return new DFSPathFinder<V>(gds, this);
225 }
226} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java
new file mode 100644
index 00000000..8f804dfd
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java
@@ -0,0 +1,259 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.counting;
11
12import java.util.Collections;
13import java.util.List;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation;
17import tools.refinery.viatra.runtime.base.itc.alg.misc.topsort.TopologicalSorting;
18import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
21import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
22import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
23import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity;
24
25/**
26 * Transitive closure relation implementation for the Counting algorithm.
27 *
28 * @author Tamas Szabo
29 *
30 * @param <V>
31 */
32public class CountingTcRelation<V> implements ITcRelation<V> {
33
34 private IMultiLookup<V, V> tuplesForward = null;
35 private IMultiLookup<V, V> tuplesBackward = null;
36
37 protected CountingTcRelation(boolean backwardIndexing) {
38 tuplesForward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class);
39 if (backwardIndexing)
40 tuplesBackward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class);
41 }
42
43 protected boolean isEmpty() {
44 return 0 == this.tuplesForward.countKeys();
45 }
46
47 protected void clear() {
48 this.tuplesForward.clear();
49
50 if (tuplesBackward != null) {
51 this.tuplesBackward.clear();
52 }
53 }
54
55 protected void union(CountingTcRelation<V> rA) {
56 IMultiLookup<V, V> rForward = rA.tuplesForward;
57 for (V source : rForward.distinctKeys()) {
58 IMemoryView<V> targetBag = rForward.lookup(source);
59 for (V target : targetBag.distinctValues()) {
60 this.addTuple(source, target, targetBag.getCount(target));
61 }
62 }
63 }
64
65 public int getCount(V source, V target) {
66 IMemoryView<V> bucket = tuplesForward.lookup(source);
67 return bucket == null ? 0 : bucket.getCount(target);
68 }
69
70 /**
71 * Returns true if the tc relation did not contain previously such a tuple that is defined by (source,target), false
72 * otherwise (in this case count is incremented with the given count parameter).
73 *
74 * @param source
75 * the source of the tuple
76 * @param target
77 * the target of the tuple
78 * @param count
79 * the count of the tuple, must be positive
80 * @return true if the relation did not contain previously the tuple
81 */
82 public boolean addTuple(V source, V target, int count) {
83 if (tuplesBackward != null) {
84 tuplesBackward.addPairPositiveMultiplicity(target, source, count);
85 }
86
87 ChangeGranularity change =
88 tuplesForward.addPairPositiveMultiplicity(source, target, count);
89
90 return change != ChangeGranularity.DUPLICATE;
91 }
92
93 /**
94 * Derivation count of the tuple (source,target) is incremented or decremented.
95 * Returns true iff updated to / from zero derivation count.
96 * @since 1.7
97 */
98 public boolean updateTuple(V source, V target, boolean isInsertion) {
99 if (isInsertion) {
100 if (tuplesBackward != null) {
101 tuplesBackward.addPair(target, source);
102 }
103 ChangeGranularity change =
104 tuplesForward.addPair(source, target);
105 return change != ChangeGranularity.DUPLICATE;
106 } else {
107 if (tuplesBackward != null) {
108 tuplesBackward.removePair(target, source);
109 }
110 ChangeGranularity change =
111 tuplesForward.removePair(source, target);
112 return change != ChangeGranularity.DUPLICATE;
113 }
114 }
115
116 public void deleteTupleEnd(V deleted) {
117 Set<V> sourcesToDelete = CollectionsFactory.createSet();
118 Set<V> targetsToDelete = CollectionsFactory.createSet();
119
120 for (V target : tuplesForward.lookupOrEmpty(deleted).distinctValues()) {
121 targetsToDelete.add(target);
122 }
123 if (tuplesBackward != null) {
124 for (V source : tuplesBackward.lookupOrEmpty(deleted).distinctValues()) {
125 sourcesToDelete.add(source);
126 }
127 } else {
128 for (V sourceCandidate : tuplesForward.distinctKeys()) {
129 if (tuplesForward.lookupOrEmpty(sourceCandidate).containsNonZero(deleted))
130 sourcesToDelete.add(sourceCandidate);
131 }
132 }
133
134 for (V source : sourcesToDelete) {
135 int count = tuplesForward.lookupOrEmpty(source).getCount(deleted);
136 for (int i=0; i< count; ++i) tuplesForward.removePair(source, deleted);
137 }
138 for (V target : targetsToDelete) {
139 int count = tuplesForward.lookupOrEmpty(deleted).getCount(target);
140 for (int i=0; i< count; ++i) tuplesForward.removePair(deleted, target);
141 }
142
143 if (tuplesBackward != null) {
144 for (V source : sourcesToDelete) {
145 int count = tuplesBackward.lookupOrEmpty(deleted).getCount(source);
146 for (int i=0; i< count; ++i) tuplesBackward.removePair(deleted, source);
147 }
148 for (V target : targetsToDelete) {
149 int count = tuplesBackward.lookupOrEmpty(target).getCount(deleted);
150 for (int i=0; i< count; ++i) tuplesBackward.removePair(target, deleted);
151 }
152 }
153 }
154
155 @Override
156 public String toString() {
157 StringBuilder sb = new StringBuilder("TcRelation = ");
158
159 for (V source : tuplesForward.distinctKeys()) {
160 IMemoryView<V> targets = tuplesForward.lookup(source);
161 for (V target : targets.distinctValues()) {
162 sb.append("{(" + source + "," + target + ")," + targets.getCount(target) + "} ");
163 }
164 }
165
166 return sb.toString();
167 }
168
169 @Override
170 public Set<V> getTupleEnds(V source) {
171 IMemoryView<V> tupEnds = tuplesForward.lookup(source);
172 if (tupEnds == null)
173 return null;
174 return tupEnds.distinctValues();
175 }
176
177 /**
178 * Returns the set of nodes from which the target node is reachable, if already computed.
179 *
180 * @param target
181 * the target node
182 * @return the set of source nodes
183 * @throws UnsupportedOperationException if backwards index not computed
184 */
185 public Set<V> getTupleStarts(V target) {
186 if (tuplesBackward != null) {
187 IMemoryView<V> tupStarts = tuplesBackward.lookup(target);
188 if (tupStarts == null)
189 return null;
190 return tupStarts.distinctValues();
191 } else {
192 throw new UnsupportedOperationException("built without backward indexing");
193 }
194 }
195
196 @Override
197 public Set<V> getTupleStarts() {
198 Set<V> nodes = CollectionsFactory.createSet();
199 for (V s : tuplesForward.distinctKeys()) {
200 nodes.add(s);
201 }
202 return nodes;
203 }
204
205 /**
206 * Returns true if a (source, target) node is present in the transitive closure relation, false otherwise.
207 *
208 * @param source
209 * the source node
210 * @param target
211 * the target node
212 * @return true if tuple is present, false otherwise
213 */
214 public boolean containsTuple(V source, V target) {
215 return tuplesForward.lookupOrEmpty(source).containsNonZero(target);
216 }
217
218 @SuppressWarnings("unchecked")
219 @Override
220 public boolean equals(Object obj) {
221 if (this == obj) {
222 return true;
223 } else if (obj == null || this.getClass() != obj.getClass()) {
224 return false;
225 } else {
226 CountingTcRelation<V> aTR = (CountingTcRelation<V>) obj;
227
228 return tuplesForward.equals(aTR.tuplesForward);
229 }
230 }
231
232 @Override
233 public int hashCode() {
234 return tuplesForward.hashCode();
235 }
236
237 public static <V> CountingTcRelation<V> createFrom(IBiDirectionalGraphDataSource<V> gds) {
238 List<V> topologicalSorting = TopologicalSorting.compute(gds);
239 CountingTcRelation<V> tc = new CountingTcRelation<V>(true);
240 Collections.reverse(topologicalSorting);
241 for (V n : topologicalSorting) {
242 IMemoryView<V> sourceNodes = gds.getSourceNodes(n);
243 Set<V> tupEnds = tc.getTupleEnds(n);
244 for (V s : sourceNodes.distinctValues()) {
245 int count = sourceNodes.getCount(s);
246 for (int i = 0; i < count; i++) {
247 tc.updateTuple(s, n, true);
248 if (tupEnds != null) {
249 for (V t : tupEnds) {
250 tc.updateTuple(s, t, true);
251 }
252 }
253 }
254 }
255 }
256
257 return tc;
258 }
259}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java
new file mode 100644
index 00000000..b92d08bf
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java
@@ -0,0 +1,308 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.dred;
11
12import java.util.ArrayList;
13import java.util.HashMap;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Map;
17import java.util.Map.Entry;
18import java.util.Set;
19
20import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder;
21import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder;
22import tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple;
23import tools.refinery.viatra.runtime.base.itc.alg.misc.dfs.DFSAlg;
24import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
25import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver;
26import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource;
27import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver;
28import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
29
30/**
31 * This class is the optimized implementation of the DRED algorithm.
32 *
33 * @author Tamas Szabo
34 *
35 * @param <V>
36 * the type parameter of the nodes in the graph data source
37 */
38public class DRedAlg<V> implements IGraphObserver<V>, ITcDataSource<V> {
39
40 private IGraphDataSource<V> graphDataSource = null;
41 private DRedTcRelation<V> tc = null;
42 private DRedTcRelation<V> dtc = null;
43 private List<ITcObserver<V>> observers;
44
45 /**
46 * Constructs a new DRED algorithm and initializes the transitive closure relation with the given graph data source.
47 * Attach itself on the graph data source as an observer.
48 *
49 * @param gds
50 * the graph data source instance
51 */
52 public DRedAlg(IGraphDataSource<V> gds) {
53 this.observers = new ArrayList<ITcObserver<V>>();
54 this.graphDataSource = gds;
55 this.tc = new DRedTcRelation<V>();
56 this.dtc = new DRedTcRelation<V>();
57 initTc();
58 graphDataSource.attachObserver(this);
59 }
60
61 /**
62 * Constructs a new DRED algorithm and initializes the transitive closure relation with the given relation. Attach
63 * itself on the graph data source as an observer.
64 *
65 * @param gds
66 * the graph data source instance
67 * @param tc
68 * the transitive closure instance
69 */
70 public DRedAlg(IGraphDataSource<V> gds, DRedTcRelation<V> tc) {
71 this.graphDataSource = gds;
72 this.tc = tc;
73 this.dtc = new DRedTcRelation<V>();
74 graphDataSource.attachObserver(this);
75 }
76
77 /**
78 * Initializes the transitive closure relation.
79 */
80 private void initTc() {
81 DFSAlg<V> dfsa = new DFSAlg<V>(this.graphDataSource);
82 this.setTcRelation(dfsa.getTcRelation());
83 this.graphDataSource.detachObserver(dfsa);
84 }
85
86 @Override
87 public void edgeInserted(V source, V target) {
88 if (!source.equals(target)) {
89 Set<V> tupStarts = null;
90 Set<V> tupEnds = null;
91 Set<Tuple<V>> tuples = new HashSet<Tuple<V>>();
92
93 if (!source.equals(target) && tc.addTuple(source, target)) {
94 tuples.add(new Tuple<V>(source, target));
95 }
96
97 // 2. d+(tc(x,y)) :- d+(tc(x,z)) & lv(z,y) Descartes product
98 tupStarts = tc.getTupleStarts(source);
99 tupEnds = tc.getTupleEnds(target);
100
101 for (V s : tupStarts) {
102 for (V t : tupEnds) {
103 if (!s.equals(t) && tc.addTuple(s, t)) {
104 tuples.add(new Tuple<V>(s, t));
105 }
106 }
107 }
108
109 // (s, source) -> (source, target)
110 // tupStarts = tc.getTupleStarts(source);
111 for (V s : tupStarts) {
112 if (!s.equals(target) && tc.addTuple(s, target)) {
113 tuples.add(new Tuple<V>(s, target));
114 }
115 }
116
117 // (source, target) -> (target, t)
118 // tupEnds = tc.getTupleEnds(target);
119 for (V t : tupEnds) {
120 if (!source.equals(t) && tc.addTuple(source, t)) {
121 tuples.add(new Tuple<V>(source, t));
122 }
123 }
124
125 notifyTcObservers(tuples, 1);
126 }
127 }
128
129 @Override
130 public void edgeDeleted(V source, V target) {
131 if (!source.equals(target)) {
132
133 // Computing overestimate, Descartes product of A and B sets, where
134 // A: those nodes from which source is reachable
135 // B: those nodes which is reachable from target
136
137 Map<Tuple<V>, Integer> tuples = new HashMap<Tuple<V>, Integer>();
138 Set<V> sources = tc.getTupleStarts(source);
139 Set<V> targets = tc.getTupleEnds(target);
140
141 tc.removeTuple(source, target);
142 tuples.put(new Tuple<V>(source, target), -1);
143
144 for (V s : sources) {
145 for (V t : targets) {
146 if (!s.equals(t)) {
147 tc.removeTuple(s, t);
148 tuples.put(new Tuple<V>(s, t), -1);
149 }
150 }
151 }
152
153 for (V s : sources) {
154 if (!s.equals(target)) {
155 tc.removeTuple(s, target);
156 tuples.put(new Tuple<V>(s, target), -1);
157 }
158 }
159
160 for (V t : targets) {
161 if (!source.equals(t)) {
162 tc.removeTuple(source, t);
163 tuples.put(new Tuple<V>(source, t), -1);
164 }
165 }
166
167 // System.out.println("overestimate: "+dtc);
168
169 // Modify overestimate with those tuples that have alternative derivations
170 // 1. q+(tc(x,y)) :- lv(x,y)
171 for (V s : graphDataSource.getAllNodes()) {
172 IMemoryView<V> targetNodes = graphDataSource.getTargetNodes(s);
173 for (Entry<V, Integer> entry : targetNodes.entriesWithMultiplicities()) {
174 for (int i = 0; i < entry.getValue(); i++) {
175 V t = entry.getKey();
176 if (!s.equals(t)) {
177 tc.addTuple(s, t);
178 Tuple<V> tuple = new Tuple<V>(s, t);
179 Integer count = tuples.get(tuple);
180 if (count != null && count == -1) {
181 tuples.remove(tuple);
182 }
183 }
184
185 }
186 }
187 }
188
189 // 2. q+(tc(x,y)) :- tcv(x,z) & lv(z,y)
190 DRedTcRelation<V> newTups = new DRedTcRelation<V>();
191 dtc.clear();
192 dtc.union(tc);
193
194 while (!dtc.isEmpty()) {
195
196 newTups.clear();
197 newTups.union(dtc);
198 dtc.clear();
199
200 for (V s : newTups.getTupleStarts()) {
201 for (V t : newTups.getTupleEnds(s)) {
202 IMemoryView<V> targetNodes = graphDataSource.getTargetNodes(t);
203 if (targetNodes != null) {
204 for (Entry<V, Integer> entry : targetNodes.entriesWithMultiplicities()) {
205 for (int i = 0; i < entry.getValue(); i++) {
206 V tn = entry.getKey();
207 if (!s.equals(tn) && tc.addTuple(s, tn)) {
208 dtc.addTuple(s, tn);
209 tuples.remove(new Tuple<V>(s, tn));
210 }
211 }
212 }
213 }
214 }
215 }
216 }
217
218 notifyTcObservers(tuples.keySet(), -1);
219 }
220 }
221
222 @Override
223 public void nodeInserted(V n) {
224 // Node inserted does not result new tc tuple.
225 }
226
227 @Override
228 public void nodeDeleted(V n) {
229 // FIXME node deletion may involve the deletion of incoming and outgoing edges too
230 Set<V> set = tc.getTupleEnds(n);
231 Set<V> modSet = null;
232
233 // n -> target
234 modSet = new HashSet<V>(set);
235
236 for (V tn : modSet) {
237 this.tc.removeTuple(n, tn);
238 }
239
240 // source -> n
241 set = tc.getTupleStarts(n);
242
243 modSet = new HashSet<V>(set);
244
245 for (V sn : modSet) {
246 this.tc.removeTuple(sn, n);
247 }
248 }
249
250 public DRedTcRelation<V> getTcRelation() {
251 return this.tc;
252 }
253
254 public void setTcRelation(DRedTcRelation<V> tc) {
255 this.tc = tc;
256 }
257
258 @Override
259 public boolean isReachable(V source, V target) {
260 return tc.containsTuple(source, target);
261 }
262
263 @Override
264 public void attachObserver(ITcObserver<V> to) {
265 this.observers.add(to);
266 }
267
268 @Override
269 public void detachObserver(ITcObserver<V> to) {
270 this.observers.remove(to);
271 }
272
273 @Override
274 public Set<V> getAllReachableTargets(V source) {
275 return tc.getTupleEnds(source);
276 }
277
278 @Override
279 public Set<V> getAllReachableSources(V target) {
280 return tc.getTupleStarts(target);
281 }
282
283 protected void notifyTcObservers(Set<Tuple<V>> tuples, int dir) {
284 for (ITcObserver<V> o : observers) {
285 for (Tuple<V> t : tuples) {
286 if (!t.getSource().equals(t.getTarget())) {
287 if (dir == 1) {
288 o.tupleInserted(t.getSource(), t.getTarget());
289 }
290 if (dir == -1) {
291 o.tupleDeleted(t.getSource(), t.getTarget());
292 }
293 }
294 }
295 }
296 }
297
298 @Override
299 public void dispose() {
300 tc = null;
301 dtc = null;
302 }
303
304 @Override
305 public IGraphPathFinder<V> getPathFinder() {
306 return new DFSPathFinder<V>(graphDataSource, this);
307 }
308}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java
new file mode 100644
index 00000000..8543b79c
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java
@@ -0,0 +1,223 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.dred;
11
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.Map;
15import java.util.Map.Entry;
16import java.util.Set;
17
18import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation;
19
20public class DRedTcRelation<V> implements ITcRelation<V> {
21
22 // tc(a,b) means that b is transitively reachable from a
23 private Map<V, Set<V>> tuplesForward;
24
25 // data structure to efficiently get those nodes from which a given node is reachable
26 // symmetric to tuplesForward
27 private Map<V, Set<V>> tuplesBackward;
28
29 public DRedTcRelation() {
30 this.tuplesForward = new HashMap<V, Set<V>>();
31 this.tuplesBackward = new HashMap<V, Set<V>>();
32 }
33
34 public void clear() {
35 this.tuplesForward.clear();
36 this.tuplesBackward.clear();
37 }
38
39 public boolean isEmpty() {
40 return tuplesForward.isEmpty();
41 }
42
43 public void removeTuple(V source, V target) {
44
45 // removing tuple from 'forward' tc relation
46 Set<V> sSet = tuplesForward.get(source);
47 if (sSet != null) {
48 sSet.remove(target);
49 if (sSet.size() == 0)
50 tuplesForward.remove(source);
51 }
52
53 // removing tuple from 'backward' tc relation
54 Set<V> tSet = tuplesBackward.get(target);
55 if (tSet != null) {
56 tSet.remove(source);
57 if (tSet.size() == 0)
58 tuplesBackward.remove(target);
59 }
60 }
61
62 /**
63 * Returns true if the tc relation did not contain previously such a tuple that is defined by (source,target), false
64 * otherwise.
65 *
66 * @param source
67 * the source of the tuple
68 * @param target
69 * the target of the tuple
70 * @return true if the relation did not contain previously the tuple
71 */
72 public boolean addTuple(V source, V target) {
73
74 // symmetric modification, it is sufficient to check the return value in one collection
75 // adding tuple to 'forward' tc relation
76 Set<V> sSet = tuplesForward.get(source);
77 if (sSet == null) {
78 Set<V> newSet = new HashSet<V>();
79 newSet.add(target);
80 tuplesForward.put(source, newSet);
81 } else {
82 sSet.add(target);
83 }
84
85 // adding tuple to 'backward' tc relation
86 Set<V> tSet = tuplesBackward.get(target);
87 if (tSet == null) {
88 Set<V> newSet = new HashSet<V>();
89 newSet.add(source);
90 tuplesBackward.put(target, newSet);
91 return true;
92 } else {
93 boolean ret = tSet.add(source);
94 return ret;
95 }
96
97 }
98
99 /**
100 * Union operation of two tc realtions.
101 *
102 * @param rA
103 * the other tc relation
104 */
105 public void union(DRedTcRelation<V> rA) {
106 for (V source : rA.tuplesForward.keySet()) {
107 for (V target : rA.tuplesForward.get(source)) {
108 this.addTuple(source, target);
109 }
110 }
111 }
112
113 /**
114 * Computes the difference of this tc relation and the given rA parameter.
115 *
116 * @param rA
117 * the subtrahend relation
118 */
119 public void difference(DRedTcRelation<V> rA) {
120 for (V source : rA.tuplesForward.keySet()) {
121 for (V target : rA.tuplesForward.get(source)) {
122 this.removeTuple(source, target);
123 }
124 }
125 }
126
127 @Override
128 public Set<V> getTupleEnds(V source) {
129 Set<V> t = tuplesForward.get(source);
130 return (t == null) ? new HashSet<V>() : new HashSet<V>(t);
131 }
132
133 /**
134 * Returns the set of nodes from which the target node is reachable.
135 *
136 * @param target
137 * the target node
138 * @return the set of source nodes
139 */
140 public Set<V> getTupleStarts(V target) {
141 Set<V> t = tuplesBackward.get(target);
142 return (t == null) ? new HashSet<V>() : new HashSet<V>(t);
143 }
144
145 @Override
146 public Set<V> getTupleStarts() {
147 Set<V> t = tuplesForward.keySet();
148 return new HashSet<V>(t);
149 }
150
151 @Override
152 public String toString() {
153 StringBuilder sb = new StringBuilder("TcRelation = ");
154
155 for (Entry<V, Set<V>> entry : this.tuplesForward.entrySet()) {
156 V source = entry.getKey();
157 for (V target : entry.getValue()) {
158 sb.append("(" + source + "," + target + ") ");
159 }
160 }
161 return sb.toString();
162 }
163
164 /**
165 * Returns true if a (source, target) node is present in the transitive closure relation, false otherwise.
166 *
167 * @param source
168 * the source node
169 * @param target
170 * the target node
171 * @return true if tuple is present, false otherwise
172 */
173 public boolean containsTuple(V source, V target) {
174 if (tuplesForward.containsKey(source)) {
175 if (tuplesForward.get(source).contains(target))
176 return true;
177 }
178 return false;
179 }
180
181 @SuppressWarnings("unchecked")
182 @Override
183 public boolean equals(Object obj) {
184 if (this == obj) {
185 return true;
186 }
187 if ((obj == null) || (obj.getClass() != this.getClass())) {
188 return false;
189 }
190
191 DRedTcRelation<V> aTR = (DRedTcRelation<V>) obj;
192
193 for (Entry<V, Set<V>> entry : aTR.tuplesForward.entrySet()) {
194 V source = entry.getKey();
195 for (V target : entry.getValue()) {
196 if (!this.containsTuple(source, target))
197 return false;
198 }
199 }
200
201 for (Entry<V, Set<V>> entry : this.tuplesForward.entrySet()) {
202 V source = entry.getKey();
203 for (V target : entry.getValue()) {
204 if (!aTR.containsTuple(source, target))
205 return false;
206 }
207 }
208
209 return true;
210 }
211
212 @Override
213 public int hashCode() {
214 int hash = 7;
215 hash = 31 * hash + tuplesForward.hashCode();
216 hash = 31 * hash + tuplesBackward.hashCode();
217 return hash;
218 }
219
220 public Map<V, Set<V>> getTuplesForward() {
221 return tuplesForward;
222 }
223}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java
new file mode 100644
index 00000000..b369f1a1
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java
@@ -0,0 +1,110 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.fw;
11
12import java.util.HashMap;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation;
16import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource;
17import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper;
18import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
19import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver;
20import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
21
22public class FloydWarshallAlg<V> implements IGraphObserver<V> {
23
24 private DRedTcRelation<V> tc = null;
25 private IBiDirectionalGraphDataSource<V> gds = null;
26
27 public FloydWarshallAlg(IGraphDataSource<V> gds) {
28 if (gds instanceof IBiDirectionalGraphDataSource<?>) {
29 this.gds = (IBiDirectionalGraphDataSource<V>) gds;
30 } else {
31 this.gds = new IBiDirectionalWrapper<V>(gds);
32 }
33
34 this.tc = new DRedTcRelation<V>();
35 gds.attachObserver(this);
36 generateTc();
37 }
38
39 private void generateTc() {
40
41 tc.clear();
42
43 int n = gds.getAllNodes().size();
44 Map<V, Integer> mapForw = new HashMap<V, Integer>();
45 Map<Integer, V> mapBackw = new HashMap<Integer, V>();
46 int[][] P = new int[n][n];
47
48 int i, j, k;
49
50 // initialize adjacent matrix
51 for (i = 0; i < n; i++) {
52 for (j = 0; j < n; j++) {
53 P[i][j] = 0;
54 }
55 }
56
57 i = 0;
58 for (V node : gds.getAllNodes()) {
59 mapForw.put(node, i);
60 mapBackw.put(i, node);
61 i++;
62 }
63
64 for (V source : gds.getAllNodes()) {
65 IMemoryView<V> targets = gds.getTargetNodes(source);
66 for (V target : targets.distinctValues()) {
67 P[mapForw.get(source)][mapForw.get(target)] = 1;
68 }
69 }
70
71 for (k = 0; k < n; k++) {
72 for (i = 0; i < n; i++) {
73 for (j = 0; j < n; j++) {
74 P[i][j] = P[i][j] | (P[i][k] & P[k][j]);
75 }
76 }
77 }
78
79 for (i = 0; i < n; i++) {
80 for (j = 0; j < n; j++) {
81 if (P[i][j] == 1 && i != j)
82 tc.addTuple(mapBackw.get(i), mapBackw.get(j));
83 }
84 }
85 }
86
87 @Override
88 public void edgeInserted(V source, V target) {
89 generateTc();
90 }
91
92 @Override
93 public void edgeDeleted(V source, V target) {
94 generateTc();
95 }
96
97 @Override
98 public void nodeInserted(V n) {
99 generateTc();
100 }
101
102 @Override
103 public void nodeDeleted(V n) {
104 generateTc();
105 }
106
107 public DRedTcRelation<V> getTcRelation() {
108 return this.tc;
109 }
110}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java
new file mode 100644
index 00000000..fdf64f77
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java
@@ -0,0 +1,36 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.incscc;
10
11import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver;
12import tools.refinery.viatra.runtime.matchers.util.Direction;
13
14/**
15 * @author Tamas Szabo
16 *
17 */
18public class CountingListener<V> implements ITcObserver<V> {
19
20 private IncSCCAlg<V> alg;
21
22 public CountingListener(IncSCCAlg<V> alg) {
23 this.alg = alg;
24 }
25
26 @Override
27 public void tupleInserted(V source, V target) {
28 alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.INSERT);
29 }
30
31 @Override
32 public void tupleDeleted(V source, V target) {
33 alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.DELETE);
34 }
35
36} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java
new file mode 100644
index 00000000..f1e0ad44
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java
@@ -0,0 +1,645 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.incscc;
11
12import java.util.ArrayList;
13import java.util.HashSet;
14import java.util.Iterator;
15import java.util.List;
16import java.util.Map;
17import java.util.Map.Entry;
18import java.util.Objects;
19import java.util.Set;
20
21import tools.refinery.viatra.runtime.base.itc.alg.counting.CountingAlg;
22import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation;
23import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder;
24import tools.refinery.viatra.runtime.base.itc.alg.misc.GraphHelper;
25import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder;
26import tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple;
27import tools.refinery.viatra.runtime.base.itc.alg.misc.bfs.BFS;
28import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC;
29import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCCResult;
30import tools.refinery.viatra.runtime.base.itc.alg.util.CollectionHelper;
31import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph;
32import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource;
33import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper;
34import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
35import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver;
36import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource;
37import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver;
38import tools.refinery.viatra.runtime.matchers.algorithms.UnionFind;
39import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
40import tools.refinery.viatra.runtime.matchers.util.Direction;
41import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
42
43/**
44 * Incremental SCC maintenance + counting algorithm.
45 *
46 * @author Tamas Szabo
47 *
48 * @param <V>
49 * the type parameter of the nodes in the graph data source
50 */
51public class IncSCCAlg<V> implements IGraphObserver<V>, ITcDataSource<V> {
52
53 public UnionFind<V> sccs;
54 public IBiDirectionalGraphDataSource<V> gds;
55 private CountingAlg<V> counting;
56 private Graph<V> reducedGraph;
57 private IBiDirectionalGraphDataSource<V> reducedGraphIndexer;
58 private List<ITcObserver<V>> observers;
59 private CountingListener<V> countingListener;
60
61 public IncSCCAlg(IGraphDataSource<V> graphDataSource) {
62
63 if (graphDataSource instanceof IBiDirectionalGraphDataSource<?>) {
64 gds = (IBiDirectionalGraphDataSource<V>) graphDataSource;
65 } else {
66 gds = new IBiDirectionalWrapper<V>(graphDataSource);
67 }
68 observers = CollectionsFactory.createObserverList();
69 sccs = new UnionFind<V>();
70 reducedGraph = new Graph<V>();
71 reducedGraphIndexer = new IBiDirectionalWrapper<V>(reducedGraph);
72 countingListener = new CountingListener<V>(this);
73 initalizeInternalDataStructures();
74 gds.attachObserver(this);
75 }
76
77 private void initalizeInternalDataStructures() {
78 SCCResult<V> _sccres = SCC.computeSCC(gds);
79 Set<Set<V>> _sccs = _sccres.getSccs();
80
81 for (Set<V> _set : _sccs) {
82 sccs.makeSet(_set);
83 }
84
85 // Initalization of the reduced graph
86 for (V n : sccs.getPartitionHeads()) {
87 reducedGraph.insertNode(n);
88 }
89
90 for (V source : gds.getAllNodes()) {
91 final IMemoryView<V> targetNodes = gds.getTargetNodes(source);
92 for (Entry<V, Integer> entry : targetNodes.entriesWithMultiplicities()) {
93 for (int i = 0; i < entry.getValue(); i++) {
94 V target = entry.getKey();
95 V sourceRoot = sccs.find(source);
96 V targetRoot = sccs.find(target);
97
98 if (!sourceRoot.equals(targetRoot)) {
99 reducedGraph.insertEdge(sourceRoot, targetRoot);
100 }
101 }
102 }
103 }
104
105 counting = new CountingAlg<V>(reducedGraph);
106 }
107
108 @Override
109 public void edgeInserted(V source, V target) {
110 V sourceRoot = sccs.find(source);
111 V targetRoot = sccs.find(target);
112
113 // Different SCC
114 if (!sourceRoot.equals(targetRoot)) {
115
116 // source is reachable from target?
117 if (counting.isReachable(targetRoot, sourceRoot)) {
118
119 Set<V> predecessorRoots = counting.getAllReachableSources(sourceRoot);
120 Set<V> successorRoots = counting.getAllReachableTargets(targetRoot);
121
122 // 1. intersection of source and target roots, these will be in the merged SCC
123 Set<V> isectRoots = CollectionHelper.intersection(predecessorRoots, successorRoots);
124 isectRoots.add(sourceRoot);
125 isectRoots.add(targetRoot);
126
127 // notifications must be issued before Union-Find modifications
128 if (observers.size() > 0) {
129 Set<V> sourceSCCs = createSetNullTolerant(predecessorRoots);
130 sourceSCCs.add(sourceRoot);
131 Set<V> targetSCCs = createSetNullTolerant(successorRoots);
132 targetSCCs.add(targetRoot);
133
134 // tracing back to actual nodes
135 for (V sourceSCC : sourceSCCs) {
136 targetLoop: for (V targetSCC : targetSCCs) {
137 if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop;
138
139 boolean needsNotification =
140 // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc.
141 // Issue notifications only if there is no self-loop present at the moment
142 (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper
143 .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0)
144 ||
145 // Case 2. sourceSCC and targetSCC are different sccs.
146 (!sourceSCC.equals(targetSCC));
147 // if self loop is already present omit the notification
148 if (needsNotification) {
149 notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC),
150 Direction.INSERT);
151 }
152 }
153 }
154 }
155
156 // 2. delete edges, nodes
157 List<V> sourceSCCs = new ArrayList<V>();
158 List<V> targetSCCs = new ArrayList<V>();
159
160 for (V r : isectRoots) {
161 List<V> sourceSCCsOfSCC = getSourceSCCsOfSCC(r);
162 List<V> targetSCCsOfSCC = getTargetSCCsOfSCC(r);
163
164 for (V sourceSCC : sourceSCCsOfSCC) {
165 if (!sourceSCC.equals(r)) {
166 reducedGraph.deleteEdgeIfExists(sourceSCC, r);
167 }
168 }
169
170 for (V targetSCC : targetSCCsOfSCC) {
171 if (!isectRoots.contains(targetSCC) && !r.equals(targetSCC)) {
172 reducedGraph.deleteEdgeIfExists(r, targetSCC);
173 }
174 }
175
176 sourceSCCs.addAll(sourceSCCsOfSCC);
177 targetSCCs.addAll(targetSCCsOfSCC);
178 }
179
180 for (V r : isectRoots) {
181 reducedGraph.deleteNode(r);
182 }
183
184 // 3. union
185 Iterator<V> iterator = isectRoots.iterator();
186 V newRoot = iterator.next();
187 while (iterator.hasNext()) {
188 newRoot = sccs.union(newRoot, iterator.next());
189 }
190
191 // 4. add new node
192 reducedGraph.insertNode(newRoot);
193
194 // 5. add edges
195 Set<V> containedNodes = sccs.getPartition(newRoot);
196
197 for (V sourceSCC : sourceSCCs) {
198 if (!containedNodes.contains(sourceSCC) && !sourceSCC.equals(newRoot)) {
199 reducedGraph.insertEdge(sourceSCC, newRoot);
200 }
201 }
202 for (V targetSCC : targetSCCs) {
203 if (!containedNodes.contains(targetSCC) && !targetSCC.equals(newRoot)) {
204 reducedGraph.insertEdge(newRoot, targetSCC);
205 }
206 }
207 } else {
208 if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 1) {
209 counting.attachObserver(countingListener);
210 }
211 reducedGraph.insertEdge(sourceRoot, targetRoot);
212 counting.detachObserver(countingListener);
213 }
214 } else {
215 // Notifications about self-loops
216 if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1
217 && GraphHelper.getEdgeCount(source, target, gds) == 1) {
218 notifyTcObservers(source, source, Direction.INSERT);
219 }
220 }
221 }
222
223 @Override
224 public void edgeDeleted(V source, V target) {
225 V sourceRoot = sccs.find(source);
226 V targetRoot = sccs.find(target);
227
228 if (!sourceRoot.equals(targetRoot)) {
229 if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 0) {
230 counting.attachObserver(countingListener);
231 }
232 reducedGraph.deleteEdgeIfExists(sourceRoot, targetRoot);
233 counting.detachObserver(countingListener);
234 } else {
235 // get the graph for the scc whose root is sourceRoot
236 Graph<V> g = GraphHelper.getSubGraph(sccs.getPartition(sourceRoot), gds);
237
238 // if source is not reachable from target anymore
239 if (!BFS.isReachable(source, target, g)) {
240 // create copies of the current state before destructive manipulation
241 Map<V, Integer> reachableSources = CollectionsFactory.createMap();
242 for (Entry<V, Integer> entry : reducedGraphIndexer.getSourceNodes(sourceRoot).entriesWithMultiplicities()) {
243 reachableSources.put(entry.getKey(), entry.getValue());
244 }
245 Map<V, Integer> reachableTargets = CollectionsFactory.createMap();
246 for (Entry<V, Integer> entry : reducedGraphIndexer.getTargetNodes(sourceRoot).entriesWithMultiplicities()) {
247 reachableTargets.put(entry.getKey(), entry.getValue());
248 }
249
250 SCCResult<V> _newSccs = SCC.computeSCC(g);
251
252 // delete scc node (and with its edges too)
253 for (Entry<V, Integer> entry : reachableSources.entrySet()) {
254 V s = entry.getKey();
255 for (int i = 0; i < entry.getValue(); i++) {
256 reducedGraph.deleteEdgeIfExists(s, sourceRoot);
257 }
258 }
259
260 for (Entry<V, Integer> entry : reachableTargets.entrySet()) {
261 V t = entry.getKey();
262 for (int i = 0; i < entry.getValue(); i++) {
263 reducedGraph.deleteEdgeIfExists(sourceRoot, t);
264 }
265 }
266
267 sccs.deleteSet(sourceRoot);
268 reducedGraph.deleteNode(sourceRoot);
269
270 Set<Set<V>> newSCCs = _newSccs.getSccs();
271 Set<V> newSCCRoots = CollectionsFactory.createSet();
272
273 // add new nodes and edges to the reduced graph
274 for (Set<V> newSCC : newSCCs) {
275 V newRoot = sccs.makeSet(newSCC);
276 reducedGraph.insertNode(newRoot);
277 newSCCRoots.add(newRoot);
278 }
279 for (V newSCCRoot : newSCCRoots) {
280 List<V> sourceSCCsOfSCC = getSourceSCCsOfSCC(newSCCRoot);
281 List<V> targetSCCsOfSCC = getTargetSCCsOfSCC(newSCCRoot);
282
283 for (V sourceSCC : sourceSCCsOfSCC) {
284 if (!sourceSCC.equals(newSCCRoot)) {
285 reducedGraph.insertEdge(sccs.find(sourceSCC), newSCCRoot);
286 }
287 }
288 for (V targetSCC : targetSCCsOfSCC) {
289 if (!newSCCRoots.contains(targetSCC) && !targetSCC.equals(newSCCRoot))
290 reducedGraph.insertEdge(newSCCRoot, targetSCC);
291 }
292 }
293
294 // Must be after the union-find modifications
295 if (observers.size() > 0) {
296 V newSourceRoot = sccs.find(source);
297 V newTargetRoot = sccs.find(target);
298
299 Set<V> sourceSCCs = createSetNullTolerant(counting.getAllReachableSources(newSourceRoot));
300 sourceSCCs.add(newSourceRoot);
301
302 Set<V> targetSCCs = createSetNullTolerant(counting.getAllReachableTargets(newTargetRoot));
303 targetSCCs.add(newTargetRoot);
304
305 for (V sourceSCC : sourceSCCs) {
306 targetLoop: for (V targetSCC : targetSCCs) {
307 if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop;
308
309 boolean needsNotification =
310 // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc.
311 // Issue notifications only if there is no self-loop present at the moment
312 (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper
313 .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0)
314 ||
315 // Case 2. sourceSCC and targetSCC are different sccs.
316 (!sourceSCC.equals(targetSCC));
317 // if self loop is already present omit the notification
318 if (needsNotification) {
319 notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC),
320 Direction.DELETE);
321 }
322 }
323 }
324 }
325 } else {
326 // only handle self-loop notifications - sourceRoot equals to targetRoot
327 if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1
328 && GraphHelper.getEdgeCount(source, target, gds) == 0) {
329 notifyTcObservers(source, source, Direction.DELETE);
330 }
331 }
332 }
333 }
334
335 @Override
336 public void nodeInserted(V n) {
337 sccs.makeSet(n);
338 reducedGraph.insertNode(n);
339 }
340
341 @Override
342 public void nodeDeleted(V n) {
343 IMemoryView<V> sources = gds.getSourceNodes(n);
344 IMemoryView<V> targets = gds.getTargetNodes(n);
345
346 for (Entry<V, Integer> entry : sources.entriesWithMultiplicities()) {
347 for (int i = 0; i < entry.getValue(); i++) {
348 V source = entry.getKey();
349 edgeDeleted(source, n);
350 }
351 }
352
353 for (Entry<V, Integer> entry : targets.entriesWithMultiplicities()) {
354 for (int i = 0; i < entry.getValue(); i++) {
355 V target = entry.getKey();
356 edgeDeleted(n, target);
357 }
358 }
359
360 sccs.deleteSet(n);
361 }
362
363 @Override
364 public void attachObserver(ITcObserver<V> to) {
365 observers.add(to);
366 }
367
368 @Override
369 public void detachObserver(ITcObserver<V> to) {
370 observers.remove(to);
371 }
372
373 @Override
374 public Set<V> getAllReachableTargets(V source) {
375 V sourceRoot = sccs.find(source);
376 Set<V> containedNodes = sccs.getPartition(sourceRoot);
377 Set<V> targets = CollectionsFactory.createSet();
378
379 if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(source, gds) == 1) {
380 targets.addAll(containedNodes);
381 }
382
383 Set<V> rootSet = counting.getAllReachableTargets(sourceRoot);
384 if (rootSet != null) {
385 for (V _root : rootSet) {
386 targets.addAll(sccs.getPartition(_root));
387 }
388 }
389
390 return targets;
391 }
392
393 @Override
394 public Set<V> getAllReachableSources(V target) {
395 V targetRoot = sccs.find(target);
396 Set<V> containedNodes = sccs.getPartition(targetRoot);
397 Set<V> sources = CollectionsFactory.createSet();
398
399 if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(target, gds) == 1) {
400 sources.addAll(containedNodes);
401 }
402
403 Set<V> rootSet = counting.getAllReachableSources(targetRoot);
404 if (rootSet != null) {
405 for (V _root : rootSet) {
406 sources.addAll(sccs.getPartition(_root));
407 }
408 }
409 return sources;
410 }
411
412 @Override
413 public boolean isReachable(V source, V target) {
414 V sourceRoot = sccs.find(source);
415 V targetRoot = sccs.find(target);
416
417 if (sourceRoot.equals(targetRoot))
418 return true;
419 else
420 return counting.isReachable(sourceRoot, targetRoot);
421 }
422
423 public List<V> getReachabilityPath(V source, V target) {
424 if (!isReachable(source, target)) {
425 return null;
426 } else {
427 Set<V> sccsInSubGraph = CollectionHelper.intersection(counting.getAllReachableTargets(source),
428 counting.getAllReachableSources(target));
429 sccsInSubGraph.add(sccs.find(source));
430 sccsInSubGraph.add(sccs.find(target));
431 Set<V> nodesInSubGraph = CollectionsFactory.createSet();
432
433 for (V sccRoot : sccsInSubGraph) {
434 nodesInSubGraph.addAll(sccs.getPartition(sccRoot));
435 }
436
437 return GraphHelper.constructPath(source, target, nodesInSubGraph, gds);
438 }
439 }
440
441 // for JUnit
442 public boolean checkTcRelation(DRedTcRelation<V> tc) {
443
444 for (V s : tc.getTupleStarts()) {
445 for (V t : tc.getTupleEnds(s)) {
446 if (!isReachable(s, t))
447 return false;
448 }
449 }
450
451 for (V root : counting.getTcRelation().getTupleStarts()) {
452 for (V end : counting.getTcRelation().getTupleEnds(root)) {
453 for (V s : sccs.getPartition(root)) {
454 for (V t : sccs.getPartition(end)) {
455 if (!tc.containsTuple(s, t))
456 return false;
457 }
458 }
459 }
460 }
461
462 return true;
463 }
464
465 /**
466 * Return the SCCs from which the SCC represented by the root node is reachable. Note that an SCC can be present
467 * multiple times in the returned list (multiple edges between the two SCCs).
468 *
469 * @param root
470 * @return the list of reachable target SCCs
471 */
472 private List<V> getSourceSCCsOfSCC(V root) {
473 List<V> sourceSCCs = new ArrayList<V>();
474
475 for (V containedNode : this.sccs.getPartition(root)) {
476 IMemoryView<V> sourceNodes = this.gds.getSourceNodes(containedNode);
477 for (V source : sourceNodes.distinctValues()) {
478 sourceSCCs.add(this.sccs.find(source));
479 }
480 }
481
482 return sourceSCCs;
483 }
484
485 /**
486 * Returns true if the SCC represented by the given root node has incoming edges in the reduced graph,
487 * false otherwise (if this SCC is a source in the reduced graph).
488 *
489 * @param root the root node of an SCC
490 * @return true if it has incoming edges, false otherwise
491 * @since 1.6
492 */
493 public boolean hasIncomingEdges(final V root) {
494 for (final V containedNode : this.sccs.getPartition(root)) {
495 final IMemoryView<V> sourceNodes = this.gds.getSourceNodes(containedNode);
496 for (final V source : sourceNodes.distinctValues()) {
497 final V otherRoot = this.sccs.find(source);
498 if (!Objects.equals(root, otherRoot)) {
499 return true;
500 }
501 }
502 }
503 return false;
504 }
505
506 /**
507 * Return the SCCs which are reachable from the SCC represented by the root node. Note that an SCC can be present
508 * multiple times in the returned list (multiple edges between the two SCCs).
509 *
510 * @param root
511 * @return the list of reachable target SCCs
512 */
513 private List<V> getTargetSCCsOfSCC(V root) {
514 List<V> targetSCCs = new ArrayList<V>();
515
516 for (V containedNode : this.sccs.getPartition(root)) {
517 IMemoryView<V> targetNodes = this.gds.getTargetNodes(containedNode);
518 for (V target : targetNodes.distinctValues()) {
519 targetSCCs.add(this.sccs.find(target));
520 }
521 }
522
523 return targetSCCs;
524 }
525
526 /**
527 * Returns true if the SCC represented by the given root node has outgoing edges in the reduced graph,
528 * false otherwise (if this SCC is a sink in the reduced graph).
529 *
530 * @param root the root node of an SCC
531 * @return true if it has outgoing edges, false otherwise
532 * @since 1.6
533 */
534 public boolean hasOutgoingEdges(V root) {
535 for (final V containedNode : this.sccs.getPartition(root)) {
536 final IMemoryView<V> targetNodes = this.gds.getTargetNodes(containedNode);
537 for (final V target : targetNodes.distinctValues()) {
538 final V otherRoot = this.sccs.find(target);
539 if (!Objects.equals(root, otherRoot)) {
540 return true;
541 }
542 }
543 }
544 return false;
545 }
546
547 @Override
548 public void dispose() {
549 gds.detachObserver(this);
550 counting.dispose();
551 }
552
553 /**
554 * Call this method to notify the observers of the transitive closure relation. The tuples used in the notification
555 * will be the Descartes product of the two sets given.
556 *
557 * @param sources
558 * the source nodes
559 * @param targets
560 * the target nodes
561 * @param direction
562 */
563 protected void notifyTcObservers(Set<V> sources, Set<V> targets, Direction direction) {
564 for (V s : sources) {
565 for (V t : targets) {
566 notifyTcObservers(s, t, direction);
567 }
568 }
569 }
570
571 private void notifyTcObservers(V source, V target, Direction direction) {
572 for (ITcObserver<V> observer : observers) {
573 if (direction == Direction.INSERT) {
574 observer.tupleInserted(source, target);
575 }
576 if (direction == Direction.DELETE) {
577 observer.tupleDeleted(source, target);
578 }
579 }
580 }
581
582 /**
583 * Returns the node that is selected as the representative of the SCC containing the argument.
584 * @since 1.6
585 */
586 public V getRepresentative(V node) {
587 return sccs.find(node);
588 }
589
590 public Set<Tuple<V>> getTcRelation() {
591 Set<Tuple<V>> resultSet = new HashSet<Tuple<V>>();
592
593 for (V sourceRoot : sccs.getPartitionHeads()) {
594 Set<V> sources = sccs.getPartition(sourceRoot);
595 if (sources.size() > 1 || GraphHelper.getEdgeCount(sources.iterator().next(), gds) == 1) {
596 for (V source : sources) {
597 for (V target : sources) {
598 resultSet.add(new Tuple<V>(source, target));
599 }
600 }
601 }
602
603 Set<V> reachableTargets = counting.getAllReachableTargets(sourceRoot);
604 if (reachableTargets != null) {
605 for (V targetRoot : reachableTargets) {
606 for (V source : sources) {
607 for (V target : sccs.getPartition(targetRoot)) {
608 resultSet.add(new Tuple<V>(source, target));
609 }
610 }
611 }
612 }
613 }
614
615 return resultSet;
616 }
617
618 public boolean isIsolated(V node) {
619 IMemoryView<V> targets = gds.getTargetNodes(node);
620 IMemoryView<V> sources = gds.getSourceNodes(node);
621 return targets.isEmpty() && sources.isEmpty();
622 }
623
624 @Override
625 public IGraphPathFinder<V> getPathFinder() {
626 return new DFSPathFinder<V>(gds, this);
627 }
628
629 /**
630 * The graph of SCCs; each SCC is represented by its representative node (see {@link #getRepresentative(Object)})
631 * @since 1.6
632 */
633 public Graph<V> getReducedGraph() {
634 return reducedGraph;
635 }
636
637 private static <V> Set<V> createSetNullTolerant(Set<V> initial) {
638 if (initial != null)
639 return CollectionsFactory.createSet(initial);
640 else
641 return CollectionsFactory.createSet();
642 }
643
644
645}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java
new file mode 100644
index 00000000..51017b1a
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java
@@ -0,0 +1,146 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Abel Hegedus and 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.base.itc.alg.misc;
10
11import java.util.ArrayList;
12import java.util.Deque;
13import java.util.HashSet;
14import java.util.Iterator;
15import java.util.LinkedList;
16import java.util.List;
17import java.util.Set;
18
19import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
20import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource;
21import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
22
23/**
24 * A depth-first search implementation of the {@link IGraphPathFinder}.
25 *
26 * TODO use ITC to filter nodes that must be traversed, instead of checks
27 *
28 * @author Abel Hegedus
29 *
30 * @param <V>
31 * the node type of the graph
32 */
33public class DFSPathFinder<V> implements IGraphPathFinder<V> {
34
35 private IGraphDataSource<V> graph;
36 private ITcDataSource<V> itc;
37
38 public DFSPathFinder(IGraphDataSource<V> graph, ITcDataSource<V> itc) {
39 this.graph = graph;
40 this.itc = itc;
41 }
42
43 @Override
44 public Iterable<Deque<V>> getAllPaths(V sourceNode, V targetNode) {
45 Set<V> endNodes = new HashSet<V>();
46 endNodes.add(targetNode);
47 return getAllPathsToTargets(sourceNode, endNodes);
48 }
49
50 @Override
51 public Iterable<Deque<V>> getAllPathsToTargets(V sourceNode, Set<V> targetNodes) {
52 List<Deque<V>> paths = new ArrayList<Deque<V>>();
53 Deque<V> visited = new LinkedList<V>();
54 Set<V> reachableTargets = new HashSet<V>();
55 for (V targetNode : targetNodes) {
56 if (itc.isReachable(sourceNode, targetNode)) {
57 reachableTargets.add(targetNode);
58 }
59 }
60 if (!reachableTargets.isEmpty()) {
61 return paths;
62 }
63 visited.add(sourceNode);
64 return getPaths(paths, visited, reachableTargets);
65 }
66
67 protected Iterable<Deque<V>> getPaths(List<Deque<V>> paths, Deque<V> visited, Set<V> targetNodes) {
68 IMemoryView<V> nodes = graph.getTargetNodes(visited.getLast());
69 // examine adjacent nodes
70 for (V node : nodes.distinctValues()) {
71 if (visited.contains(node)) {
72 continue;
73 }
74 if (targetNodes.contains(node)) {
75 visited.add(node);
76 // clone visited LinkedList
77 Deque<V> visitedClone = new LinkedList<V>(visited);
78 paths.add(visitedClone);
79 visited.removeLast();
80 break;
81 }
82 }
83
84 // in breadth-first, recursion needs to come after visiting connected nodes
85 for (V node : nodes.distinctValues()) {
86 if (visited.contains(node) || targetNodes.contains(node)) {
87 continue;
88 }
89 boolean canReachTarget = false;
90 for (V target : targetNodes) {
91 if (itc.isReachable(node, target)) {
92 canReachTarget = true;
93 break;
94 }
95 }
96 if (canReachTarget) {
97 visited.addLast(node);
98 getPaths(paths, visited, targetNodes);
99 visited.removeLast();
100 }
101 }
102
103 return paths;
104 }
105
106 public String printPaths(List<Deque<V>> paths) {
107 StringBuilder sb = new StringBuilder();
108 for (Deque<V> visited : paths) {
109 sb.append("Path: ");
110 for (V node : visited) {
111 sb.append(node);
112 sb.append(" --> ");
113 }
114 sb.append("\n");
115 }
116 return sb.toString();
117 }
118
119 @Override
120 public Deque<V> getPath(V sourceNode, V targetNode) {
121 // TODO optimize
122 Iterable<Deque<V>> allPaths = getAllPaths(sourceNode, targetNode);
123 Iterator<Deque<V>> pathIterator = allPaths.iterator();
124 return pathIterator.hasNext() ? pathIterator.next() : new LinkedList<V>();
125 }
126
127 @Override
128 public Iterable<Deque<V>> getShortestPaths(V sourceNode, V targetNode) {
129 // TODO optimize
130 Iterable<Deque<V>> allPaths = getAllPaths(sourceNode, targetNode);
131 List<Deque<V>> shortestPaths = new ArrayList<Deque<V>>();
132 int shortestPathLength = -1;
133 for (Deque<V> path : allPaths) {
134 int pathLength = path.size();
135 if (shortestPathLength == -1 || pathLength < shortestPathLength) {
136 shortestPaths.clear();
137 shortestPathLength = pathLength;
138 }
139 if (pathLength == shortestPathLength) {
140 shortestPaths.add(path);
141 }
142 }
143 return shortestPaths;
144 }
145
146}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java
new file mode 100644
index 00000000..cf68d36a
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java
@@ -0,0 +1,38 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.misc;
11
12public class Edge<V> {
13 private V source;
14 private V target;
15
16 public Edge(V source, V target) {
17 super();
18 this.source = source;
19 this.target = target;
20 }
21
22 public V getSource() {
23 return source;
24 }
25
26 public void setSource(V source) {
27 this.source = source;
28 }
29
30 public V getTarget() {
31 return target;
32 }
33
34 public void setTarget(V target) {
35 this.target = target;
36 }
37
38}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java
new file mode 100644
index 00000000..194e979b
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java
@@ -0,0 +1,173 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Tamas Szabo, 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.base.itc.alg.misc;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Map.Entry;
16import java.util.Set;
17
18import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph;
19import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource;
20import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
21import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
22
23/**
24 * Utility class for graph related operations.
25 *
26 * @author Tamas Szabo
27 */
28public class GraphHelper {
29
30 private GraphHelper() {/*Utility class constructor*/}
31
32 /**
33 * Returns the subgraph from the given {@link IBiDirectionalGraphDataSource} which contains the given set of nodes.
34 *
35 * @param nodesInSubGraph
36 * the nodes that are present in the subgraph
37 * @param graphDataSource
38 * the graph data source for the original graph
39 * @return the subgraph associated to the given nodes
40 */
41 public static <V> Graph<V> getSubGraph(Collection<V> nodesInSubGraph,
42 IBiDirectionalGraphDataSource<V> graphDataSource) {
43 Graph<V> g = new Graph<V>();
44 if (nodesInSubGraph != null) {
45 for (V node : nodesInSubGraph) {
46 g.insertNode(node);
47 }
48
49 for (V node : nodesInSubGraph) {
50 IMemoryView<V> sources = graphDataSource.getSourceNodes(node);
51 for (Entry<V, Integer> entry : sources.entriesWithMultiplicities()) {
52 for (int i = 0; i < entry.getValue(); i++) {
53 V s = entry.getKey();
54 if (nodesInSubGraph.contains(s)) {
55 g.insertEdge(s, node);
56 }
57 }
58 }
59 }
60 }
61
62 return g;
63 }
64
65 /**
66 * Constructs a path between source and target in the given graph. Both the {@link IGraphDataSource} and the set of
67 * nodes are used, this way it is possible to construct a path in a given subgraph.
68 *
69 * The returned {@link List} contains the nodes along the path (this means that there is an edge in the graph
70 * between two consecutive nodes). A self loop (one edge) is indicated with the source node being present two times
71 * in the returned {@link List}.
72 *
73 * @param source
74 * the source node
75 * @param target
76 * the target node
77 * @param nodesInGraph
78 * the nodes that are present in the subgraph
79 * @param graphDataSource
80 * the graph data source
81 * @return the path between the two nodes
82 */
83 public static <V> List<V> constructPath(V source, V target, Set<V> nodesInGraph,
84 IGraphDataSource<V> graphDataSource) {
85 Set<V> visitedNodes = new HashSet<V>();
86 List<V> path = new ArrayList<V>();
87
88 visitedNodes.add(source);
89 path.add(source);
90 V act = source;
91
92 // if source and target are the same node
93 if (source.equals(target) && graphDataSource.getTargetNodes(source).containsNonZero(target)) {
94 // the node will be present in the path two times
95 path.add(source);
96 return path;
97 } else {
98 while (act != null) {
99 V nextNode = getNextNodeToVisit(act, graphDataSource, nodesInGraph, visitedNodes);
100 if (nextNode == null && path.size() > 1) {
101 // needs to backtrack along path
102 // remove the last element in the path because we can't go
103 // anywhere from there
104 path.remove(path.size() - 1);
105 while (nextNode == null && path.size() > 0) {
106 V lastPathElement = path.get(path.size() - 1);
107 nextNode = getNextNodeToVisit(lastPathElement, graphDataSource, nodesInGraph, visitedNodes);
108 if (nextNode == null) {
109 path.remove(path.size() - 1);
110 }
111 }
112 }
113
114 if (nextNode != null) {
115 visitedNodes.add(nextNode);
116 path.add(nextNode);
117 if (nextNode.equals(target)) {
118 return path;
119 }
120 }
121 act = nextNode;
122 }
123 return null;
124 }
125 }
126
127 private static <V> V getNextNodeToVisit(V act, IGraphDataSource<V> graphDataSource, Set<V> nodesInSubGraph,
128 Set<V> visitedNodes) {
129 IMemoryView<V> targetNodes = graphDataSource.getTargetNodes(act);
130 for (Entry<V, Integer> entry : targetNodes.entriesWithMultiplicities()) {
131 for (int i = 0; i < entry.getValue(); i++) {
132 V node = entry.getKey();
133 if (nodesInSubGraph.contains(node) && !visitedNodes.contains(node)) {
134 return node;
135 }
136 }
137 }
138 return null;
139 }
140
141 /**
142 * Returns the number of self-loop edges for the given node.
143 *
144 * @param node
145 * the node
146 * @param graphDataSource
147 * the graph data source
148 * @return the number of self-loop edges
149 */
150 public static <V> int getEdgeCount(V node, IGraphDataSource<V> graphDataSource) {
151 return getEdgeCount(node, node, graphDataSource);
152 }
153
154 /**
155 * Returns the number of edges between the given source and target nodes.
156 *
157 * @param source
158 * the source node
159 * @param target
160 * the target node
161 * @param graphDataSource
162 * the graph data source
163 * @return the number of parallel edges between the two nodes
164 */
165 public static <V> int getEdgeCount(V source, V target, IGraphDataSource<V> graphDataSource) {
166 Integer count = graphDataSource.getTargetNodes(source).getCount(target);
167 if (count == null) {
168 return 0;
169 } else {
170 return count;
171 }
172 }
173}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java
new file mode 100644
index 00000000..cebb09c8
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java
@@ -0,0 +1,67 @@
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.base.itc.alg.misc;
10
11import java.util.Deque;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource;
15
16/**
17 * The path finder provides methods for retrieving paths in a graph between a source node and one or more target nodes.
18 * Use {@link ITcDataSource#getPathFinder()} for instantiating.
19 *
20 * @author Abel Hegedus
21 *
22 * @param <V> the node type of the graph
23 */
24public interface IGraphPathFinder<V> {
25
26 /**
27 * Returns an arbitrary path from the source node to the target node (if such exists). If there is no path
28 * between them, an empty collection is returned.
29 *
30 * @param sourceNode the source node of the path
31 * @param targetNode the target node of the path
32 * @return the path from the source to the target, or empty collection if target is not reachable from source.
33 */
34 Deque<V> getPath(V sourceNode, V targetNode);
35
36 /**
37 * Returns the collection of shortest paths from the source node to the target node (if such exists). If there is no path
38 * between them, an empty collection is returned.
39 *
40 * @param sourceNode the source node of the path
41 * @param targetNode the target node of the path
42 * @return the collection of shortest paths from the source to the target, or empty collection if target is not reachable from source.
43 */
44 Iterable<Deque<V>> getShortestPaths(V sourceNode, V targetNode);
45
46 /**
47 * Returns the collection of paths from the source node to the target node (if such exists). If there is no path
48 * between them, an empty collection is returned.
49 *
50 * @param sourceNode the source node of the path
51 * @param targetNode the target node of the path
52 * @return the collection of paths from the source to the target, or empty collection if target is not reachable from source.
53 */
54 Iterable<Deque<V>> getAllPaths(V sourceNode, V targetNode);
55
56 /**
57 * Returns the collection of paths from the source node to any of the target nodes (if such exists). If there is no path
58 * between them, an empty collection is returned.
59 *
60 * @param sourceNode the source node of the path
61 * @param targetNodes the set of target nodes of the paths
62 * @return the collection of paths from the source to any of the targets, or empty collection if neither target is reachable from source.
63 */
64 Iterable<Deque<V>> getAllPathsToTargets(V sourceNode, Set<V> targetNodes);
65
66
67} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java
new file mode 100644
index 00000000..a41ff6c7
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java
@@ -0,0 +1,31 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.misc;
11
12import java.util.Set;
13
14public interface ITcRelation<V> {
15
16 /**
17 * Returns the starting nodes from a transitive closure relation.
18 *
19 * @return the set of starting nodes
20 */
21 public Set<V> getTupleStarts();
22
23 /**
24 * Returns the set of nodes that are reachable from the given node.
25 *
26 * @param start
27 * the starting node
28 * @return the set of reachable nodes
29 */
30 public Set<V> getTupleEnds(V start);
31}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java
new file mode 100644
index 00000000..a2d54a59
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java
@@ -0,0 +1,60 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.misc;
11
12public class Tuple<V> {
13
14 private V source;
15 private V target;
16
17 public Tuple(V source, V target) {
18 super();
19 this.source = source;
20 this.target = target;
21 }
22
23 public V getSource() {
24 return source;
25 }
26
27 public void setSource(V source) {
28 this.source = source;
29 }
30
31 public V getTarget() {
32 return target;
33 }
34
35 public void setTarget(V target) {
36 this.target = target;
37 }
38
39 @Override
40 public String toString() {
41 return "(" + source.toString() + "," + target.toString() + ")";
42 }
43
44 @Override
45 public boolean equals(Object o) {
46 if (o instanceof Tuple) {
47 Tuple<?> t = (Tuple<?>) o;
48
49 if (t.getSource().equals(this.source) && t.getTarget().equals(this.target)) {
50 return true;
51 }
52 }
53 return false;
54 }
55
56 @Override
57 public int hashCode() {
58 return source.hashCode() + target.hashCode();
59 }
60}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java
new file mode 100644
index 00000000..798f31d2
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java
@@ -0,0 +1,151 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.misc.bfs;
11
12import java.util.ArrayList;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Set;
16
17import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource;
18import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
19
20public class BFS<V> {
21
22 private BFS() {/*Utility class constructor*/}
23
24 /**
25 * Performs a breadth first search on the given graph to determine whether source is reachable from target.
26 *
27 * @param <V>
28 * the type parameter of the nodes in the graph
29 * @param source
30 * the source node
31 * @param target
32 * the target node
33 * @param graph
34 * the graph data source
35 * @return true if source is reachable from target, false otherwise
36 */
37 public static <V> boolean isReachable(V source, V target, IGraphDataSource<V> graph) {
38 List<V> nodeQueue = new ArrayList<V>();
39 Set<V> visited = new HashSet<V>();
40
41 nodeQueue.add(source);
42 visited.add(source);
43
44 boolean ret = _isReachable(target, graph, nodeQueue, visited);
45 return ret;
46 }
47
48 private static <V> boolean _isReachable(V target, IGraphDataSource<V> graph, List<V> nodeQueue, Set<V> visited) {
49
50 while (!nodeQueue.isEmpty()) {
51 V node = nodeQueue.remove(0);
52 for (V t : graph.getTargetNodes(node).distinctValues()){
53 if (t.equals(target)) {
54 return true;
55 }
56 if (!visited.contains(t)) {
57 visited.add(t);
58 nodeQueue.add(t);
59 }
60 }
61 }
62 return false;
63 }
64
65 public static <V> Set<V> reachableSources(IBiDirectionalGraphDataSource<V> graph, V target) {
66 Set<V> retSet = new HashSet<V>();
67 retSet.add(target);
68 List<V> nodeQueue = new ArrayList<V>();
69 nodeQueue.add(target);
70
71 _reachableSources(graph, nodeQueue, retSet);
72
73 return retSet;
74 }
75
76 private static <V> void _reachableSources(IBiDirectionalGraphDataSource<V> graph, List<V> nodeQueue,
77 Set<V> retSet) {
78 while (!nodeQueue.isEmpty()) {
79 V node = nodeQueue.remove(0);
80 for (V _node : graph.getSourceNodes(node).distinctValues()) {
81 if (!retSet.contains(_node)) {
82 retSet.add(_node);
83 nodeQueue.add(_node);
84 }
85 }
86 }
87 }
88
89 public static <V> Set<V> reachableTargets(IGraphDataSource<V> graph, V source) {
90 Set<V> retSet = new HashSet<V>();
91 retSet.add(source);
92 List<V> nodeQueue = new ArrayList<V>();
93 nodeQueue.add(source);
94
95 _reachableTargets(graph, nodeQueue, retSet);
96
97 return retSet;
98 }
99
100 private static <V> void _reachableTargets(IGraphDataSource<V> graph, List<V> nodeQueue, Set<V> retSet) {
101 while (!nodeQueue.isEmpty()) {
102 V node = nodeQueue.remove(0);
103
104 for (V _node : graph.getTargetNodes(node).distinctValues()) {
105
106 if (!retSet.contains(_node)) {
107 retSet.add(_node);
108 nodeQueue.add(_node);
109 }
110 }
111 }
112 }
113
114 /**
115 * Performs a breadth first search on the given graph and collects all the nodes along the path from source to
116 * target if such path exists.
117 *
118 * @param <V>
119 * the type parameter of the nodes in the graph
120 * @param source
121 * the source node
122 * @param target
123 * the target node
124 * @param graph
125 * the graph data source
126 * @return the set of nodes along the path
127 */
128 public static <V> Set<V> collectNodesAlongPath(V source, V target, IGraphDataSource<V> graph) {
129 Set<V> path = new HashSet<V>();
130 _collectNodesAlongPath(source, target, graph, path);
131 return path;
132 }
133
134 private static <V> boolean _collectNodesAlongPath(V node, V target, IGraphDataSource<V> graph, Set<V> path) {
135
136 boolean res = false;
137
138 // end recursion
139 if (node.equals(target)) {
140 path.add(node);
141 return true;
142 } else {
143 for (V _nodeT : graph.getTargetNodes(node).distinctValues()) {
144 res = (_collectNodesAlongPath(_nodeT, target, graph, path)) || res;
145 }
146 if (res)
147 path.add(node);
148 return res;
149 }
150 }
151}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java
new file mode 100644
index 00000000..c8d25c4e
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java
@@ -0,0 +1,93 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.misc.dfs;
11
12import java.util.HashMap;
13
14import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation;
15import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
16import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver;
17
18public class DFSAlg<V> implements IGraphObserver<V> {
19
20 private IGraphDataSource<V> gds;
21 private DRedTcRelation<V> tc;
22 private int[] visited;
23 private HashMap<V, Integer> nodeMap;
24
25 public DFSAlg(IGraphDataSource<V> gds) {
26 this.gds = gds;
27 this.tc = new DRedTcRelation<V>();
28 gds.attachObserver(this);
29 deriveTc();
30 }
31
32 private void deriveTc() {
33 tc.clear();
34
35 this.visited = new int[gds.getAllNodes().size()];
36 nodeMap = new HashMap<V, Integer>();
37
38 int j = 0;
39 for (V n : gds.getAllNodes()) {
40 nodeMap.put(n, j);
41 j++;
42 }
43
44 for (V n : gds.getAllNodes()) {
45 oneDFS(n, n);
46 initVisitedArray();
47 }
48 }
49
50 private void initVisitedArray() {
51 for (int i = 0; i < visited.length; i++)
52 visited[i] = 0;
53 }
54
55 private void oneDFS(V act, V source) {
56
57 if (!act.equals(source)) {
58 tc.addTuple(source, act);
59 }
60
61 visited[nodeMap.get(act)] = 1;
62
63 for (V t : gds.getTargetNodes(act).distinctValues()) {
64 if (visited[nodeMap.get(t)] == 0) {
65 oneDFS(t, source);
66 }
67 }
68 }
69
70 public DRedTcRelation<V> getTcRelation() {
71 return this.tc;
72 }
73
74 @Override
75 public void edgeInserted(V source, V target) {
76 deriveTc();
77 }
78
79 @Override
80 public void edgeDeleted(V source, V target) {
81 deriveTc();
82 }
83
84 @Override
85 public void nodeInserted(V n) {
86 deriveTc();
87 }
88
89 @Override
90 public void nodeDeleted(V n) {
91 deriveTc();
92 }
93}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java
new file mode 100644
index 00000000..c99a48ab
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java
@@ -0,0 +1,179 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.misc.scc;
11
12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.HashMap;
15import java.util.List;
16import java.util.Map;
17
18import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource;
19import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper;
20import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
21import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver;
22
23public class PKAlg<V> implements IGraphObserver<V> {
24
25 /**
26 * Maps the nodes to their indicies.
27 */
28 private Map<V, Integer> node2index;
29 private Map<Integer, V> index2node;
30 private Map<V, Boolean> node2mark;
31
32 /**
33 * Maps the index of a node to the index in the topsort.
34 */
35 private Map<Integer, Integer> index2topsort;
36 private Map<Integer, Integer> topsort2index;
37
38 /**
39 * Index associated to the inserted nodes (incrementing with every insertion).
40 */
41 private int index;
42
43 /**
44 * Index within the topsort for the target node when edge insertion occurs.
45 */
46 private int lower_bound;
47
48 /**
49 * Index within the topsort for the source node when edge insertion occurs.
50 */
51 private int upper_bound;
52
53 private List<V> RF;
54 private List<V> RB;
55 private IBiDirectionalGraphDataSource<V> gds;
56
57 public PKAlg(IGraphDataSource<V> gds) {
58 if (gds instanceof IBiDirectionalGraphDataSource<?>) {
59 this.gds = (IBiDirectionalGraphDataSource<V>) gds;
60 } else {
61 this.gds = new IBiDirectionalWrapper<V>(gds);
62 }
63
64 node2mark = new HashMap<V, Boolean>();
65 node2index = new HashMap<V, Integer>();
66 index2node = new HashMap<Integer, V>();
67 index2topsort = new HashMap<Integer, Integer>();
68 topsort2index = new HashMap<Integer, Integer>();
69 index = 0;
70
71 gds.attachObserver(this);
72 }
73
74 @Override
75 public void edgeInserted(V source, V target) {
76
77 RF = new ArrayList<V>();
78 RB = new ArrayList<V>();
79
80 lower_bound = index2topsort.get(node2index.get(target));
81 upper_bound = index2topsort.get(node2index.get(source));
82
83 if (lower_bound < upper_bound) {
84 dfsForward(target);
85 dfsBackward(source);
86 reorder();
87 }
88 }
89
90 private List<Integer> getIndicies(List<V> list) {
91 List<Integer> indicies = new ArrayList<Integer>();
92
93 for (V n : list)
94 indicies.add(index2topsort.get(node2index.get(n)));
95
96 return indicies;
97 }
98
99 private void reorder() {
100
101 Collections.reverse(RB);
102
103 // azon csomopontok indexei amelyek sorrendje nem jo
104 List<Integer> L = getIndicies(RF);
105 L.addAll(getIndicies(RB));
106 Collections.sort(L);
107
108 for (int i = 0; i < RB.size(); i++) {
109 index2topsort.put(node2index.get(RB.get(i)), L.get(i));
110 topsort2index.put(L.get(i), node2index.get(RB.get(i)));
111 }
112
113 for (int i = 0; i < RF.size(); i++) {
114 index2topsort.put(node2index.get(RF.get(i)), L.get(i + RB.size()));
115 topsort2index.put(L.get(i + RB.size()), node2index.get(RF.get(i)));
116 }
117 }
118
119 @SuppressWarnings("unused")
120 private List<V> getTopSort() {
121 List<V> topsort = new ArrayList<V>();
122
123 for (int i : topsort2index.values()) {
124 topsort.add(index2node.get(i));
125 }
126
127 return topsort;
128 }
129
130 private void dfsBackward(V node) {
131 node2mark.put(node, true);
132 RB.add(node);
133
134 for (V sn : gds.getSourceNodes(node).distinctValues()) {
135 int top_id = index2topsort.get(node2index.get(sn));
136
137 if (!node2mark.get(sn) && lower_bound < top_id)
138 dfsBackward(sn);
139 }
140 }
141
142 private void dfsForward(V node) {
143 node2mark.put(node, true);
144 RF.add(node);
145
146 for (V tn : gds.getTargetNodes(node).distinctValues()) {
147 int top_id = index2topsort.get(node2index.get(tn));
148
149 if (top_id == upper_bound)
150 System.out.println("!!!Cycle detected!!!");
151 else if (!node2mark.get(tn) && top_id < upper_bound)
152 dfsForward(tn);
153 }
154 }
155
156 @Override
157 public void edgeDeleted(V source, V target) {
158 // Edge deletion does not affect topsort
159 }
160
161 @Override
162 public void nodeInserted(V n) {
163 node2mark.put(n, false);
164 node2index.put(n, index);
165 index2node.put(index, n);
166 index2topsort.put(index, index);
167 topsort2index.put(index, index);
168 index++;
169 }
170
171 @Override
172 public void nodeDeleted(V n) {
173 node2mark.remove(n);
174 int node_id = node2index.remove(n);
175 index2node.remove(node_id);
176 int top_id = index2topsort.remove(node_id);
177 topsort2index.remove(top_id);
178 }
179}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java
new file mode 100644
index 00000000..8915998b
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java
@@ -0,0 +1,146 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.misc.scc;
11
12import java.util.HashSet;
13import java.util.Map;
14import java.util.Set;
15import java.util.Stack;
16
17import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19
20/**
21 * Efficient algorithms to compute the Strongly Connected Components in a directed graph.
22 *
23 * @author Tamas Szabo
24 *
25 * @param <V>
26 * the type parameter of the nodes in the graph
27 */
28public class SCC<V> {
29
30 private SCC() {/*Utility class constructor*/}
31
32 public static long sccId = 0;
33
34 /**
35 * Computes the SCCs for the given graph and returns them as a multiset. (Iterative version of Tarjan's algorithm)
36 *
37 * @param g
38 * the directed graph data source
39 * @return the set of SCCs
40 */
41 public static <V> SCCResult<V> computeSCC(IGraphDataSource<V> g) {
42 int index = 0;
43 Set<Set<V>> ret = new HashSet<Set<V>>();
44
45 // stores the lowlink and index information for the given node
46 Map<V, SCCProperty> nodeMap = CollectionsFactory.createMap();
47
48 // stores all target nodes of a given node - the list will be modified
49 Map<V, Set<V>> targetNodeMap = CollectionsFactory.createMap();
50
51 // stores those target nodes for a given node which have not been visited
52 Map<V, Set<V>> notVisitedMap = CollectionsFactory.createMap();
53
54 // stores the nodes during the traversal
55 Stack<V> nodeStack = new Stack<V>();
56
57 // stores the nodes which belong to an scc (there can be many sccs in the stack at the same time)
58 Stack<V> sccStack = new Stack<V>();
59
60 boolean sink = false, finishedTraversal = true;
61
62 // initialize all nodes with 0 index and 0 lowlink
63 Set<V> allNodes = g.getAllNodes();
64 for (V n : allNodes) {
65 nodeMap.put(n, new SCCProperty(0, 0));
66 }
67
68 for (V n : allNodes) {
69 // if the node has not been visited yet
70 if (nodeMap.get(n).getIndex() == 0) {
71 nodeStack.push(n);
72
73 while (!nodeStack.isEmpty()) {
74 V currentNode = nodeStack.peek();
75 sink = false;
76 finishedTraversal = false;
77 SCCProperty prop = nodeMap.get(currentNode);
78
79 if (nodeMap.get(currentNode).getIndex() == 0) {
80 index++;
81 sccStack.push(currentNode);
82 prop.setIndex(index);
83 prop.setLowlink(index);
84
85 notVisitedMap.put(currentNode, new HashSet<V>());
86
87 // storing the target nodes of the actual node
88 if (g.getTargetNodes(currentNode) != null) {
89 Set<V> targets = g.getTargetNodes(currentNode).distinctValues();
90 targetNodeMap.put(currentNode, CollectionsFactory.createSet(targets));
91 }
92 }
93
94 if (targetNodeMap.get(currentNode) != null) {
95
96 // remove node from stack, the exploration of its children has finished
97 if (targetNodeMap.get(currentNode).size() == 0) {
98 targetNodeMap.remove(currentNode);
99
100 nodeStack.pop();
101
102 for (V targetNode : g.getTargetNodes(currentNode).distinctValues()) {
103 if (notVisitedMap.get(currentNode).contains(targetNode)) {
104 prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getLowlink()));
105 } else if (sccStack.contains(targetNode)) {
106 prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getIndex()));
107 }
108 }
109
110 finishedTraversal = true;
111 } else {
112 V targetNode = targetNodeMap.get(currentNode).iterator().next();
113 targetNodeMap.get(currentNode).remove(targetNode);
114 // if the targetNode has not yet been visited push it to the stack
115 // and mark it in the notVisitedMap
116 if (nodeMap.get(targetNode).getIndex() == 0) {
117 notVisitedMap.get(currentNode).add(targetNode);
118 nodeStack.add(targetNode);
119 }
120 }
121 }
122 // if currentNode has no target nodes
123 else {
124 nodeStack.pop();
125 sink = true;
126 }
127
128 // create scc if node is a sink or an scc has been found
129 if ((sink || finishedTraversal) && (prop.getLowlink() == prop.getIndex())) {
130 Set<V> sc = new HashSet<V>();
131 V targetNode = null;
132
133 do {
134 targetNode = sccStack.pop();
135 sc.add(targetNode);
136 } while (!targetNode.equals(currentNode));
137
138 ret.add(sc);
139 }
140 }
141 }
142 }
143
144 return new SCCResult<V>(ret, g);
145 }
146}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java
new file mode 100644
index 00000000..b26e3d37
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java
@@ -0,0 +1,37 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.misc.scc;
11
12public class SCCProperty {
13 private int index;
14 private int lowlink;
15
16 public SCCProperty(int index, int lowlink) {
17 super();
18 this.index = index;
19 this.lowlink = lowlink;
20 }
21
22 public int getIndex() {
23 return index;
24 }
25
26 public void setIndex(int index) {
27 this.index = index;
28 }
29
30 public int getLowlink() {
31 return lowlink;
32 }
33
34 public void setLowlink(int lowlink) {
35 this.lowlink = lowlink;
36 }
37}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java
new file mode 100644
index 00000000..fde59d3b
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java
@@ -0,0 +1,81 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.misc.scc;
11
12import java.util.Map.Entry;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
16
17public class SCCResult<V> {
18
19 private Set<Set<V>> sccs;
20 private IGraphDataSource<V> gds;
21
22 public SCCResult(Set<Set<V>> sccs, IGraphDataSource<V> gds) {
23 this.sccs = sccs;
24 this.gds = gds;
25 }
26
27 public Set<Set<V>> getSccs() {
28 return sccs;
29 }
30
31 public int getSCCCount() {
32 return sccs.size();
33 }
34
35 public double getAverageNodeCount() {
36 double a = 0;
37
38 for (Set<V> s : sccs) {
39 a += s.size();
40 }
41
42 return a / sccs.size();
43 }
44
45 public double getAverageEdgeCount() {
46 long edgeSum = 0;
47
48 for (Set<V> scc : sccs) {
49 for (V source : scc) {
50 for (Entry<V, Integer> entry : gds.getTargetNodes(source).entriesWithMultiplicities()) {
51 if (scc.contains(entry.getKey())) {
52 edgeSum += entry.getValue();
53 }
54 }
55 }
56 }
57
58 return (double) edgeSum / (double) sccs.size();
59 }
60
61 public int getBiggestSCCSize() {
62 int max = 0;
63
64 for (Set<V> scc : sccs) {
65 if (scc.size() > max)
66 max = scc.size();
67 }
68
69 return max;
70 }
71
72 public long getSumOfSquares() {
73 long sum = 0;
74
75 for (Set<V> scc : sccs) {
76 sum += scc.size() * scc.size();
77 }
78
79 return sum;
80 }
81}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java
new file mode 100644
index 00000000..dd18e6c8
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java
@@ -0,0 +1,77 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.misc.topsort;
11
12import java.util.HashSet;
13import java.util.LinkedList;
14import java.util.List;
15import java.util.Set;
16import java.util.Stack;
17
18import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
19
20/**
21 * @since 1.6
22 */
23public class TopologicalSorting {
24
25 private TopologicalSorting() {/*Utility class constructor*/}
26
27 private static final class Pair<T> {
28 public T element;
29 public boolean isParent;
30
31 public Pair(final T element, final boolean isParent) {
32 this.element = element;
33 this.isParent = isParent;
34 }
35 }
36
37 /**
38 * Returns a topological ordering for the given graph data source.
39 * Output format: if there is an a -> b (transitive) reachability, then node <code>a</code> will come before node <code>b</code> in the resulting list.
40 *
41 * @param gds the graph data source
42 * @return a topological ordering
43 */
44 public static <T> List<T> compute(final IGraphDataSource<T> gds) {
45 final Set<T> visited = new HashSet<T>();
46 final LinkedList<T> result = new LinkedList<T>();
47 final Stack<Pair<T>> dfsStack = new Stack<Pair<T>>();
48
49 for (final T node : gds.getAllNodes()) {
50 if (!visited.contains(node)) {
51 dfsStack.push(new Pair<T>(node, false));
52 }
53
54 while (!dfsStack.isEmpty()) {
55 final Pair<T> head = dfsStack.pop();
56 final T source = head.element;
57
58 if (head.isParent) {
59 // we have already seen source, push it to the resulting stack
60 result.addFirst(source);
61 } else {
62 // first time we see source, continue with its children
63 visited.add(source);
64 dfsStack.push(new Pair<T>(source, true));
65
66 for (final T target : gds.getTargetNodes(source).distinctValues()) {
67 if (!visited.contains(target)) {
68 dfsStack.push(new Pair<T>(target, false));
69 }
70 }
71 }
72 }
73 }
74
75 return result;
76 }
77}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RepresentativeElectionAlgorithm.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java
index ff5c7158..51015404 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RepresentativeElectionAlgorithm.java
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java
@@ -3,19 +3,22 @@
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.rete.network; 6package tools.refinery.viatra.runtime.base.itc.alg.representative;
7 7
8import org.eclipse.viatra.query.runtime.base.itc.graphimpl.Graph; 8import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph;
9import org.eclipse.viatra.query.runtime.base.itc.igraph.IGraphObserver; 9import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver;
10import org.eclipse.viatra.query.runtime.matchers.util.Direction; 10import tools.refinery.viatra.runtime.matchers.util.Direction;
11 11
12import java.util.*; 12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.Map;
15import java.util.Set;
13 16
14public abstract class RepresentativeElectionAlgorithm implements IGraphObserver<Object> { 17public abstract class RepresentativeElectionAlgorithm implements IGraphObserver<Object> {
15 protected final Graph<Object> graph; 18 protected final Graph<Object> graph;
16 protected final Map<Object, Object> representatives = new HashMap<>(); 19 protected final Map<Object, Object> representatives = new HashMap<>();
17 protected final Map<Object, Set<Object>> components = new HashMap<>(); 20 protected final Map<Object, Set<Object>> components = new HashMap<>();
18 private RepresentativeElectionNode observer; 21 private RepresentativeObserver observer;
19 22
20 protected RepresentativeElectionAlgorithm(Graph<Object> graph) { 23 protected RepresentativeElectionAlgorithm(Graph<Object> graph) {
21 this.graph = graph; 24 this.graph = graph;
@@ -82,7 +85,7 @@ public abstract class RepresentativeElectionAlgorithm implements IGraphObserver<
82 } 85 }
83 } 86 }
84 87
85 public void setObserver(RepresentativeElectionNode observer) { 88 public void setObserver(RepresentativeObserver observer) {
86 this.observer = observer; 89 this.observer = observer;
87 } 90 }
88 91
@@ -131,7 +134,7 @@ public abstract class RepresentativeElectionAlgorithm implements IGraphObserver<
131 } 134 }
132 } 135 }
133 136
134 interface Factory { 137 public interface Factory {
135 RepresentativeElectionAlgorithm create(Graph<Object> graph); 138 RepresentativeElectionAlgorithm create(Graph<Object> graph);
136 } 139 }
137} 140}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java
new file mode 100644
index 00000000..93cce1ea
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java
@@ -0,0 +1,12 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.viatra.runtime.base.itc.alg.representative;
7
8import tools.refinery.viatra.runtime.matchers.util.Direction;
9
10public interface RepresentativeObserver {
11 void tupleChanged(Object node, Object representative, Direction direction);
12}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/StronglyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java
index 11155f3e..ba42bb13 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/StronglyConnectedComponentAlgorithm.java
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java
@@ -3,12 +3,12 @@
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.rete.network; 6package tools.refinery.viatra.runtime.base.itc.alg.representative;
7 7
8import org.eclipse.viatra.query.runtime.base.itc.alg.misc.GraphHelper; 8import tools.refinery.viatra.runtime.base.itc.alg.misc.GraphHelper;
9import org.eclipse.viatra.query.runtime.base.itc.alg.misc.bfs.BFS; 9import tools.refinery.viatra.runtime.base.itc.alg.misc.bfs.BFS;
10import org.eclipse.viatra.query.runtime.base.itc.alg.misc.scc.SCC; 10import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC;
11import org.eclipse.viatra.query.runtime.base.itc.graphimpl.Graph; 11import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph;
12 12
13import java.util.Collection; 13import java.util.Collection;
14import java.util.Set; 14import java.util.Set;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/WeaklyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java
index 118004dd..22159499 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/WeaklyConnectedComponentAlgorithm.java
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java
@@ -3,9 +3,9 @@
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.rete.network; 6package tools.refinery.viatra.runtime.base.itc.alg.representative;
7 7
8import org.eclipse.viatra.query.runtime.base.itc.graphimpl.Graph; 8import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph;
9 9
10import java.util.ArrayDeque; 10import java.util.ArrayDeque;
11import java.util.HashSet; 11import java.util.HashSet;
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java
new file mode 100644
index 00000000..c9b3cafa
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java
@@ -0,0 +1,64 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.util;
10
11import java.util.Set;
12
13import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
14
15/**
16 * @author Tamas Szabo
17 *
18 */
19public class CollectionHelper {
20
21 private CollectionHelper() {/*Utility class constructor*/}
22
23 /**
24 * Returns the intersection of two sets. It calls {@link Set#retainAll(java.util.Collection)} but returns a new set
25 * containing the elements of the intersection.
26 *
27 * @param set1
28 * the first set (can be null, interpreted as empty)
29 * @param set2
30 * the second set (can be null, interpreted as empty)
31 * @return the intersection of the sets
32 * @since 1.7
33 */
34 public static <V> Set<V> intersection(Set<V> set1, Set<V> set2) {
35 if (set1 == null || set2 == null)
36 return CollectionsFactory.createSet();
37
38 Set<V> intersection = CollectionsFactory.createSet(set1);
39 intersection.retainAll(set2);
40 return intersection;
41 }
42
43
44 /**
45 * Returns the difference of two sets (S1\S2). It calls {@link Set#removeAll(java.util.Collection)} but returns a
46 * new set containing the elements of the difference.
47 *
48 * @param set1
49 * the first set (can be null, interpreted as empty)
50 * @param set2
51 * the second set (can be null, interpreted as empty)
52 * @return the difference of the sets
53 * @since 1.7
54 */
55 public static <V> Set<V> difference(Set<V> set1, Set<V> set2) {
56 if (set1 == null)
57 return CollectionsFactory.createSet();
58
59 Set<V> difference = CollectionsFactory.createSet(set1);
60 if (set2 != null) difference.removeAll(set2);
61 return difference;
62 }
63
64}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java
new file mode 100644
index 00000000..0e21f323
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java
@@ -0,0 +1,160 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.base.itc.graphimpl;
10
11import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC;
12import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCCResult;
13import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
14
15import java.util.HashMap;
16import java.util.Map;
17import java.util.Set;
18import java.util.function.Function;
19
20/**
21 * This class contains utility methods to generate dot representations for {@link Graph} instances.
22 *
23 * @author Tamas Szabo
24 * @since 2.3
25 */
26public class DotGenerator {
27
28 private static final String[] colors = new String[] { "yellow", "blue", "red", "green", "gray", "cyan" };
29
30 private DotGenerator() {
31
32 }
33
34 /**
35 * Generates the dot representation for the given graph.
36 *
37 * @param graph
38 * the graph
39 * @param colorSCCs
40 * specifies if the strongly connected components with size greater than shall be colored
41 * @param nameFunction
42 * use this function to provide custom names to nodes, null if the default toString shall be used
43 * @param colorFunction
44 * use this function to provide custom color to nodes, null if the default white color shall be used
45 * @param edgeFunction
46 * use this function to provide custom edge labels, null if no edge label shall be printed
47 * @return the dot representation as a string
48 */
49 public static <V> String generateDot(final Graph<V> graph, final boolean colorSCCs,
50 final Function<V, String> nameFunction, final Function<V, String> colorFunction,
51 final Function<V, Function<V, String>> edgeFunction) {
52 final Map<V, String> colorMap = new HashMap<V, String>();
53
54 if (colorSCCs) {
55 final SCCResult<V> result = SCC.computeSCC(graph);
56 final Set<Set<V>> sccs = result.getSccs();
57
58 int i = 0;
59 for (final Set<V> scc : sccs) {
60 if (scc.size() > 1) {
61 for (final V node : scc) {
62 final String color = colorMap.get(node);
63 if (color == null) {
64 colorMap.put(node, colors[i % colors.length]);
65 } else {
66 colorMap.put(node, colorMap.get(node) + ":" + colors[i % colors.length]);
67 }
68 }
69 i++;
70 }
71 }
72
73 // if a node has no color yet, then make it white
74 for (final V node : graph.getAllNodes()) {
75 if (!colorMap.containsKey(node)) {
76 colorMap.put(node, "white");
77 }
78 }
79 } else {
80 for (final V node : graph.getAllNodes()) {
81 colorMap.put(node, "white");
82 }
83 }
84
85 if (colorFunction != null) {
86 for (final V node : graph.getAllNodes()) {
87 colorMap.put(node, colorFunction.apply(node));
88 }
89 }
90
91 final StringBuilder builder = new StringBuilder();
92 builder.append("digraph g {\n");
93
94 for (final V node : graph.getAllNodes()) {
95 final String nodePresentation = nameFunction == null ? node.toString() : nameFunction.apply(node);
96 builder.append("\"" + nodePresentation + "\"");
97 builder.append("[style=filled,fillcolor=" + colorMap.get(node) + "]");
98 builder.append(";\n");
99 }
100
101 for (final V source : graph.getAllNodes()) {
102 final IMemoryView<V> targets = graph.getTargetNodes(source);
103 if (!targets.isEmpty()) {
104 final String sourcePresentation = nameFunction == null ? source.toString() : nameFunction.apply(source);
105 for (final V target : targets.distinctValues()) {
106 String edgeLabel = null;
107 if (edgeFunction != null) {
108 final Function<V, String> v1 = edgeFunction.apply(source);
109 if (v1 != null) {
110 edgeLabel = v1.apply(target);
111 }
112 }
113
114 final String targetPresentation = nameFunction == null ? target.toString()
115 : nameFunction.apply(target);
116
117 builder.append("\"" + sourcePresentation + "\" -> \"" + targetPresentation + "\"");
118 if (edgeLabel != null) {
119 builder.append("[label=\"" + edgeLabel + "\"]");
120 }
121 builder.append(";\n");
122 }
123 }
124 }
125
126 builder.append("}");
127 return builder.toString();
128 }
129
130 /**
131 * Generates the dot representation for the given graph. No special pretty printing customization will be applied.
132 *
133 * @param graph
134 * the graph
135 * @return the dot representation as a string
136 */
137 public static <V> String generateDot(final Graph<V> graph) {
138 return generateDot(graph, false, null, null, null);
139 }
140
141 /**
142 * Returns a simple name shortener function that can be used in the graphviz visualization to help with readability.
143 * WARNING: if you shorten the name of the {@link Node}s too much, the visualization may become incorrect because
144 * grahpviz will treat different nodes as the same if their shortened names are the same.
145 *
146 * @param maxLength
147 * the maximum length of the text that is kept from the toString of the objects in the graph
148 * @return the shrunk toString value
149 */
150 public static <V> Function<V, String> getNameShortener(final int maxLength) {
151 return new Function<V, String>() {
152 @Override
153 public String apply(final V obj) {
154 final String value = obj.toString();
155 return value.substring(0, Math.min(value.length(), maxLength));
156 }
157 };
158 }
159
160}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java
new file mode 100644
index 00000000..70cbc77e
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java
@@ -0,0 +1,185 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.graphimpl;
11
12import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource;
13import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
14import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver;
15import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
16import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
17import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
18import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
19
20import java.util.List;
21import java.util.Map;
22import java.util.Map.Entry;
23import java.util.Set;
24
25public class Graph<V> implements IGraphDataSource<V>, IBiDirectionalGraphDataSource<V> {
26
27 // source -> target -> count
28 private IMultiLookup<V, V> outgoingEdges;
29 // target -> source -> count
30 private IMultiLookup<V, V> incomingEdges;
31
32 private Set<V> nodes;
33
34 private List<IGraphObserver<V>> observers;
35
36 public Graph() {
37 outgoingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class);
38 incomingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class);
39 nodes = CollectionsFactory.createSet();
40 observers = CollectionsFactory.createObserverList();
41 }
42
43 public void insertEdge(V source, V target) {
44 outgoingEdges.addPair(source, target);
45 incomingEdges.addPair(target, source);
46
47 for (IGraphObserver<V> go : observers) {
48 go.edgeInserted(source, target);
49 }
50 }
51
52 /**
53 * No-op if trying to delete edge that does not exist
54 *
55 * @since 2.0
56 * @see #deleteEdgeIfExists(Object, Object)
57 */
58 public void deleteEdgeIfExists(V source, V target) {
59 boolean containedEdge = outgoingEdges.lookupOrEmpty(source).containsNonZero(target);
60 if (containedEdge) {
61 deleteEdgeThatExists(source, target);
62 }
63 }
64
65 /**
66 * @throws IllegalStateException
67 * if trying to delete edge that does not exist
68 * @since 2.0
69 * @see #deleteEdgeIfExists(Object, Object)
70 */
71 public void deleteEdgeThatExists(V source, V target) {
72 outgoingEdges.removePair(source, target);
73 incomingEdges.removePair(target, source);
74 for (IGraphObserver<V> go : observers) {
75 go.edgeDeleted(source, target);
76 }
77 }
78
79 /**
80 * @deprecated use explicitly {@link #deleteEdgeThatExists(Object, Object)} or
81 * {@link #deleteEdgeIfExists(Object, Object)} instead. To preserve backwards compatibility, this method
82 * delegates to the latter.
83 *
84 */
85 @Deprecated
86 public void deleteEdge(V source, V target) {
87 deleteEdgeIfExists(source, target);
88 }
89
90 /**
91 * Insert the given node into the graph.
92 */
93 public void insertNode(V node) {
94 if (nodes.add(node)) {
95 for (IGraphObserver<V> go : observers) {
96 go.nodeInserted(node);
97 }
98 }
99 }
100
101 /**
102 * Deletes the given node AND all of the edges going in and out from the node.
103 */
104 public void deleteNode(V node) {
105 if (nodes.remove(node)) {
106 IMemoryView<V> incomingView = incomingEdges.lookup(node);
107 if (incomingView != null) {
108 Map<V, Integer> incoming = CollectionsFactory.createMap(incomingView.asMap());
109
110 for (Entry<V, Integer> entry : incoming.entrySet()) {
111 for (int i = 0; i < entry.getValue(); i++) {
112 deleteEdgeThatExists(entry.getKey(), node);
113 }
114 }
115 }
116
117 IMemoryView<V> outgoingView = outgoingEdges.lookup(node);
118 if (outgoingView != null) {
119 Map<V, Integer> outgoing = CollectionsFactory.createMap(outgoingView.asMap());
120
121 for (Entry<V, Integer> entry : outgoing.entrySet()) {
122 for (int i = 0; i < entry.getValue(); i++) {
123 deleteEdgeThatExists(node, entry.getKey());
124 }
125 }
126 }
127
128 for (IGraphObserver<V> go : observers) {
129 go.nodeDeleted(node);
130 }
131 }
132 }
133
134 @Override
135 public void attachObserver(IGraphObserver<V> go) {
136 observers.add(go);
137 }
138
139 @Override
140 public void attachAsFirstObserver(IGraphObserver<V> observer) {
141 observers.add(0, observer);
142 }
143
144 @Override
145 public void detachObserver(IGraphObserver<V> go) {
146 observers.remove(go);
147 }
148
149 @Override
150 public Set<V> getAllNodes() {
151 return nodes;
152 }
153
154 @Override
155 public IMemoryView<V> getTargetNodes(V source) {
156 return outgoingEdges.lookupOrEmpty(source);
157 }
158
159 @Override
160 public IMemoryView<V> getSourceNodes(V target) {
161 return incomingEdges.lookupOrEmpty(target);
162 }
163
164 @Override
165 public String toString() {
166 StringBuilder sb = new StringBuilder();
167 sb.append("nodes = ");
168 for (V n : getAllNodes()) {
169 sb.append(n.toString());
170 sb.append(" ");
171 }
172 sb.append(" edges = ");
173 for (V source : outgoingEdges.distinctKeys()) {
174 IMemoryView<V> targets = outgoingEdges.lookup(source);
175 for (V target : targets.distinctValues()) {
176 int count = targets.getCount(target);
177 for (int i = 0; i < count; i++) {
178 sb.append("(" + source + "," + target + ") ");
179 }
180 }
181 }
182 return sb.toString();
183 }
184
185}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java
new file mode 100644
index 00000000..64659447
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java
@@ -0,0 +1,37 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.igraph;
11
12import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
13import tools.refinery.viatra.runtime.matchers.util.IMultiset;
14
15/**
16 * A bi-directional graph data source supports all operations that an {@link IGraphDataSource} does, but it
17 * also makes it possible to query the incoming edges of nodes, not only the outgoing edges.
18 *
19 * @author Tamas Szabo
20 *
21 * @param <V> the type of the nodes in the graph
22 */
23public interface IBiDirectionalGraphDataSource<V> extends IGraphDataSource<V> {
24
25 /**
26 * Returns the source nodes for the given target node.
27 * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source.
28 *
29 * The method must not return null.
30 *
31 * @param target the target node
32 * @return the multiset of source nodes
33 * @since 2.0
34 */
35 public IMemoryView<V> getSourceNodes(V target);
36
37}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java
new file mode 100644
index 00000000..becab0eb
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java
@@ -0,0 +1,110 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.igraph;
11
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
15import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
16import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
17import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
18
19/**
20 * This class can be used to wrap an {@link IGraphDataSource} into an {@link IBiDirectionalGraphDataSource}. This class
21 * provides support for the retrieval of source nodes for a given target which is not supported by standard
22 * {@link IGraphDataSource} implementations.
23 *
24 * @author Tamas Szabo
25 *
26 * @param <V>
27 * the type parameter of the nodes in the graph data source
28 */
29public class IBiDirectionalWrapper<V> implements IBiDirectionalGraphDataSource<V>, IGraphObserver<V> {
30
31 private IGraphDataSource<V> wrappedDataSource;
32 // target -> source -> count
33 private IMultiLookup<V, V> incomingEdges;
34
35 public IBiDirectionalWrapper(IGraphDataSource<V> gds) {
36 this.wrappedDataSource = gds;
37
38 this.incomingEdges = CollectionsFactory.createMultiLookup(
39 Object.class, MemoryType.MULTISETS, Object.class);
40
41 if (gds.getAllNodes() != null) {
42 for (V source : gds.getAllNodes()) {
43 IMemoryView<V> targets = gds.getTargetNodes(source);
44 for (V target : targets.distinctValues()) {
45 int count = targets.getCount(target);
46 for (int i = 0; i < count; i++) {
47 edgeInserted(source, target);
48 }
49 }
50 }
51 }
52
53 gds.attachAsFirstObserver(this);
54 }
55
56 @Override
57 public void attachObserver(IGraphObserver<V> observer) {
58 wrappedDataSource.attachObserver(observer);
59 }
60
61 @Override
62 public void attachAsFirstObserver(IGraphObserver<V> observer) {
63 wrappedDataSource.attachAsFirstObserver(observer);
64 }
65
66 @Override
67 public void detachObserver(IGraphObserver<V> observer) {
68 wrappedDataSource.detachObserver(observer);
69 }
70
71 @Override
72 public Set<V> getAllNodes() {
73 return wrappedDataSource.getAllNodes();
74 }
75
76 @Override
77 public IMemoryView<V> getTargetNodes(V source) {
78 return wrappedDataSource.getTargetNodes(source);
79 }
80
81 @Override
82 public IMemoryView<V> getSourceNodes(V target) {
83 return incomingEdges.lookupOrEmpty(target);
84 }
85
86 @Override
87 public void edgeInserted(V source, V target) {
88 incomingEdges.addPair(target, source);
89 }
90
91 @Override
92 public void edgeDeleted(V source, V target) {
93 incomingEdges.removePair(target, source);
94 }
95
96 @Override
97 public void nodeInserted(V n) {
98
99 }
100
101 @Override
102 public void nodeDeleted(V node) {
103
104 }
105
106 @Override
107 public String toString() {
108 return wrappedDataSource.toString();
109 }
110}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java
new file mode 100644
index 00000000..3fa65936
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java
@@ -0,0 +1,70 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.igraph;
11
12import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
13import tools.refinery.viatra.runtime.matchers.util.IMultiset;
14
15import java.util.Set;
16
17/**
18 * The interface prescribes the set of operations that a graph data source must support.
19 * <p> Note that the old version of the interface is broken at version 1.6;
20 * MultiSets are now presented as Maps instead of Lists.
21 *
22 * @author Tamas Szabo
23 *
24 * @param <V>
25 * the type of the nodes in the graph
26 */
27public interface IGraphDataSource<V> {
28
29 /**
30 * Attaches a new graph observer to this graph data source. Observers will be notified in the order they have been registered.
31 *
32 * @param observer the graph observer
33 */
34 public void attachObserver(IGraphObserver<V> observer);
35
36 /**
37 * Attaches a new graph observer to this graph data source as the first one.
38 * In the notification order this observer will be the first one as long as another call to this method happens.
39 *
40 * @param observer the graph observer
41 * @since 1.6
42 */
43 public void attachAsFirstObserver(IGraphObserver<V> observer);
44
45 /**
46 * Detaches an already registered graph observer from this graph data source.
47 *
48 * @param observer the graph observer
49 */
50 public void detachObserver(IGraphObserver<V> observer);
51
52 /**
53 * Returns the complete set of nodes in the graph data source.
54 *
55 * @return the set of all nodes
56 */
57 public Set<V> getAllNodes();
58
59 /**
60 * Returns the target nodes for the given source node.
61 * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source.
62 *
63 * The method must not return null.
64 *
65 * @param source the source node
66 * @return the multiset of target nodes
67 * @since 2.0
68 */
69 public IMemoryView<V> getTargetNodes(V source);
70}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java
new file mode 100644
index 00000000..5cb2d9fa
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java
@@ -0,0 +1,55 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.igraph;
11
12/**
13 * Interface GraphObserver is used to observ the changes in a graph; edge and node insertion/deleteion.
14 *
15 * @author Tamas Szabo
16 *
17 */
18public interface IGraphObserver<V> {
19
20 /**
21 * Used to notify when an edge is inserted into the graph.
22 *
23 * @param source
24 * the source of the edge
25 * @param target
26 * the target of the edge
27 */
28 public void edgeInserted(V source, V target);
29
30 /**
31 * Used to notify when an edge is deleted from the graph.
32 *
33 * @param source
34 * the source of the edge
35 * @param target
36 * the target of the edge
37 */
38 public void edgeDeleted(V source, V target);
39
40 /**
41 * Used to notify when a node is inserted into the graph.
42 *
43 * @param n
44 * the node
45 */
46 public void nodeInserted(V n);
47
48 /**
49 * Used to notify when a node is deleted from the graph.
50 *
51 * @param n
52 * the node
53 */
54 public void nodeDeleted(V n);
55}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java
new file mode 100644
index 00000000..5924b723
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java
@@ -0,0 +1,82 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.igraph;
11
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder;
15
16/**
17 * This interface defines those methods that a transitive reachability data source should provide.
18 *
19 * @author Tamas Szabo
20 *
21 * @param <V>
22 * the type parameter of the node
23 */
24public interface ITcDataSource<V> {
25
26 /**
27 * Attach a transitive closure relation observer.
28 *
29 * @param to
30 * the observer object
31 */
32 public void attachObserver(ITcObserver<V> to);
33
34 /**
35 * Detach a transitive closure relation observer.
36 *
37 * @param to
38 * the observer object
39 */
40 public void detachObserver(ITcObserver<V> to);
41
42 /**
43 * Returns all nodes which are reachable from the source node.
44 *
45 * @param source
46 * the source node
47 * @return the set of target nodes
48 */
49 public Set<V> getAllReachableTargets(V source);
50
51 /**
52 * Returns all nodes from which the target node is reachable.
53 *
54 * @param target
55 * the target node
56 * @return the set of source nodes
57 */
58 public Set<V> getAllReachableSources(V target);
59
60 /**
61 * Returns true if the target node is reachable from the source node.
62 *
63 * @param source
64 * the source node
65 * @param target
66 * the target node
67 * @return true if target is reachable from source, false otherwise
68 */
69 public boolean isReachable(V source, V target);
70
71 /**
72 * The returned {@link IGraphPathFinder} can be used to retrieve paths between nodes using transitive reachability.
73 *
74 * @return a path finder for the graph.
75 */
76 public IGraphPathFinder<V> getPathFinder();
77
78 /**
79 * Call this method to properly dispose the data structures of a transitive closure algorithm.
80 */
81 public void dispose();
82}
diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java
new file mode 100644
index 00000000..fded53f1
--- /dev/null
+++ b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java
@@ -0,0 +1,39 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.igraph;
11
12/**
13 * Interface ITcObserver is used to observe the changes in a transitive closure relation; tuple insertion/deletion.
14 *
15 * @author Szabo Tamas
16 *
17 */
18public interface ITcObserver<V> {
19
20 /**
21 * Used to notify when a tuple is inserted into the transitive closure relation.
22 *
23 * @param source
24 * the source of the tuple
25 * @param target
26 * the target of the tuple
27 */
28 public void tupleInserted(V source, V target);
29
30 /**
31 * Used to notify when a tuple is deleted from the transitive closure relation.
32 *
33 * @param source
34 * the source of the tuple
35 * @param target
36 * the target of the tuple
37 */
38 public void tupleDeleted(V source, V target);
39}
diff --git a/subprojects/viatra-runtime-base/about.html b/subprojects/viatra-runtime-base/about.html
new file mode 100644
index 00000000..d1d5593a
--- /dev/null
+++ b/subprojects/viatra-runtime-base/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-base/build.gradle.kts b/subprojects/viatra-runtime-base/build.gradle.kts
new file mode 100644
index 00000000..55c6acb8
--- /dev/null
+++ b/subprojects/viatra-runtime-base/build.gradle.kts
@@ -0,0 +1,19 @@
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-itc"))
13 api(project(":refinery-viatra-runtime-matchers"))
14 implementation(libs.eclipse)
15 implementation(libs.ecore)
16 implementation(libs.emf)
17 implementation(libs.osgi)
18 implementation(libs.slf4j.log4j)
19}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/ViatraBasePlugin.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/ViatraBasePlugin.java
new file mode 100644
index 00000000..96b40825
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/ViatraBasePlugin.java
@@ -0,0 +1,46 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base;
11
12import org.eclipse.core.runtime.Plugin;
13import tools.refinery.viatra.runtime.base.comprehension.WellbehavingDerivedFeatureRegistry;
14import org.osgi.framework.BundleContext;
15
16public class ViatraBasePlugin extends Plugin {
17
18 // The shared instance
19 private static ViatraBasePlugin plugin;
20
21 public static final String PLUGIN_ID = "tools.refinery.viatra.runtime.base";
22 public static final String WELLBEHAVING_DERIVED_FEATURE_EXTENSION_POINT_ID = "tools.refinery.viatra.runtime.base.wellbehaving.derived.features";
23
24 @Override
25 public void start(BundleContext context) throws Exception {
26 super.start(context);
27 plugin = this;
28 WellbehavingDerivedFeatureRegistry.initRegistry();
29 }
30
31 @Override
32 public void stop(BundleContext context) throws Exception {
33 plugin = null;
34 super.stop(context);
35 }
36
37 /**
38 * Returns the shared instance
39 *
40 * @return the shared instance
41 */
42 public static ViatraBasePlugin getDefault() {
43 return plugin;
44 }
45
46}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/BaseIndexOptions.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/BaseIndexOptions.java
new file mode 100644
index 00000000..92663400
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/BaseIndexOptions.java
@@ -0,0 +1,395 @@
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.base.api;
10
11import java.util.Objects;
12
13import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexFeatureFilter;
14import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexObjectFilter;
15import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexResourceFilter;
16import tools.refinery.viatra.runtime.base.api.profiler.ProfilerMode;
17
18/**
19 * The base index options indicate how the indices are built.
20 *
21 * <p>
22 * One of the options is to build indices in <em>wildcard mode</em>, meaning that all EClasses, EDataTypes, EReferences
23 * and EAttributes are indexed. This is convenient, but comes at a high memory cost. To save memory, one can disable
24 * <em>wildcard mode</em> and manually register those EClasses, EDataTypes, EReferences and EAttributes that should be
25 * indexed.
26 * </p>
27 *
28 * <p>
29 * Another choice is whether to build indices in <em>dynamic EMF mode</em>, meaning that types are identified by the
30 * String IDs that are ultimately derived from the nsURI of the EPackage. Multiple types with the same ID are treated as
31 * the same. This is useful if dynamic EMF is used, where there can be multiple copies (instantiations) of the same
32 * EPackage, representing essentially the same metamodel. If one disables <em>dynamic EMF mode</em>, an error is logged
33 * if duplicate EPackages with the same nsURI are encountered.
34 * </p>
35 *
36 * @author Abel Hegedus
37 * @noimplement This class is not intended to be subclasses outside of VIATRA runtime
38 *
39 */
40public class BaseIndexOptions {
41
42 /**
43 *
44 * By default, base indices will be constructed with wildcard mode set as false.
45 */
46 protected static final IndexingLevel WILDCARD_MODE_DEFAULT = IndexingLevel.NONE;
47 /**
48 *
49 * By default, base indices will be constructed with only well-behaving features traversed.
50 */
51 private static final boolean TRAVERSE_ONLY_WELLBEHAVING_DERIVED_FEATURES_DEFAULT = true;
52 /**
53 *
54 * By default, base indices will be constructed with dynamic EMF mode set as false.
55 */
56 protected static final boolean DYNAMIC_EMF_MODE_DEFAULT = false;
57
58 /**
59 *
60 * By default, the scope will make the assumption that it is free from dangling edges.
61 * @since 1.6
62 */
63 protected static final boolean DANGLING_FREE_ASSUMPTION_DEFAULT = true;
64
65 /**
66 * By default, duplicate notifications are only logged.
67 *
68 * @since 1.6
69 */
70 protected static final boolean STRICT_NOTIFICATION_MODE_DEFAULT = true;
71
72 /**
73 * @since 2.3
74 */
75 protected static final ProfilerMode INDEX_PROFILER_MODE_DEFAULT = ProfilerMode.OFF;
76
77 /**
78 * @since 1.6
79 */
80 protected boolean danglingFreeAssumption = DANGLING_FREE_ASSUMPTION_DEFAULT;
81 protected boolean dynamicEMFMode = DYNAMIC_EMF_MODE_DEFAULT;
82 protected boolean traverseOnlyWellBehavingDerivedFeatures = TRAVERSE_ONLY_WELLBEHAVING_DERIVED_FEATURES_DEFAULT;
83 protected IndexingLevel wildcardMode = WILDCARD_MODE_DEFAULT;
84 protected IBaseIndexObjectFilter notifierFilterConfiguration;
85 protected IBaseIndexResourceFilter resourceFilterConfiguration;
86 /**
87 * @since 1.5
88 */
89 protected IBaseIndexFeatureFilter featureFilterConfiguration;
90
91 /**
92 * If strict notification mode is turned on, errors related to inconsistent notifications, e.g. duplicate deletions
93 * cause the entire Base index to be considered invalid, e.g. the query engine on top of the index should become
94 * tainted.
95 *
96 * @since 1.6
97 */
98 protected boolean strictNotificationMode = STRICT_NOTIFICATION_MODE_DEFAULT;
99
100 /**
101 * Returns whether base index profiling should be turned on.
102 *
103 * @since 2.3
104 */
105 protected ProfilerMode indexerProfilerMode = INDEX_PROFILER_MODE_DEFAULT;
106
107 /**
108 * Creates a base index options with the default values.
109 */
110 public BaseIndexOptions() {
111 }
112
113 /**
114 * Creates a base index options using the provided values for dynamic EMF mode and wildcard mode.
115 * @since 1.4
116 */
117 public BaseIndexOptions(boolean dynamicEMFMode, IndexingLevel wildcardMode) {
118 this.dynamicEMFMode = dynamicEMFMode;
119 this.wildcardMode = wildcardMode;
120 }
121
122 /**
123 *
124 * @param dynamicEMFMode
125 * @since 0.9
126 */
127 public BaseIndexOptions withDynamicEMFMode(boolean dynamicEMFMode) {
128 BaseIndexOptions result = copy();
129 result.dynamicEMFMode = dynamicEMFMode;
130 return result;
131 }
132
133 /**
134 * Sets the dangling edge handling property of the index option. If not set explicitly, it is considered as `true`.
135 * @param danglingFreeAssumption if true,
136 * the base index will assume that there are no dangling references
137 * (pointing out of scope or to proxies)
138 * @since 1.6
139 */
140 public BaseIndexOptions withDanglingFreeAssumption(boolean danglingFreeAssumption) {
141 BaseIndexOptions result = copy();
142 result.danglingFreeAssumption = danglingFreeAssumption;
143 return result;
144 }
145
146 /**
147 * Adds an object-level filter to the indexer configuration. Warning - object-level indexing can increase indexing time
148 * noticeably. If possibly, use {@link #withResourceFilterConfiguration(IBaseIndexResourceFilter)} instead.
149 *
150 * @param filter
151 * @since 0.9
152 */
153 public BaseIndexOptions withObjectFilterConfiguration(IBaseIndexObjectFilter filter) {
154 BaseIndexOptions result = copy();
155 result.notifierFilterConfiguration = filter;
156 return result;
157 }
158
159 /**
160 * @return the selected object filter configuration, or null if not set
161 */
162 public IBaseIndexObjectFilter getObjectFilterConfiguration() {
163 return notifierFilterConfiguration;
164 }
165
166 /**
167 * Returns a copy of the configuration with a specified resource filter
168 *
169 * @param filter
170 * @since 0.9
171 */
172 public BaseIndexOptions withResourceFilterConfiguration(IBaseIndexResourceFilter filter) {
173 BaseIndexOptions result = copy();
174 result.resourceFilterConfiguration = filter;
175 return result;
176 }
177
178 /**
179 * @return the selected resource filter, or null if not set
180 */
181 public IBaseIndexResourceFilter getResourceFilterConfiguration() {
182 return resourceFilterConfiguration;
183 }
184
185
186 /**
187 * Returns a copy of the configuration with a specified feature filter
188 *
189 * @param filter
190 * @since 1.5
191 */
192 public BaseIndexOptions withFeatureFilterConfiguration(IBaseIndexFeatureFilter filter) {
193 BaseIndexOptions result = copy();
194 result.featureFilterConfiguration = filter;
195 return result;
196 }
197
198 /**
199 * @return the selected feature filter, or null if not set
200 * @since 1.5
201 */
202 public IBaseIndexFeatureFilter getFeatureFilterConfiguration() {
203 return featureFilterConfiguration;
204 }
205
206
207 /**
208 * @return whether the base index option has dynamic EMF mode set
209 */
210 public boolean isDynamicEMFMode() {
211 return dynamicEMFMode;
212 }
213
214 /**
215 * @return whether the base index makes the assumption that there can be no dangling edges
216 * @since 1.6
217 */
218 public boolean isDanglingFreeAssumption() {
219 return danglingFreeAssumption;
220 }
221
222 /**
223 * @return whether the base index option has traverse only well-behaving derived features set
224 */
225 public boolean isTraverseOnlyWellBehavingDerivedFeatures() {
226 return traverseOnlyWellBehavingDerivedFeatures;
227 }
228
229 /**
230 *
231 * @param wildcardMode
232 * @since 1.4
233 */
234 public BaseIndexOptions withWildcardLevel(IndexingLevel wildcardLevel) {
235 BaseIndexOptions result = copy();
236 result.wildcardMode = wildcardLevel;
237 return result;
238 }
239
240 /**
241 * @since 1.6
242 */
243 public BaseIndexOptions withStrictNotificationMode(boolean strictNotificationMode) {
244 BaseIndexOptions result = copy();
245 result.strictNotificationMode = strictNotificationMode;
246 return result;
247 }
248
249 /**
250 * @since 2.3
251 */
252 public BaseIndexOptions withIndexProfilerMode(ProfilerMode indexerProfilerMode) {
253 BaseIndexOptions result = copy();
254 result.indexerProfilerMode = indexerProfilerMode;
255 return result;
256 }
257
258 /**
259 * @return whether the base index option has wildcard mode set
260 */
261 public boolean isWildcardMode() {
262 return wildcardMode == IndexingLevel.FULL;
263 }
264
265 /**
266 * @return the wildcardMode level
267 * @since 1.4
268 */
269 public IndexingLevel getWildcardLevel() {
270 return wildcardMode;
271 }
272
273 /**
274 * If strict notification mode is turned on, errors related to inconsistent notifications, e.g. duplicate deletions
275 * cause the entire Base index to be considered invalid, e.g. the query engine on top of the index should become
276 * tainted.
277 *
278 * @since 1.6
279 */
280 public boolean isStrictNotificationMode() {
281 return strictNotificationMode;
282 }
283
284 /**
285 * Returns whether base indexer profiling is enabled. The profiling causes some slowdown, but provides information
286 * about how much time the base indexer takes for initialization and updates.
287 *
288 * @since 2.3
289 */
290 public ProfilerMode getIndexerProfilerMode() {
291 return indexerProfilerMode;
292 }
293
294 /**
295 * Creates an independent copy of itself. The values of each option will be the same as this options. This method is
296 * used when a provided option must be copied to avoid external option changes afterward.
297 *
298 * @return the copy of this options
299 */
300 public BaseIndexOptions copy() {
301 BaseIndexOptions baseIndexOptions = new BaseIndexOptions(this.dynamicEMFMode, this.wildcardMode);
302 baseIndexOptions.danglingFreeAssumption = this.danglingFreeAssumption;
303 baseIndexOptions.traverseOnlyWellBehavingDerivedFeatures = this.traverseOnlyWellBehavingDerivedFeatures;
304 baseIndexOptions.notifierFilterConfiguration = this.notifierFilterConfiguration;
305 baseIndexOptions.resourceFilterConfiguration = this.resourceFilterConfiguration;
306 baseIndexOptions.featureFilterConfiguration = this.featureFilterConfiguration;
307 baseIndexOptions.strictNotificationMode = this.strictNotificationMode;
308 baseIndexOptions.indexerProfilerMode = this.indexerProfilerMode;
309 return baseIndexOptions;
310 }
311
312 @Override
313 public int hashCode() {
314 return Objects.hash(dynamicEMFMode, notifierFilterConfiguration, resourceFilterConfiguration,
315 featureFilterConfiguration, traverseOnlyWellBehavingDerivedFeatures, wildcardMode, strictNotificationMode,
316 danglingFreeAssumption, indexerProfilerMode);
317 }
318
319 @Override
320 public boolean equals(Object obj) {
321 if (this == obj)
322 return true;
323 if (obj == null)
324 return false;
325 if (!(obj instanceof BaseIndexOptions))
326 return false;
327 BaseIndexOptions other = (BaseIndexOptions) obj;
328 if (dynamicEMFMode != other.dynamicEMFMode)
329 return false;
330 if (danglingFreeAssumption != other.danglingFreeAssumption)
331 return false;
332 if (notifierFilterConfiguration == null) {
333 if (other.notifierFilterConfiguration != null)
334 return false;
335 } else if (!notifierFilterConfiguration
336 .equals(other.notifierFilterConfiguration))
337 return false;
338 if (resourceFilterConfiguration == null) {
339 if (other.resourceFilterConfiguration != null)
340 return false;
341 } else if (!resourceFilterConfiguration
342 .equals(other.resourceFilterConfiguration)){
343 return false;
344 }
345
346 if (featureFilterConfiguration == null) {
347 if (other.featureFilterConfiguration != null)
348 return false;
349 } else if (!featureFilterConfiguration
350 .equals(other.featureFilterConfiguration)){
351 return false;
352 }
353
354 if (traverseOnlyWellBehavingDerivedFeatures != other.traverseOnlyWellBehavingDerivedFeatures)
355 return false;
356 if (wildcardMode != other.wildcardMode)
357 return false;
358 if (strictNotificationMode != other.strictNotificationMode) {
359 return false;
360 }
361 if (indexerProfilerMode != other.indexerProfilerMode) {
362 return false;
363 }
364 return true;
365 }
366
367
368 @Override
369 public String toString() {
370 StringBuilder sb = new StringBuilder();
371 appendModifier(sb, dynamicEMFMode, DYNAMIC_EMF_MODE_DEFAULT, "dynamicEMF");
372 appendModifier(sb, wildcardMode, WILDCARD_MODE_DEFAULT, "wildcard");
373 appendModifier(sb, danglingFreeAssumption, DANGLING_FREE_ASSUMPTION_DEFAULT, "danglingFreeAssumption");
374 appendModifier(sb, traverseOnlyWellBehavingDerivedFeatures, TRAVERSE_ONLY_WELLBEHAVING_DERIVED_FEATURES_DEFAULT, "wellBehavingOnly");
375 appendModifier(sb, strictNotificationMode, STRICT_NOTIFICATION_MODE_DEFAULT, "strictNotificationMode");
376 appendModifier(sb, indexerProfilerMode, INDEX_PROFILER_MODE_DEFAULT, "indexerProfilerMode");
377 appendModifier(sb, notifierFilterConfiguration, null, "notifierFilter=");
378 appendModifier(sb, resourceFilterConfiguration, null, "resourceFilter=");
379 appendModifier(sb, featureFilterConfiguration, null, "featureFilterConfiguration=");
380 final String result = sb.toString();
381 return result.isEmpty() ? "defaults" : result;
382 }
383
384 private static void appendModifier(StringBuilder sb, Object actualValue, Object expectedValue, String switchName) {
385 if (Objects.equals(expectedValue, actualValue)) {
386 // silent
387 } else {
388 sb.append(Boolean.FALSE.equals(actualValue) ? '-' : '+');
389 sb.append(switchName);
390 if (! (actualValue instanceof Boolean))
391 sb.append(actualValue);
392 }
393 }
394
395}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/DataTypeListener.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/DataTypeListener.java
new file mode 100644
index 00000000..3d30df5e
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/DataTypeListener.java
@@ -0,0 +1,44 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.base.api;
10
11import org.eclipse.emf.ecore.EDataType;
12
13/**
14 * Interface for observing insertion and deletion of instances of data types.
15 *
16 * @author Tamas Szabo
17 *
18 */
19public interface DataTypeListener {
20
21 /**
22 * Called when an instance of the given type is inserted.
23 *
24 * @param type
25 * the {@link EDataType}
26 * @param instance
27 * the instance of the data type
28 * @param firstOccurrence
29 * true if this value was not previously present in the model
30 */
31 public void dataTypeInstanceInserted(EDataType type, Object instance, boolean firstOccurrence);
32
33 /**
34 * Called when an instance of the given type is deleted.
35 *
36 * @param type
37 * the {@link EDataType}
38 * @param instance
39 * the instance of the data type
40 * @param lastOccurrence
41 * true if this value is no longer present in the model
42 */
43 public void dataTypeInstanceDeleted(EDataType type, Object instance, boolean lastOccurrence);
44}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/EMFBaseIndexChangeListener.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/EMFBaseIndexChangeListener.java
new file mode 100644
index 00000000..5a2486f9
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/EMFBaseIndexChangeListener.java
@@ -0,0 +1,33 @@
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.base.api;
10
11/**
12 * Listener interface for change notifications from the VIATRA Base index.
13 *
14 * @author Abel Hegedus
15 *
16 */
17public interface EMFBaseIndexChangeListener {
18
19 /**
20 * NOTE: it is possible that this method is called only ONCE! Consider returning a constant value that is set in the constructor.
21 *
22 * @return true, if the listener should be notified only after index changes, false if notification is needed after each model change
23 */
24 public boolean onlyOnIndexChange();
25
26 /**
27 * Called after a model change is handled by the VIATRA Base index and if <code>indexChanged == onlyOnIndexChange()</code>.
28 *
29 * @param indexChanged true, if the model change also affected the contents of the base index
30 */
31 public void notifyChanged(boolean indexChanged);
32
33}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/FeatureListener.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/FeatureListener.java
new file mode 100644
index 00000000..fa2d679e
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/FeatureListener.java
@@ -0,0 +1,48 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.base.api;
10
11import org.eclipse.emf.ecore.EAttribute;
12import org.eclipse.emf.ecore.EObject;
13import org.eclipse.emf.ecore.EReference;
14import org.eclipse.emf.ecore.EStructuralFeature;
15
16/**
17 * Interface for observing insertion and deletion of structural feature values ("settings"). (Works both for
18 * single-valued and many-valued features.)
19 *
20 * @author Tamas Szabo
21 *
22 */
23public interface FeatureListener {
24
25 /**
26 * Called when the given value is inserted into the given feature of the given host EObject.
27 *
28 * @param host
29 * the host (holder) of the feature
30 * @param feature
31 * the {@link EAttribute} or {@link EReference} instance
32 * @param value
33 * the target of the feature
34 */
35 public void featureInserted(EObject host, EStructuralFeature feature, Object value);
36
37 /**
38 * Called when the given value is removed from the given feature of the given host EObject.
39 *
40 * @param host
41 * the host (holder) of the feature
42 * @param feature
43 * the {@link EAttribute} or {@link EReference} instance
44 * @param value
45 * the target of the feature
46 */
47 public void featureDeleted(EObject host, EStructuralFeature feature, Object value);
48}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IEClassifierProcessor.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IEClassifierProcessor.java
new file mode 100644
index 00000000..aaa98918
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IEClassifierProcessor.java
@@ -0,0 +1,25 @@
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.base.api;
10
11import org.eclipse.emf.ecore.EClass;
12import org.eclipse.emf.ecore.EDataType;
13import org.eclipse.emf.ecore.EObject;
14
15/**
16 * @author Abel Hegedus
17 *
18 */
19public interface IEClassifierProcessor<ClassType, InstanceType> {
20
21 void process(ClassType type, InstanceType instance);
22
23 public interface IEClassProcessor extends IEClassifierProcessor<EClass, EObject>{}
24 public interface IEDataTypeProcessor extends IEClassifierProcessor<EDataType, Object>{}
25}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IEMFIndexingErrorListener.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IEMFIndexingErrorListener.java
new file mode 100644
index 00000000..1dc3291b
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IEMFIndexingErrorListener.java
@@ -0,0 +1,22 @@
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.base.api;
10
11/**
12 *
13 * This interface contains callbacks for various internal errors from the {@link NavigationHelper base index}.
14 *
15 * @author Zoltan Ujhelyi
16 *
17 */
18public interface IEMFIndexingErrorListener {
19
20 void error(String description, Throwable t);
21 void fatal(String description, Throwable t);
22}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IQueryResultSetter.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IQueryResultSetter.java
new file mode 100644
index 00000000..717bad4b
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IQueryResultSetter.java
@@ -0,0 +1,63 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, 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.base.api;
10
11/**
12 * Setter interface for query result multimaps that allow modifications of the model through the multimap.
13 *
14 * <p>
15 * The model modifications should ensure that the multimap changes exactly as required (i.e. a put results in only one
16 * new key-value pair and remove results in only one removed pair).
17 *
18 * <p>
19 * The input parameters of both put and remove can be validated by implementing the {@link #validate(Object, Object)}
20 * method.
21 *
22 * @author Abel Hegedus
23 *
24 * @param <KeyType>
25 * @param <ValueType>
26 */
27public interface IQueryResultSetter<KeyType, ValueType> {
28 /**
29 * Modify the underlying model of the query in order to have the given key-value pair as a new result of the query.
30 *
31 * @param key
32 * the key for which a new value is added to the query results
33 * @param value
34 * the new value that should be added to the query results for the given key
35 * @return true, if the query result changed
36 */
37 boolean put(KeyType key, ValueType value);
38
39 /**
40 * Modify the underlying model of the query in order to remove the given key-value pair from the results of the
41 * query.
42 *
43 * @param key
44 * the key for which the value is removed from the query results
45 * @param value
46 * the value that should be removed from the query results for the given key
47 * @return true, if the query result changed
48 */
49 boolean remove(KeyType key, ValueType value);
50
51 /**
52 * Validates a given key-value pair for the query result. The validation has to ensure that (1) if the pair does not
53 * exist in the result, it can be added safely (2) if the pair already exists in the result, it can be removed
54 * safely
55 *
56 * @param key
57 * the key of the pair that is validated
58 * @param value
59 * the value of the pair that is validated
60 * @return true, if the pair does not exists but can be added or the pair exists and can be removed
61 */
62 boolean validate(KeyType key, ValueType value);
63} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IQueryResultUpdateListener.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IQueryResultUpdateListener.java
new file mode 100644
index 00000000..5addfd78
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IQueryResultUpdateListener.java
@@ -0,0 +1,45 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, 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.base.api;
10
11/**
12 * Listener interface for receiving notification from {@link QueryResultMultimap}
13 *
14 * @author Abel Hegedus
15 *
16 * @param <KeyType>
17 * @param <ValueType>
18 */
19public interface IQueryResultUpdateListener<KeyType, ValueType> {
20 /**
21 * This method is called by the query result multimap when a new key-value pair is put into the multimap
22 *
23 * <p>
24 * Only invoked if the contents of the multimap changed!
25 *
26 * @param key
27 * the key of the newly inserted pair
28 * @param value
29 * the value of the newly inserted pair
30 */
31 void notifyPut(KeyType key, ValueType value);
32
33 /**
34 * This method is called by the query result multimap when key-value pair is removed from the multimap
35 *
36 * <p>
37 * Only invoked if the contents of the multimap changed!
38 *
39 * @param key
40 * the key of the removed pair
41 * @param value
42 * the value of the removed pair
43 */
44 void notifyRemove(KeyType key, ValueType value);
45} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IStructuralFeatureInstanceProcessor.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IStructuralFeatureInstanceProcessor.java
new file mode 100644
index 00000000..208ad761
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IStructuralFeatureInstanceProcessor.java
@@ -0,0 +1,19 @@
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.base.api;
10
11import org.eclipse.emf.ecore.EObject;
12
13/**
14 * @author Gabor Bergmann
15 * @since 1.7
16 */
17public interface IStructuralFeatureInstanceProcessor {
18 void process(EObject source, Object target);
19}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IndexingLevel.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IndexingLevel.java
new file mode 100644
index 00000000..df5c59f5
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/IndexingLevel.java
@@ -0,0 +1,113 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.base.api;
10
11import tools.refinery.viatra.runtime.matchers.context.IndexingService;
12
13import java.util.Set;
14
15/**
16 * The values of this enum denotes the level of indexing the base indexer is capable of.
17 *
18 * @author Grill Balázs
19 * @since 1.4
20 *
21 */
22public enum IndexingLevel {
23
24 /**
25 * No indexing is performed
26 */
27 NONE,
28
29 /**
30 * Only cardinality information is stored. This indexing level makes possible to calculate
31 * results of {@link NavigationHelper#countAllInstances(org.eclipse.emf.ecore.EClass)}, {@link NavigationHelper#countFeatures(org.eclipse.emf.ecore.EStructuralFeature)}
32 * and {@link NavigationHelper#countDataTypeInstances(org.eclipse.emf.ecore.EDataType)} with minimal memory footprint.
33 */
34 STATISTICS,
35
36 /**
37 * Notifications are dispatched about the changes
38 */
39 NOTIFICATIONS,
40
41 /**
42 * Cardinality information is stored and live notifications are dispatched
43 */
44 BOTH,
45
46 /**
47 * Full indexing is performed, set of instances is available
48 */
49 FULL
50
51 ;
52
53 private static final IndexingLevel[][] mergeTable = {
54 /* NONE STATISTICS NOTIFICATIONS BOTH FULL*/
55 /* NONE */{ NONE, STATISTICS, NOTIFICATIONS, BOTH, FULL},
56 /* STATISTICS */{ STATISTICS, STATISTICS, BOTH, BOTH, FULL},
57 /* NOTIFICATIONS */{ NOTIFICATIONS, BOTH, NOTIFICATIONS, BOTH, FULL},
58 /* BOTH */{ BOTH, BOTH, BOTH, BOTH, FULL},
59 /* FULL */{ FULL, FULL, FULL, FULL, FULL}
60 };
61
62 public static IndexingLevel toLevel(IndexingService service){
63 switch(service){
64 case INSTANCES:
65 return IndexingLevel.FULL;
66 case NOTIFICATIONS:
67 return IndexingLevel.NOTIFICATIONS;
68 case STATISTICS:
69 return IndexingLevel.STATISTICS;
70 default:
71 return IndexingLevel.NONE;
72 }
73 }
74
75 public static IndexingLevel toLevel(Set<IndexingService> services){
76 IndexingLevel result = NONE;
77 for(IndexingService service : services){
78 result = result.merge(toLevel(service));
79 }
80 return result;
81 }
82
83 /**
84 * Merge this level with the given other level, The resulting indexing level will provide the
85 * functionality which conforms to both given levels.
86 */
87 public IndexingLevel merge(IndexingLevel other){
88 if (other == null) return this;
89 return mergeTable[this.ordinal()][other.ordinal()];
90 }
91
92 /**
93 * Tells whether the indexer shall perform separate statistics calculation for this level
94 */
95 public boolean hasStatistics() {
96 return this == IndexingLevel.BOTH || this == IndexingLevel.STATISTICS || this == IndexingLevel.FULL;
97 }
98
99 /**
100 * Tells whether the indexer shall perform instance indexing
101 */
102 public boolean hasInstances(){
103 return this == IndexingLevel.FULL;
104 }
105
106 /**
107 * Returns whether the current indexing level includes all features from the parameter level
108 * @since 1.5
109 */
110 public boolean providesLevel(IndexingLevel level) {
111 return this.merge(level) == this;
112 }
113}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/InstanceListener.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/InstanceListener.java
new file mode 100644
index 00000000..6339545d
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/InstanceListener.java
@@ -0,0 +1,41 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.base.api;
10
11import org.eclipse.emf.ecore.EClass;
12import org.eclipse.emf.ecore.EObject;
13
14/**
15 * Interface for observing insertion / deletion of instances of EClass.
16 *
17 * @author Tamas Szabo
18 *
19 */
20public interface InstanceListener {
21
22 /**
23 * Called when the given instance was added to the model.
24 *
25 * @param clazz
26 * an EClass registered for this listener, for which a new instance (possibly an instance of a subclass) was inserted into the model
27 * @param instance
28 * an EObject instance that was inserted into the model
29 */
30 public void instanceInserted(EClass clazz, EObject instance);
31
32 /**
33 * Called when the given instance was removed from the model.
34 *
35 * @param clazz
36 * an EClass registered for this listener, for which an instance (possibly an instance of a subclass) was removed from the model
37 * @param instance
38 * an EObject instance that was removed from the model
39 */
40 public void instanceDeleted(EClass clazz, EObject instance);
41}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/LightweightEObjectObserver.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/LightweightEObjectObserver.java
new file mode 100644
index 00000000..f0245b5d
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/LightweightEObjectObserver.java
@@ -0,0 +1,32 @@
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.base.api;
10
11import org.eclipse.emf.common.notify.Notification;
12import org.eclipse.emf.ecore.EObject;
13import org.eclipse.emf.ecore.EStructuralFeature;
14
15
16/**
17 * Listener interface for lightweight observation on EObject feature value changes.
18 *
19 * @author Abel Hegedus
20 *
21 */
22public interface LightweightEObjectObserver {
23
24 /**
25 *
26 * @param host
27 * @param feature
28 * @param notification
29 */
30 void notifyFeatureChanged(EObject host, EStructuralFeature feature, Notification notification);
31
32}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/LightweightEObjectObserverAdapter.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/LightweightEObjectObserverAdapter.java
new file mode 100644
index 00000000..bcdb8ff4
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/LightweightEObjectObserverAdapter.java
@@ -0,0 +1,74 @@
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.base.api;
10
11import static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkArgument;
12
13import java.util.Collection;
14import java.util.HashSet;
15
16import org.eclipse.emf.common.notify.Notification;
17import org.eclipse.emf.ecore.EObject;
18import org.eclipse.emf.ecore.EStructuralFeature;
19
20/**
21 * Adapter class for lightweight observer which filters feature updates to a selected set of features.
22 *
23 * @author Abel Hegedus
24 *
25 */
26public abstract class LightweightEObjectObserverAdapter implements LightweightEObjectObserver {
27
28 private Collection<EStructuralFeature> observedFeatures;
29
30 /**
31 * Creates a new adapter with the given set of observed features.
32 */
33 public LightweightEObjectObserverAdapter(Collection<EStructuralFeature> observedFeatures) {
34 checkArgument(observedFeatures != null, "List of observed features must not be null!");
35 this.observedFeatures = new HashSet<>(observedFeatures);
36 }
37
38 public void observeAdditionalFeature(EStructuralFeature observedFeature) {
39 checkArgument(observedFeature != null, "Cannot observe null feature!");
40 this.observedFeatures.add(observedFeature);
41 }
42
43 public void observeAdditionalFeatures(Collection<EStructuralFeature> observedFeatures) {
44 checkArgument(observedFeatures != null, "List of additional observed features must not be null!");
45 this.observedFeatures.addAll(observedFeatures);
46 }
47
48 public void removeObservedFeature(EStructuralFeature observedFeature) {
49 checkArgument(observedFeature != null, "Cannot remove null observed feature!");
50 this.observedFeatures.remove(observedFeature);
51 }
52
53 public void removeObservedFeatures(Collection<EStructuralFeature> observedFeatures) {
54 checkArgument(observedFeatures != null, "List of observed features to remove must not be null!");
55 this.observedFeatures.removeAll(observedFeatures);
56 }
57
58 @Override
59 public void notifyFeatureChanged(EObject host, EStructuralFeature feature, Notification notification) {
60 if(this.observedFeatures.contains(feature)) {
61 observedFeatureUpdate(host, feature, notification);
62 }
63 }
64
65 /**
66 * This method is called when the feature that changed is among the observed features of the adapter.
67 *
68 * @param host
69 * @param feature
70 * @param notification
71 */
72 public abstract void observedFeatureUpdate(EObject host, EStructuralFeature feature, Notification notification);
73
74}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/NavigationHelper.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/NavigationHelper.java
new file mode 100644
index 00000000..6a9f704b
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/NavigationHelper.java
@@ -0,0 +1,885 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.api;
11
12import org.eclipse.emf.common.notify.Notifier;
13import org.eclipse.emf.common.util.EList;
14import org.eclipse.emf.common.util.Enumerator;
15import org.eclipse.emf.ecore.*;
16import org.eclipse.emf.ecore.EStructuralFeature.Setting;
17import org.eclipse.emf.ecore.resource.Resource;
18import org.eclipse.emf.ecore.resource.ResourceSet;
19import tools.refinery.viatra.runtime.base.api.IEClassifierProcessor.IEClassProcessor;
20import tools.refinery.viatra.runtime.base.api.IEClassifierProcessor.IEDataTypeProcessor;
21import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
22
23import java.lang.reflect.InvocationTargetException;
24import java.util.Collection;
25import java.util.ConcurrentModificationException;
26import java.util.Set;
27import java.util.concurrent.Callable;
28
29/**
30 *
31 * Using an index of the EMF model, this interface exposes useful query functionality, such as:
32 * <ul>
33 * <li>
34 * Getting all the (direct or descendant) instances of a given {@link EClass}
35 * <li>
36 * Inverse navigation along arbitrary {@link EReference} instances (heterogenous paths too)
37 * <li>
38 * Finding model elements by attribute value (i.e. inverse navigation along {@link EAttribute})
39 * <li>
40 * Querying instances of given data types, or structural features.
41 * </ul>
42 * As queries are served from an index, results are always instantaneous.
43 *
44 * <p>
45 * Such indices will be built on an EMF model rooted at an {@link EObject}, {@link Resource} or {@link ResourceSet}.
46 * The boundaries of the model are defined by the containment (sub)tree.
47 * The indices will be <strong>maintained incrementally</strong> on changes to the model; these updates can also be
48 * observed by registering listeners.
49 * </p>
50 *
51 * <p>
52 * One of the options is to build indices in <em>wildcard mode</em>, meaning that all EClasses, EDataTypes, EReferences
53 * and EAttributes are indexed. This is convenient, but comes at a high memory cost. To save memory, one can disable
54 * <em>wildcard mode</em> and manually register those EClasses, EDataTypes, EReferences and EAttributes that should be
55 * indexed.
56 * </p>
57 *
58 * <p>
59 * Another choice is whether to build indices in <em>dynamic EMF mode</em>, meaning that types are identified by the String IDs
60 * that are ultimately derived from the nsURI of the EPackage. Multiple types with the same ID are treated as the same.
61 * This is useful if dynamic EMF is used, where there can be multiple copies (instantiations) of the same EPackage,
62 * representing essentially the same metamodel. If one disables <em>dynamic EMF mode</em>, an error is logged if
63 * duplicate EPackages with the same nsURI are encountered.
64 * </p>
65 *
66 * <p>
67 * Note that none of the defined query methods return null upon empty result sets. All query methods return either a copy of
68 * the result sets (where {@link Setting} is instantiated) or an unmodifiable collection of the result view.
69 *
70 * <p>
71 * Instantiate using {@link ViatraBaseFactory}
72 *
73 * @author Tamas Szabo
74 * @noimplement This interface is not intended to be implemented by clients.
75 *
76 */
77public interface NavigationHelper {
78
79 /**
80 * Indicates whether indexing is performed in <em>wildcard mode</em>, where every aspect of the EMF model is
81 * automatically indexed.
82 *
83 * @return true if everything is indexed, false if manual registration of interesting EClassifiers and
84 * EStructuralFeatures is required.
85 */
86 public boolean isInWildcardMode();
87
88 /**
89 * Indicates whether indexing is performed in <em>wildcard mode</em> for a selected indexing level
90 *
91 * @return true if everything is indexed, false if manual registration of interesting EClassifiers and
92 * EStructuralFeatures is required.
93 * @since 1.5
94 */
95 public boolean isInWildcardMode(IndexingLevel level);
96
97 /**
98 * Returns the current {@link IndexingLevel} applied to all model elements. For specific types it is possible to request a higher indexing levels, but cannot be lowered.
99 * @return the current level of index specified
100 * @since 1.4
101 */
102 public IndexingLevel getWildcardLevel();
103
104 /**
105 * Starts wildcard indexing at the given level. After this call, no registration is required for this {@link IndexingLevel}.
106 * a previously set wildcard level cannot be lowered, only extended.
107 *
108 * @since 1.4
109 */
110 public void setWildcardLevel(IndexingLevel level);
111
112 /**
113 * Indicates whether indexing is performed in <em>dynamic EMF mode</em>, i.e. EPackage nsURI collisions are
114 * tolerated and EPackages with the same URI are automatically considered as equal.
115 *
116 * @return true if multiple EPackages with the same nsURI are treated as the same,
117 * false if an error is logged instead in this case.
118 */
119 public boolean isInDynamicEMFMode();
120
121 /**
122 * For a given attribute value <code>value</code>, find each {@link EAttribute} and host {@link EObject}
123 * such that this attribute of the the host object takes the given value. The method will
124 * return a set of {@link Setting}s, one for each such host object - EAttribute - value triplet.
125 *
126 * <p>
127 * <strong>Precondition:</strong> Unset / null attribute values are not indexed, so <code>value!=null</code>
128 *
129 * <p>
130 * <strong>Precondition:</strong> Will only find those EAttributes that have already been registered using
131 * {@link #registerEStructuralFeatures(Set)}, unless running in <em>wildcard mode</em> (see
132 * {@link #isInWildcardMode()}).
133 *
134 * @param value
135 * the value of the attribute
136 * @return a set of {@link Setting}s, one for each EObject and EAttribute that have the given value
137 * @see #findByAttributeValue(Object)
138 */
139 public Set<Setting> findByAttributeValue(Object value);
140
141 /**
142 * For given <code>attributes</code> and an attribute value <code>value</code>, find each host {@link EObject}
143 * such that any of these attributes of the the host object takes the given value. The method will
144 * return a set of {@link Setting}s, one for each such host object - EAttribute - value triplet.
145 *
146 * <p>
147 * <strong>Precondition:</strong> Unset / null attribute values are not indexed, so <code>value!=null</code>
148 *
149 * <p>
150 * <strong>Precondition:</strong> Will only find those EAttributes that have already been registered using
151 * {@link #registerEStructuralFeatures(Set)}, unless running in <em>wildcard mode</em> (see
152 * {@link #isInWildcardMode()}).
153 *
154 * @param value
155 * the value of the attribute
156 * @param attributes
157 * the collection of attributes that should take the given value
158 * @return a set of {@link Setting}s, one for each EObject and attribute that have the given value
159 */
160 public Set<Setting> findByAttributeValue(Object value, Collection<EAttribute> attributes);
161
162 /**
163 * Find all {@link EObject}s for which the given <code>attribute</code> takes the given <code>value</code>.
164 *
165 * <p>
166 * <strong>Precondition:</strong> Unset / null attribute values are not indexed, so <code>value!=null</code>
167 *
168 * <p>
169 * <strong>Precondition:</strong> Results will be returned only if either (a) the EAttribute has already been
170 * registered using {@link #registerEStructuralFeatures(Set)}, or (b) running in <em>wildcard mode</em> (see
171 * {@link #isInWildcardMode()}).
172 *
173 * @param value
174 * the value of the attribute
175 * @param attribute
176 * the EAttribute that should take the given value
177 * @return the set of {@link EObject}s for which the given attribute has the given value
178 */
179 public Set<EObject> findByAttributeValue(Object value, EAttribute attribute);
180
181 /**
182 * Returns the set of instances for the given {@link EDataType} that can be found in the model.
183 *
184 * <p>
185 * <strong>Precondition:</strong> Results will be returned only if either (a) the EDataType has already been
186 * registered using {@link #registerEDataTypes(Set)}, or (b) running in <em>wildcard mode</em> (see
187 * {@link #isInWildcardMode()}).
188 *
189 * @param type
190 * the data type
191 * @return the set of all attribute values found in the model that are of the given data type
192 */
193 public Set<Object> getDataTypeInstances(EDataType type);
194
195 /**
196 * Returns whether an object is an instance for the given {@link EDataType} that can be found in the current scope.
197 * <p>
198 * <strong>Precondition:</strong> Result will be true only if either (a) the EDataType has already been registered
199 * using {@link #registerEDataTypes(Set)}, or (b) running in <em>wildcard mode</em> (see
200 * {@link #isInWildcardMode()}).
201 *
202 * @param value a non-null value to decide whether it is available as an EDataType instance
203 * @param type a non-null EDataType
204 * @return true, if a corresponding instance was found
205 * @since 1.7
206 */
207 public boolean isInstanceOfDatatype(Object value, EDataType type);
208
209 /**
210 * Find all {@link EObject}s that are the target of the EReference <code>reference</code> from the given
211 * <code>source</code> {@link EObject}.
212 *
213 * <p>
214 * Unset / null-valued references are not indexed, and will not be included in the results.
215 *
216 * <p>
217 * <strong>Precondition:</strong> Results will be returned only if either (a) the reference has already been
218 * registered using {@link #registerEStructuralFeatures(Set)}, or (b) running in <em>wildcard mode</em> (see
219 * {@link #isInWildcardMode()}).
220 *
221 * @param source the host object
222 * @param reference an EReference of the host object
223 * @return the set of {@link EObject}s that the given reference points to, from the given source object
224 */
225 public Set<EObject> getReferenceValues(EObject source, EReference reference);
226
227 /**
228 * Find all {@link Object}s that are the target of the EStructuralFeature <code>feature</code> from the given
229 * <code>source</code> {@link EObject}.
230 *
231 * <p>
232 * Unset / null-valued features are not indexed, and will not be included in the results.
233 *
234 * <p>
235 * <strong>Precondition:</strong> Results will be returned only if either (a) the feature has already been
236 * registered, or (b) running in <em>wildcard mode</em> (see
237 * {@link #isInWildcardMode()}).
238 *
239 * @param source the host object
240 * @param feature an EStructuralFeature of the host object
241 * @return the set of values that the given feature takes at the given source object
242 *
243 * @see #getReferenceValues(EObject, EReference)
244 */
245 public Set<Object> getFeatureTargets(EObject source, EStructuralFeature feature);
246
247 /**
248 * Decides whether the given non-null source and target objects are connected via a specific, indexed EStructuralFeature instance.
249 *
250 * <p>
251 * Unset / null-valued features are not indexed, and will not be included in the results.
252 *
253 * <p>
254 * <strong>Precondition:</strong> Result will be true only if either (a) the feature has already been
255 * registered, or (b) running in <em>wildcard mode</em> (see
256 * {@link #isInWildcardMode()}).
257 * @since 1.7
258 */
259 public boolean isFeatureInstance(EObject source, Object target, EStructuralFeature feature);
260
261 /**
262 * For a given {@link EObject} <code>target</code>, find each {@link EReference} and source {@link EObject}
263 * such that this reference (list) of the the host object points to the given target object. The method will
264 * return a set of {@link Setting}s, one for each such source object - EReference - target triplet.
265 *
266 * <p>
267 * <strong>Precondition:</strong> Unset / null reference values are not indexed, so <code>target!=null</code>
268 *
269 * <p>
270 * <strong>Precondition:</strong> Results will be returned only for those references that have already been
271 * registered using {@link #registerEStructuralFeatures(Set)}, or all references if running in
272 * <em>wildcard mode</em> (see {@link #isInWildcardMode()}).
273 *
274 * @param target
275 * the EObject pointed to by the references
276 * @return a set of {@link Setting}s, one for each source EObject and reference that point to the given target
277 */
278 public Set<Setting> getInverseReferences(EObject target);
279
280 /**
281 * For given <code>references</code> and an {@link EObject} <code>target</code>, find each source {@link EObject}
282 * such that any of these references of the the source object points to the given target object. The method will
283 * return a set of {@link Setting}s, one for each such source object - EReference - target triplet.
284 *
285 * <p>
286 * <strong>Precondition:</strong> Unset / null reference values are not indexed, so <code>target!=null</code>
287 *
288 * <p>
289 * <strong>Precondition:</strong> Will only find those EReferences that have already been registered using
290 * {@link #registerEStructuralFeatures(Set)}, unless running in <em>wildcard mode</em> (see
291 * {@link #isInWildcardMode()}).
292 *
293 * @param target
294 * the EObject pointed to by the references
295 * @param references a set of EReferences pointing to the target
296 * @return a set of {@link Setting}s, one for each source EObject and reference that point to the given target
297 */
298 public Set<Setting> getInverseReferences(EObject target, Collection<EReference> references);
299
300 /**
301 * Find all source {@link EObject}s for which the given <code>reference</code> points to the given <code>target</code> object.
302 *
303 * <p>
304 * <strong>Precondition:</strong> Unset / null reference values are not indexed, so <code>target!=null</code>
305 *
306 * <p>
307 * <strong>Precondition:</strong> Results will be returned only if either (a) the reference has already been
308 * registered using {@link #registerEStructuralFeatures(Set)}, or (b) running in <em>wildcard mode</em> (see
309 * {@link #isInWildcardMode()}).
310 *
311 * @param target
312 * the EObject pointed to by the references
313 * @param reference
314 * an EReference pointing to the target
315 * @return the collection of {@link EObject}s for which the given reference points to the given target object
316 */
317 public Set<EObject> getInverseReferences(EObject target, EReference reference);
318
319 /**
320 * Get the direct {@link EObject} instances of the given {@link EClass}. Instances of subclasses will be excluded.
321 *
322 * <p>
323 * <strong>Precondition:</strong> Results will be returned only if either (a) the EClass (or any superclass) has
324 * already been registered using {@link #registerEClasses(Set)}, or (b) running in <em>wildcard mode</em> (see
325 * {@link #isInWildcardMode()}).
326 *
327 * @param clazz
328 * an EClass
329 * @return the collection of {@link EObject} direct instances of the given EClass (not of subclasses)
330 *
331 * @see #getAllInstances(EClass)
332 */
333 public Set<EObject> getDirectInstances(EClass clazz);
334
335 /**
336 * Get the all {@link EObject} instances of the given {@link EClass}.
337 * This includes instances of subclasses.
338 *
339 * <p>
340 * <strong>Precondition:</strong> Results will be returned only if either (a) the EClass (or any superclass) has
341 * already been registered using {@link #registerEClasses(Set)}, or (b) running in <em>wildcard mode</em> (see
342 * {@link #isInWildcardMode()}).
343 *
344 * @param clazz
345 * an EClass
346 * @return the collection of {@link EObject} instances of the given EClass and any of its subclasses
347 *
348 * @see #getDirectInstances(EClass)
349 */
350 public Set<EObject> getAllInstances(EClass clazz);
351
352 /**
353 * Checks whether the given {@link EObject} is an instance of the given {@link EClass}.
354 * This includes instances of subclasses.
355 * <p> Special note: this method does not check whether the object is indexed in the scope,
356 * and will return true for out-of-scope objects as well (as long as they are instances of the class).
357 * <p> The given class does not have to be indexed.
358 * <p> The difference between this method and {@link EClassifier#isInstance(Object)} is that in dynamic EMF mode, EPackage equivalence is taken into account.
359 * @since 1.6
360 */
361 public boolean isInstanceOfUnscoped(EObject object, EClass clazz);
362
363 /**
364 * Checks whether the given {@link EObject} is an instance of the given {@link EClass}.
365 * This includes instances of subclasses.
366 * <p> Special note: this method does check whether the object is indexed in the scope,
367 * and will return false for out-of-scope objects as well (as long as they are instances of the class).
368 * <p> The given class does have to be indexed.
369 * @since 1.7
370 */
371 public boolean isInstanceOfScoped(EObject object, EClass clazz);
372
373 /**
374 * Get the total number of instances of the given {@link EClass} and all of its subclasses.
375 *
376 * @since 1.4
377 */
378 public int countAllInstances(EClass clazz);
379
380 /**
381 * Find all source {@link EObject}s for which the given <code>feature</code> points to / takes the given <code>value</code>.
382 *
383 * <p>
384 * <strong>Precondition:</strong> Unset / null-valued features are not indexed, so <code>value!=null</code>
385 *
386 * <p>
387 * <strong>Precondition:</strong> Results will be returned only if either (a) the feature has already been
388 * registered using {@link #registerEStructuralFeatures(Set)}, or (b) running in <em>wildcard mode</em> (see
389 * {@link #isInWildcardMode()}).
390 *
391 * @param value
392 * the value of the feature
393 * @param feature
394 * the feature instance
395 * @return the collection of {@link EObject} instances
396 */
397 public Set<EObject> findByFeatureValue(Object value, EStructuralFeature feature);
398
399 /**
400 * Returns those host {@link EObject}s that have a non-null value for the given feature
401 * (at least one, in case of multi-valued references).
402 *
403 * <p>
404 * Unset / null-valued features are not indexed, and will not be included in the results.
405 *
406 * <p>
407 * <strong>Precondition:</strong> Results will be returned only if either (a) the feature has already been
408 * registered using {@link #registerEStructuralFeatures(Set)}, or (b) running in <em>wildcard mode</em> (see
409 * {@link #isInWildcardMode()}).
410 *
411 * @param feature
412 * a structural feature
413 * @return the collection of {@link EObject}s that have some value for the given structural feature
414 */
415 public Set<EObject> getHoldersOfFeature(EStructuralFeature feature);
416 /**
417 * Returns all non-null values that the given feature takes at least once for any {@link EObject} in the scope
418 *
419 * <p>
420 * Unset / null-valued features are not indexed, and will not be included in the results.
421 *
422 * <p>
423 * <strong>Precondition:</strong> Results will be returned only if either (a) the feature has already been
424 * registered using {@link #registerEStructuralFeatures(Set)}, or (b) running in <em>wildcard mode</em> (see
425 * {@link #isInWildcardMode()}).
426 *
427 * @param feature
428 * a structural feature
429 * @return the collection of values that the given structural feature takes
430 * @since 2.1
431 */
432 public Set<Object> getValuesOfFeature(EStructuralFeature feature);
433
434 /**
435 * Call this method to dispose the NavigationHelper.
436 *
437 * <p>After its disposal, the NavigationHelper will no longer listen to EMF change notifications,
438 * and it will be possible to GC it even if the model is retained in memory.
439 *
440 * <dt><b>Precondition:</b><dd> no listeners can be registered at all.
441 * @throws IllegalStateException if there are any active listeners
442 *
443 */
444 public void dispose();
445
446 /**
447 * The given <code>listener</code> will be notified from now on whenever instances the given {@link EClass}es
448 * (and any of their subtypes) are added to or removed from the model.
449 *
450 * <br/>
451 * <b>Important</b>: Do not call this method from {@link InstanceListener} methods as it may cause a
452 * {@link ConcurrentModificationException}, if you want to add a listener
453 * at that point, wrap the call with {@link #executeAfterTraversal(Runnable)}.
454 *
455 * @param classes
456 * the collection of classes whose instances the listener should be notified of
457 * @param listener
458 * the listener instance
459 */
460 public void addInstanceListener(Collection<EClass> classes, InstanceListener listener);
461
462 /**
463 * Unregisters an instance listener for the given classes.
464 *
465 * <br/>
466 * <b>Important</b>: Do not call this method from {@link InstanceListener} methods as it may cause a
467 * {@link ConcurrentModificationException}, if you want to remove a listener at that point, wrap the call with
468 * {@link #executeAfterTraversal(Runnable)}.
469 *
470 * @param classes
471 * the collection of classes
472 * @param listener
473 * the listener instance
474 */
475 public void removeInstanceListener(Collection<EClass> classes, InstanceListener listener);
476
477 /**
478 * The given <code>listener</code> will be notified from now on whenever instances the given {@link EDataType}s are
479 * added to or removed from the model.
480 *
481 * <br/>
482 * <b>Important</b>: Do not call this method from {@link DataTypeListener} methods as it may cause a
483 * {@link ConcurrentModificationException}, if you want to add a listener at that point, wrap the call with
484 * {@link #executeAfterTraversal(Runnable)}.
485 *
486 * @param types
487 * the collection of types associated to the listener
488 * @param listener
489 * the listener instance
490 */
491 public void addDataTypeListener(Collection<EDataType> types, DataTypeListener listener);
492
493 /**
494 * Unregisters a data type listener for the given types.
495 *
496 * <br/>
497 * <b>Important</b>: Do not call this method from {@link DataTypeListener} methods as it may cause a
498 * {@link ConcurrentModificationException}, if you want to remove a listener at that point, wrap the call with
499 * {@link #executeAfterTraversal(Runnable)}.
500 *
501 * @param types
502 * the collection of data types
503 * @param listener
504 * the listener instance
505 */
506 public void removeDataTypeListener(Collection<EDataType> types, DataTypeListener listener);
507
508 /**
509 * The given <code>listener</code> will be notified from now on whenever instances the given
510 * {@link EStructuralFeature}s are added to or removed from the model.
511 *
512 * <br/>
513 * <b>Important</b>: Do not call this method from {@link FeatureListener} methods as it may cause a
514 * {@link ConcurrentModificationException}, if you want to add a listener at that point, wrap the call with
515 * {@link #executeAfterTraversal(Runnable)}.
516 *
517 * @param features
518 * the collection of features associated to the listener
519 * @param listener
520 * the listener instance
521 */
522 public void addFeatureListener(Collection<? extends EStructuralFeature> features, FeatureListener listener);
523
524 /**
525 * Unregisters a feature listener for the given features.
526 *
527 * <br/>
528 * <b>Important</b>: Do not call this method from {@link FeatureListener} methods as it may cause a
529 * {@link ConcurrentModificationException}, if you want to remove a listener at that point, wrap the call with
530 * {@link #executeAfterTraversal(Runnable)}.
531 *
532 * @param listener
533 * the listener instance
534 * @param features
535 * the collection of features
536 */
537 public void removeFeatureListener(Collection<? extends EStructuralFeature> features, FeatureListener listener);
538
539 /**
540 * Register a lightweight observer that is notified if the value of any feature of the given EObject changes.
541 *
542 * <br/>
543 * <b>Important</b>: Do not call this method from {@link LightweightEObjectObserver} methods as it may cause a
544 * {@link ConcurrentModificationException}, if you want to add an observer at that point, wrap the call with
545 * {@link #executeAfterTraversal(Runnable)}.
546 *
547 * @param observer
548 * the listener instance
549 * @param observedObject
550 * the observed EObject
551 * @return false if the observer was already attached to the object (call has no effect), true otherwise
552 */
553 public boolean addLightweightEObjectObserver(LightweightEObjectObserver observer, EObject observedObject);
554
555 /**
556 * Unregisters a lightweight observer for the given EObject.
557 *
558 * <br/>
559 * <b>Important</b>: Do not call this method from {@link LightweightEObjectObserver} methods as it may cause a
560 * {@link ConcurrentModificationException}, if you want to remove an observer at that point, wrap the call with
561 * {@link #executeAfterTraversal(Runnable)}.
562 *
563 * @param observer
564 * the listener instance
565 * @param observedObject
566 * the observed EObject
567 * @return false if the observer has not been previously attached to the object (call has no effect), true otherwise
568 */
569 public boolean removeLightweightEObjectObserver(LightweightEObjectObserver observer, EObject observedObject);
570
571 /**
572 * Manually turns on indexing for the given types (indexing of others are unaffected). Note that
573 * registering new types will result in a single iteration through the whole attached model.
574 * <b> Not usable in <em>wildcard mode</em>.</b>
575 *
576 * @param classes
577 * the set of classes to observe (null okay)
578 * @param dataTypes
579 * the set of data types to observe (null okay)
580 * @param features
581 * the set of features to observe (null okay)
582 * @throws IllegalStateException if in wildcard mode
583 * @since 1.4
584 */
585 public void registerObservedTypes(Set<EClass> classes, Set<EDataType> dataTypes, Set<? extends EStructuralFeature> features, IndexingLevel level);
586
587 /**
588 * Manually turns off indexing for the given types (indexing of others are unaffected). Note that if the
589 * unregistered types are re-registered later, the whole attached model needs to be visited again.
590 * <b> Not usable in <em>wildcard mode</em>.</b>
591 *
592 * <dt><b>Precondition:</b><dd> no listeners can be registered for the given types.
593 * @param classes
594 * the set of classes that will be ignored again from now on (null okay)
595 * @param dataTypes
596 * the set of data types that will be ignored again from now on (null okay)
597 * @param features
598 * the set of features that will be ignored again from now on (null okay)
599 * @throws IllegalStateException if in wildcard mode, or if there are listeners registered for the given types
600 */
601 public void unregisterObservedTypes(Set<EClass> classes, Set<EDataType> dataTypes, Set<? extends EStructuralFeature> features);
602
603 /**
604 * Manually turns on indexing for the given features (indexing of other features are unaffected). Note that
605 * registering new features will result in a single iteration through the whole attached model.
606 * <b> Not usable in <em>wildcard mode</em>.</b>
607 *
608 * @param features
609 * the set of features to observe
610 * @throws IllegalStateException if in wildcard mode
611 * @since 1.4
612 */
613 public void registerEStructuralFeatures(Set<? extends EStructuralFeature> features, IndexingLevel level);
614
615 /**
616 * Manually turns off indexing for the given features (indexing of other features are unaffected). Note that if the
617 * unregistered features are re-registered later, the whole attached model needs to be visited again.
618 * <b> Not usable in <em>wildcard mode</em>.</b>
619 *
620 * <dt><b>Precondition:</b><dd> no listeners can be registered for the given features.
621 *
622 * @param features
623 * the set of features that will be ignored again from now on
624 * @throws IllegalStateException if in wildcard mode, or if there are listeners registered for the given types
625 */
626 public void unregisterEStructuralFeatures(Set<? extends EStructuralFeature> features);
627
628 /**
629 * Manually turns on indexing for the given classes (indexing of other classes are unaffected). Instances of
630 * subclasses will also be indexed. Note that registering new classes will result in a single iteration through the whole
631 * attached model.
632 * <b> Not usable in <em>wildcard mode</em>.</b>
633 *
634 * @param classes
635 * the set of classes to observe
636 * @throws IllegalStateException if in wildcard mode
637 * @since 1.4
638 */
639 public void registerEClasses(Set<EClass> classes, IndexingLevel level);
640
641 /**
642 * Manually turns off indexing for the given classes (indexing of other classes are unaffected). Note that if the
643 * unregistered classes are re-registered later, the whole attached model needs to be visited again.
644 * <b> Not usable in <em>wildcard mode</em>.</b>
645 *
646 * <dt><b>Precondition:</b><dd> no listeners can be registered for the given classes.
647 * @param classes
648 * the set of classes that will be ignored again from now on
649 * @throws IllegalStateException if in wildcard mode, or if there are listeners registered for the given types
650 */
651 public void unregisterEClasses(Set<EClass> classes);
652
653 /**
654 * Manually turns on indexing for the given data types (indexing of other features are unaffected). Note that
655 * registering new data types will result in a single iteration through the whole attached model.
656 * <b> Not usable in <em>wildcard mode</em>.</b>
657 *
658 * @param dataTypes
659 * the set of data types to observe
660 * @throws IllegalStateException if in wildcard mode
661 * @since 1.4
662 */
663 public void registerEDataTypes(Set<EDataType> dataTypes, IndexingLevel level);
664
665 /**
666 * Manually turns off indexing for the given data types (indexing of other data types are unaffected). Note that if
667 * the unregistered data types are re-registered later, the whole attached model needs to be visited again.
668 * <b> Not usable in <em>wildcard mode</em>.</b>
669 *
670 * <dt><b>Precondition:</b><dd> no listeners can be registered for the given datatypes.
671 *
672 * @param dataTypes
673 * the set of data types that will be ignored again from now on
674 * @throws IllegalStateException if in wildcard mode, or if there are listeners registered for the given types
675 */
676 public void unregisterEDataTypes(Set<EDataType> dataTypes);
677
678 /**
679 * The given callback will be executed, and all model traversals and index registrations will be delayed until the
680 * execution is done. If there are any outstanding feature, class or datatype registrations, a single coalesced model
681 * traversal will initialize the caches and deliver the notifications.
682 *
683 * @param callable
684 */
685 public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException;
686
687 /**
688 * Execute the given runnable after traversal. It is guaranteed that the runnable is executed as soon as
689 * the indexing is finished. The callback is executed only once, then is removed from the callback queue.
690 * @param traversalCallback
691 * @throws InvocationTargetException
692 * @since 1.4
693 */
694 public void executeAfterTraversal(Runnable traversalCallback) throws InvocationTargetException;
695
696 /**
697 * Examines whether execution is currently in the callable
698 * block of an invocation of {#link {@link #coalesceTraversals(Callable)}}.
699 */
700 public boolean isCoalescing();
701
702 /**
703 * Adds a coarse-grained listener that will be invoked after the NavigationHelper index or the underlying model is changed. Can be used
704 * e.g. to check model contents. Not intended for general use.
705 *
706 * <p/> See {@link #removeBaseIndexChangeListener(EMFBaseIndexChangeListener)}
707 * @param listener
708 */
709 public void addBaseIndexChangeListener(EMFBaseIndexChangeListener listener);
710
711 /**
712 * Removes a registered listener.
713 *
714 * <p/> See {@link #addBaseIndexChangeListener(EMFBaseIndexChangeListener)}
715 *
716 * @param listener
717 */
718 public void removeBaseIndexChangeListener(EMFBaseIndexChangeListener listener);
719
720 /**
721 * Adds an additional EMF model root.
722 *
723 * @param emfRoot
724 * @throws ViatraQueryRuntimeException
725 */
726 public void addRoot(Notifier emfRoot);
727
728 /**
729 * Moves an EObject (along with its entire containment subtree) within the containment hierarchy of the EMF model.
730 * The object will be relocated from the original parent object to a different parent, or a different containment
731 * list of the same parent.
732 *
733 * <p> When indexing is enabled, such a relocation is costly if performed through normal getters/setters, as the index
734 * for the entire subtree is pruned at the old location and reconstructed at the new one.
735 * This method provides a workaround to keep the operation cheap.
736 *
737 * <p> This method is experimental. Re-entrancy not supported.
738 *
739 * @param element the eObject to be moved
740 * @param targetContainmentReferenceList containment list of the new parent object into which the element has to be moved
741 *
742 */
743 public <T extends EObject> void cheapMoveTo(T element, EList<T> targetContainmentReferenceList);
744
745 /**
746 * Moves an EObject (along with its entire containment subtree) within the containment hierarchy of the EMF model.
747 * The object will be relocated from the original parent object to a different parent, or a different containment
748 * list of the same parent.
749 *
750 * <p> When indexing is enabled, such a relocation is costly if performed through normal getters/setters, as the index
751 * for the entire subtree is pruned at the old location and reconstructed at the new one.
752 * This method provides a workaround to keep the operation cheap.
753 *
754 * <p> This method is experimental. Re-entrancy not supported.
755 *
756 * @param element the eObject to be moved
757 * @param parent the new parent object under which the element has to be moved
758 * @param containmentFeature the kind of containment reference that should be established between the new parent and the element
759 *
760 */
761 public void cheapMoveTo(EObject element, EObject parent, EReference containmentFeature);
762
763
764 /**
765 * Traverses all instances of a selected data type stored in the base index, and allows executing a custom function on
766 * it. There is no guaranteed order in which the processor will be called with the selected features.
767 *
768 * @param type
769 * @param processor
770 * @since 0.8
771 */
772 void processDataTypeInstances(EDataType type, IEDataTypeProcessor processor);
773
774 /**
775 * Traverses all direct instances of a selected class stored in the base index, and allows executing a custom function on
776 * it. There is no guaranteed order in which the processor will be called with the selected features.
777 *
778 * @param type
779 * @param processor
780 * @since 0.8
781 */
782 void processAllInstances(EClass type, IEClassProcessor processor);
783
784 /**
785 * Traverses all direct instances of a selected class stored in the base index, and allows executing a custom function on
786 * it. There is no guaranteed order in which the processor will be called with the selected features.
787 *
788 * @param type
789 * @param processor
790 * @since 0.8
791 */
792 void processDirectInstances(EClass type, IEClassProcessor processor);
793
794 /**
795 * Traverses all instances of a selected feature stored in the base index, and allows executing a custom function on
796 * it. There is no guaranteed order in which the processor will be called with the selected features.
797 *
798 * <p>
799 * <strong>Precondition:</strong> Will only find those {@link EStructuralFeature}s that have already been registered using
800 * {@link #registerEStructuralFeatures(Set)}, unless running in <em>wildcard mode</em> (see
801 * {@link #isInWildcardMode()}).
802 *
803 * @since 1.7
804 */
805 void processAllFeatureInstances(EStructuralFeature feature, IStructuralFeatureInstanceProcessor processor);
806 /**
807 * Returns all EClasses that currently have direct instances cached by the index. <ul>
808 * <li> Supertypes will not be returned, unless they have direct instances in the model as well.
809 * <li> If not in <em>wildcard mode</em>, only registered EClasses and their subtypes will be considered.
810 * <li> Note for advanced users: if a type is represented by multiple EClass objects, one of them is chosen as representative and returned.
811 * </ul>
812 */
813 public Set<EClass> getAllCurrentClasses();
814
815 /**
816 * Updates the value of indexed derived features that are not well-behaving.
817 */
818 void resampleDerivedFeatures();
819
820 /**
821 * Adds a listener for internal errors in the index. A listener can only be added once.
822 *
823 * @param listener
824 * @returns true if the listener was not already added
825 * @since 0.8
826 */
827 boolean addIndexingErrorListener(IEMFIndexingErrorListener listener);
828
829 /**
830 * Removes a listener for internal errors in the index.
831 *
832 * @param listener
833 * @returns true if the listener was successfully removed (e.g. it did exist)
834 * @since 0.8
835 */
836 boolean removeIndexingErrorListener(IEMFIndexingErrorListener listener);
837
838 /**
839 * Returns the internal, canonicalized implementation of an attribute value.
840 *
841 * <p> Behaviour: when in dynamic EMF mode, substitutes enum literals with a canonical version of the enum literal.
842 * Otherwise, returns the input.
843 *
844 * <p> The canonical enum literal will be guaranteed to be a valid EMF enum literal ({@link Enumerator}),
845 * and the best effort is made to ensure that it will be the same for all versions of the {@link EEnum},
846 * including {@link EEnumLiteral}s in different versions of ecore packages, as well as Java enums generated from them..
847 *
848 * <p> Usage is not required when simply querying the indexed model through the {@link NavigationHelper} API,
849 * as both method inputs and the results returned are automatically canonicalized in dynamic EMF mode.
850 * Using this method is required only if the client wants to do querying/filtering on the results returned, and wants to know what to look for.
851 */
852 Object toCanonicalValueRepresentation(Object value);
853
854 /**
855 * @since 1.4
856 */
857 IndexingLevel getIndexingLevel(EClass type);
858
859 /**
860 * @since 1.4
861 */
862 IndexingLevel getIndexingLevel(EDataType type);
863
864 /**
865 * @since 1.4
866 */
867 IndexingLevel getIndexingLevel(EStructuralFeature feature);
868
869 /**
870 * @since 1.4
871 */
872 public int countDataTypeInstances(EDataType dataType);
873
874 /**
875 * @since 1.4
876 */
877 public int countFeatureTargets(EObject seedSource, EStructuralFeature feature);
878
879 /**
880 * @since 1.4
881 */
882 public int countFeatures(EStructuralFeature feature);
883
884
885}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/QueryResultAssociativeStore.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/QueryResultAssociativeStore.java
new file mode 100644
index 00000000..27d08506
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/QueryResultAssociativeStore.java
@@ -0,0 +1,322 @@
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.base.api;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.HashSet;
14import java.util.Iterator;
15import java.util.Map.Entry;
16
17import org.apache.log4j.Logger;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19
20/**
21 * @author Abel Hegedus
22 *
23 */
24public abstract class QueryResultAssociativeStore<KeyType, ValueType> {
25 /**
26 * Error literal returned when associative store modification is attempted without a setter available
27 */
28 protected static final String NOT_ALLOW_MODIFICATIONS = "Query result associative store does not allow modifications";
29
30 /**
31 * Logger that can be used for reporting errors during runtime
32 */
33 private Logger logger;
34 /**
35 * The collection of listeners registered for this result associative store
36 */
37 private Collection<IQueryResultUpdateListener<KeyType, ValueType>> listeners;
38
39 /**
40 * The setter registered for changing the contents of the associative store
41 */
42 private IQueryResultSetter<KeyType, ValueType> setter;
43
44 /**
45 * @return the listeners
46 */
47 protected Collection<IQueryResultUpdateListener<KeyType, ValueType>> getListeners() {
48 return listeners;
49 }
50
51 /**
52 * @param listeners the listeners to set
53 */
54 protected void setListeners(Collection<IQueryResultUpdateListener<KeyType, ValueType>> listeners) {
55 this.listeners = listeners;
56 }
57
58 /**
59 * @return the setter
60 */
61 protected IQueryResultSetter<KeyType, ValueType> getSetter() {
62 return setter;
63 }
64
65 /**
66 * @param setter the setter to set
67 */
68 protected void setSetter(IQueryResultSetter<KeyType, ValueType> setter) {
69 this.setter = setter;
70 }
71
72 /**
73 * @param logger the logger to set
74 */
75 protected void setLogger(Logger logger) {
76 this.logger = logger;
77 }
78
79 /**
80 * Returns the entries in the cache as a collection.
81 * @return the entries
82 */
83 protected abstract Collection<Entry<KeyType, ValueType>> getCacheEntries();
84
85 /**
86 * Registers a listener for this query result associative store that is invoked every time when a key-value pair is inserted
87 * or removed from the associative store.
88 *
89 * <p>
90 * The listener can be unregistered via {@link #removeCallbackOnQueryResultUpdate(IQueryResultUpdateListener)}.
91 *
92 * @param listener
93 * the listener that will be notified of each key-value pair that is inserted or removed, starting from
94 * now.
95 * @param fireNow
96 * if true, notifyPut will be immediately invoked on all current key-values as a one-time effect.
97 */
98 public void addCallbackOnQueryResultUpdate(IQueryResultUpdateListener<KeyType, ValueType> listener, boolean fireNow) {
99 if (listeners == null) {
100 listeners = new HashSet<IQueryResultUpdateListener<KeyType, ValueType>>();
101 }
102 listeners.add(listener);
103 if(fireNow) {
104 for (Entry<KeyType, ValueType> entry : getCacheEntries()) {
105 sendNotificationToListener(Direction.INSERT, entry.getKey(), entry.getValue(), listener);
106 }
107 }
108 }
109
110 /**
111 * Unregisters a callback registered by {@link #addCallbackOnQueryResultUpdate(IQueryResultUpdateListener, boolean)}
112 * .
113 *
114 * @param listener
115 * the listener that will no longer be notified.
116 */
117 public void removeCallbackOnQueryResultUpdate(IQueryResultUpdateListener<KeyType, ValueType> listener) {
118 if (listeners != null) {
119 listeners.remove(listener);
120 }
121 }
122
123 /**
124 * This method notifies the listeners that the query result associative store has changed.
125 *
126 * @param direction
127 * the type of the change (insert or delete)
128 * @param key
129 * the key of the pair that changed
130 * @param value
131 * the value of the pair that changed
132 */
133 protected void notifyListeners(Direction direction, KeyType key, ValueType value) {
134 if(listeners != null) {
135 for (IQueryResultUpdateListener<KeyType, ValueType> listener : listeners) {
136 sendNotificationToListener(direction, key, value, listener);
137 }
138 }
139 }
140
141 private void sendNotificationToListener(Direction direction, KeyType key, ValueType value,
142 IQueryResultUpdateListener<KeyType, ValueType> listener) {
143 try {
144 if (direction == Direction.INSERT) {
145 listener.notifyPut(key, value);
146 } else {
147 listener.notifyRemove(key, value);
148 }
149 } catch (Exception e) { // NOPMD
150 logger.warn(
151 String.format(
152 "The query result associative store encountered an error during executing a callback on %s of key %s and value %s. Error message: %s. (Developer note: %s in %s called from QueryResultMultimap)",
153 direction == Direction.INSERT ? "insertion" : "removal", key, value, e.getMessage(), e
154 .getClass().getSimpleName(), listener), e);
155 throw new IllegalStateException("The query result associative store encountered an error during invoking setter",e);
156 }
157 }
158
159 /**
160 * Implementations of QueryResultAssociativeStore can put a new key-value pair into the associative store with this method. If the
161 * insertion of the key-value pair results in a change, the listeners are notified.
162 *
163 * <p>
164 * No validation or null-checking is performed during the method!
165 *
166 * @param key
167 * the key which identifies where the new value is put
168 * @param value
169 * the value that is put into the collection of the key
170 * @return true, if the insertion resulted in a change (the key-value pair was not yet in the associative store)
171 */
172 protected boolean internalPut(KeyType key, ValueType value){
173 boolean putResult = internalCachePut(key, value);
174 if (putResult) {
175 notifyListeners(Direction.INSERT, key, value);
176 }
177 return putResult;
178 }
179 /**
180 * Implementations of QueryResultAssociativeStore can remove a key-value pair from the associative store with this method. If the
181 * removal of the key-value pair results in a change, the listeners are notified.
182 *
183 * <p>
184 * No validation or null-checking is performed during the method!
185 *
186 * @param key
187 * the key which identifies where the value is removed from
188 * @param value
189 * the value that is removed from the collection of the key
190 * @return true, if the removal resulted in a change (the key-value pair was in the associative store)
191 */
192 protected boolean internalRemove(KeyType key, ValueType value){
193 boolean removeResult = internalCacheRemove(key, value);
194 if (removeResult) {
195 notifyListeners(Direction.DELETE, key, value);
196 }
197 return removeResult;
198 }
199
200
201 protected abstract boolean internalCachePut(KeyType key, ValueType value);
202 protected abstract boolean internalCacheRemove(KeyType key, ValueType value);
203 protected abstract int internalCacheSize();
204 protected abstract boolean internalCacheContainsEntry(KeyType key, ValueType value);
205
206 /**
207 * @param setter
208 * the setter to set
209 */
210 public void setQueryResultSetter(IQueryResultSetter<KeyType, ValueType> setter) {
211 this.setter = setter;
212 }
213
214 /**
215 * @return the logger
216 */
217 protected Logger getLogger() {
218 return logger;
219 }
220
221 protected void internalClear() {
222 if (setter == null) {
223 throw new UnsupportedOperationException(NOT_ALLOW_MODIFICATIONS);
224 }
225 Collection<Entry<KeyType, ValueType>> entries = new ArrayList<>(getCacheEntries());
226 Iterator<Entry<KeyType, ValueType>> iterator = entries.iterator();
227 while (iterator.hasNext()) {
228 Entry<KeyType, ValueType> entry = iterator.next();
229 modifyThroughQueryResultSetter(entry.getKey(), entry.getValue(), Direction.DELETE);
230 }
231 if (internalCacheSize() != 0) {
232 StringBuilder sb = new StringBuilder();
233 for (Entry<KeyType, ValueType> entry : getCacheEntries()) {
234 if (sb.length() > 0) {
235 sb.append(", ");
236 }
237 sb.append(entry.toString());
238 }
239 logger.warn(String
240 .format("The query result associative store is not empty after clear, remaining entries: %s. (Developer note: %s called from QueryResultMultimap)",
241 sb.toString(), setter));
242 }
243 }
244
245 /**
246 * This method is used for calling the query result setter to put or remove a value by modifying the model.
247 *
248 * <p>
249 * The given key-value pair is first validated (see {@link IQueryResultSetter#validate(Object, Object)}, then the
250 * put or remove method is called (see {@link IQueryResultSetter#put(Object, Object)} and
251 * {@link IQueryResultSetter#remove(Object, Object)}). If the setter reported that the model has been changed, the
252 * change is checked.
253 *
254 * <p>
255 * If the model modification did not change the result set in the desired way, a warning is logged.
256 *
257 * <p>
258 * If the setter throws any {@link Throwable}, it is either rethrown in case of {@link Error} and logged otherwise.
259 *
260 *
261 * @param key
262 * the key of the pair to be inserted or removed
263 * @param value
264 * the value of the pair to be inserted or removed
265 * @param direction
266 * specifies whether a put or a remove is performed
267 * @return true, if the associative store changed according to the direction
268 */
269 protected boolean modifyThroughQueryResultSetter(KeyType key, ValueType value, Direction direction) {
270 try {
271 if (setter.validate(key, value)) {
272 final int size = internalCacheSize();
273 final int expectedChange = (direction == Direction.INSERT) ? 1 : -1;
274 boolean changed = false;
275 if (direction == Direction.INSERT) {
276 changed = setter.put(key, value);
277 } else {
278 changed = setter.remove(key, value);
279 }
280 if (changed) {
281 return checkModificationThroughQueryResultSetter(key, value, direction, expectedChange, size);
282 } else {
283 logger.warn(String
284 .format("The query result associative store %s of key %s and value %s resulted in %s. (Developer note: %s called from QueryResultMultimap)",
285 direction == Direction.INSERT ? "insertion" : "removal", key, value,
286 Math.abs(internalCacheSize() - size) > 1 ? "more than one changed result"
287 : "no changed results", setter));
288 }
289 }
290 } catch (Exception e) { // NOPMD
291 logger.warn(
292 String.format(
293 "The query result associative store encountered an error during invoking setter on %s of key %s and value %s. Error message: %s. (Developer note: %s in %s called from QueryResultMultimap)",
294 direction == Direction.INSERT ? "insertion" : "removal", key, value, e.getMessage(), e
295 .getClass().getSimpleName(), setter), e);
296 throw new IllegalStateException("The query result associative store encountered an error during invoking setter",e);
297 }
298
299 return false;
300 }
301
302 /**
303 * Checks whether the model modification performed by the {@link IQueryResultSetter} resulted in the insertion or
304 * removal of exactly the required key-value pair.
305 *
306 * @param key
307 * the key for the pair that was inserted or removed
308 * @param value
309 * the value for the pair that was inserted or removed
310 * @param direction
311 * the direction of the change
312 * @param size
313 * the size of the cache before the change
314 * @return true, if the changes made by the query result setter were correct
315 */
316 protected boolean checkModificationThroughQueryResultSetter(KeyType key, ValueType value, Direction direction,
317 final int expectedChange, final int size) {
318 boolean isInsertion = direction == Direction.INSERT;
319 return (isInsertion == internalCacheContainsEntry(key, value)
320 && (internalCacheSize() - expectedChange) == size);
321 }
322}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/QueryResultMap.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/QueryResultMap.java
new file mode 100644
index 00000000..a106ea71
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/QueryResultMap.java
@@ -0,0 +1,210 @@
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.base.api;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.Map;
15import java.util.Set;
16
17import org.apache.log4j.Logger;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19
20/**
21 * @author Abel Hegedus
22 *
23 */
24public abstract class QueryResultMap<KeyType,ValueType> extends QueryResultAssociativeStore<KeyType, ValueType> implements Map<KeyType, ValueType> {
25
26 /**
27 * This map contains the current key-values. Implementing classes should not modify it directly
28 */
29 private Map<KeyType, ValueType> cache;
30
31 /**
32 * Constructor only visible to subclasses.
33 *
34 * @param logger
35 * a logger that can be used for error reporting
36 */
37 protected QueryResultMap(Logger logger) {
38 cache = new HashMap<KeyType, ValueType>();
39 setLogger(logger);
40 }
41
42 @Override
43 protected Collection<java.util.Map.Entry<KeyType, ValueType>> getCacheEntries() {
44 return cache.entrySet();
45 }
46
47 @Override
48 protected boolean internalCachePut(KeyType key, ValueType value) {
49 ValueType put = cache.put(key, value);
50 if(put == null) {
51 return value != null;
52 } else {
53 return !put.equals(value);
54 }
55 }
56
57 @Override
58 protected boolean internalCacheRemove(KeyType key, ValueType value) {
59 ValueType remove = cache.remove(key);
60 return remove != null;
61 }
62
63 @Override
64 protected int internalCacheSize() {
65 return cache.size();
66 }
67
68 @Override
69 protected boolean internalCacheContainsEntry(KeyType key, ValueType value) {
70 return cache.containsKey(key) && cache.get(key).equals(value);
71 }
72
73 /**
74 * @return the cache
75 */
76 protected Map<KeyType, ValueType> getCache() {
77 return cache;
78 }
79
80 /**
81 * @param cache
82 * the cache to set
83 */
84 protected void setCache(Map<KeyType, ValueType> cache) {
85 this.cache = cache;
86 }
87
88 // ======================= implemented Map methods ======================
89
90 @Override
91 public void clear() {
92 internalClear();
93 }
94
95 @Override
96 public boolean containsKey(Object key) {
97 return cache.containsKey(key);
98 }
99
100 @Override
101 public boolean containsValue(Object value) {
102 return cache.containsValue(value);
103 }
104
105 /**
106 * {@inheritDoc}
107 *
108 * <p>
109 * The returned set is immutable.
110 *
111 */
112 @Override
113 public Set<Entry<KeyType, ValueType>> entrySet() {
114 return Collections.unmodifiableSet((Set<Entry<KeyType, ValueType>>) getCacheEntries());
115 }
116
117 @Override
118 public ValueType get(Object key) {
119 return cache.get(key);
120 }
121
122 @Override
123 public boolean isEmpty() {
124 return cache.isEmpty();
125 }
126
127 /**
128 * {@inheritDoc}
129 *
130 * <p>
131 * The returned set is immutable.
132 *
133 */
134 @Override
135 public Set<KeyType> keySet() {
136 return Collections.unmodifiableSet(cache.keySet());
137 }
138
139 /**
140 * {@inheritDoc}
141 *
142 * <p>
143 * Throws {@link UnsupportedOperationException} if there is no {@link IQueryResultSetter}
144 */
145 @Override
146 public ValueType put(KeyType key, ValueType value) {
147 if (getSetter() == null) {
148 throw new UnsupportedOperationException(NOT_ALLOW_MODIFICATIONS);
149 }
150 ValueType oldValue = cache.get(key);
151 boolean modified = modifyThroughQueryResultSetter(key, value, Direction.INSERT);
152 return modified ? oldValue : null;
153 }
154
155 /**
156 * {@inheritDoc}
157 *
158 * <p>
159 * Throws {@link UnsupportedOperationException} if there is no {@link IQueryResultSetter}
160 */
161 @Override
162 public void putAll(Map<? extends KeyType, ? extends ValueType> map) {
163 if (getSetter() == null) {
164 throw new UnsupportedOperationException(NOT_ALLOW_MODIFICATIONS);
165 }
166 for (Entry<? extends KeyType, ? extends ValueType> entry : map.entrySet()) {
167 modifyThroughQueryResultSetter(entry.getKey(), entry.getValue(), Direction.INSERT);
168 }
169 return;
170 }
171
172 /**
173 * {@inheritDoc}
174 *
175 * <p>
176 * Throws {@link UnsupportedOperationException} if there is no {@link IQueryResultSetter}
177 */
178 @SuppressWarnings("unchecked")
179 @Override
180 public ValueType remove(Object key) {
181 if (getSetter() == null) {
182 throw new UnsupportedOperationException(NOT_ALLOW_MODIFICATIONS);
183 }
184 // if it contains the entry, the types MUST be correct
185 if (cache.containsKey(key)) {
186 ValueType value = cache.get(key);
187 modifyThroughQueryResultSetter((KeyType) key, value, Direction.DELETE);
188 return value;
189 }
190 return null;
191 }
192
193 @Override
194 public int size() {
195 return internalCacheSize();
196 }
197
198 /**
199 * {@inheritDoc}
200 *
201 * <p>
202 * The returned collection is immutable.
203 *
204 */
205 @Override
206 public Collection<ValueType> values() {
207 return Collections.unmodifiableCollection(cache.values());
208 }
209
210}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/TransitiveClosureHelper.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/TransitiveClosureHelper.java
new file mode 100644
index 00000000..fc92fef3
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/TransitiveClosureHelper.java
@@ -0,0 +1,26 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.api;
11
12import org.eclipse.emf.ecore.EObject;
13import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource;
14
15/**
16 * The class can be used to compute the transitive closure of a given emf model, where the nodes will be the objects in
17 * the model and the edges will be represented by the references between them. One must provide the set of references
18 * that the helper should treat as edges when creating an instance with the factory: only the notifications about these
19 * references will be handled.
20 *
21 * @author Tamas Szabo
22 *
23 */
24public interface TransitiveClosureHelper extends ITcDataSource<EObject> {
25
26}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/ViatraBaseFactory.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/ViatraBaseFactory.java
new file mode 100644
index 00000000..81bd4f35
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/ViatraBaseFactory.java
@@ -0,0 +1,180 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.api;
11
12import java.util.Set;
13
14import org.apache.log4j.Logger;
15import org.eclipse.emf.common.notify.Notifier;
16import org.eclipse.emf.ecore.EClass;
17import org.eclipse.emf.ecore.EDataType;
18import org.eclipse.emf.ecore.EReference;
19import org.eclipse.emf.ecore.EStructuralFeature;
20import tools.refinery.viatra.runtime.base.core.NavigationHelperImpl;
21import tools.refinery.viatra.runtime.base.core.TransitiveClosureHelperImpl;
22
23/**
24 * Factory class for the utils in the library: <ul>
25 * <li>NavigationHelper (automatic and manual)
26 * <li>TransitiveClosureUtil
27 * </ul>
28 *
29 * @author Tamas Szabo
30 *
31 */
32public class ViatraBaseFactory {
33
34 private static ViatraBaseFactory instance;
35
36 /**
37 * Get the singleton instance of ViatraBaseFactory.
38 *
39 * @return the singleton instance
40 */
41 public static synchronized ViatraBaseFactory getInstance() {
42 if (instance == null) {
43 instance = new ViatraBaseFactory();
44 }
45
46 return instance;
47 }
48
49 protected ViatraBaseFactory() {
50 super();
51 }
52
53 /**
54 * The method creates a {@link NavigationHelper} index for the given EMF model root.
55 * A new instance will be created on every call.
56 * <p>
57 * A NavigationHelper in wildcard mode will process and index all EStructuralFeatures, EClasses and EDatatypes. If
58 * wildcard mode is off, the client will have to manually register the interesting aspects of the model.
59 * <p>
60 * The NavigationHelper will be created without dynamic EMF support by default.
61 * See {@link #createNavigationHelper(Notifier, boolean, boolean, Logger)} for more options.
62 *
63 * @see NavigationHelper
64 *
65 * @param emfRoot
66 * the root of the EMF tree to be indexed. Recommended: Resource or ResourceSet. Can be null - you can
67 * add a root later using {@link NavigationHelper#addRoot(Notifier)}
68 * @param wildcardMode
69 * true if all aspects of the EMF model should be indexed automatically, false if manual registration of
70 * interesting aspects is desirable
71 * @param logger
72 * the log output where errors will be logged if encountered during the operation of the
73 * NavigationHelper; if null, the default logger for {@link NavigationHelper} is used.
74 * @return the NavigationHelper instance
75 * @throws ViatraQueryRuntimeException
76 */
77 public NavigationHelper createNavigationHelper(Notifier emfRoot, boolean wildcardMode, Logger logger) {
78 BaseIndexOptions options = new BaseIndexOptions(false, wildcardMode ? IndexingLevel.FULL : IndexingLevel.NONE);
79 return createNavigationHelper(emfRoot, options, logger);
80 }
81
82 /**
83 * The method creates a {@link NavigationHelper} index for the given EMF model root.
84 * A new instance will be created on every call.
85 * <p>
86 * A NavigationHelper in wildcard mode will process and index all EStructuralFeatures, EClasses and EDatatypes. If
87 * wildcard mode is off, the client will have to manually register the interesting aspects of the model.
88 * <p>
89 * If the dynamic model flag is set to true, the index will use String ids to distinguish between the various
90 * {@link EStructuralFeature}, {@link EClass} and {@link EDataType} instances. This way the index is able to
91 * handle dynamic EMF instance models too.
92 *
93 * @see NavigationHelper
94 *
95 * @param emfRoot
96 * the root of the EMF tree to be indexed. Recommended: Resource or ResourceSet. Can be null - you can
97 * add a root later using {@link NavigationHelper#addRoot(Notifier)}
98 * @param wildcardMode
99 * true if all aspects of the EMF model should be indexed automatically, false if manual registration of
100 * interesting aspects is desirable
101 * @param dynamicModel
102 * true if the index should use String ids (nsURIs) for the various EMF types and features, and treat
103 * multiple EPackages sharing an nsURI as the same. false if dynamic model support is not required
104 * @param logger
105 * the log output where errors will be logged if encountered during the operation of the
106 * NavigationHelper; if null, the default logger for {@link NavigationHelper} is used.
107 * @return the NavigationHelper instance
108 * @throws ViatraQueryRuntimeException
109 */
110 public NavigationHelper createNavigationHelper(Notifier emfRoot, boolean wildcardMode, boolean dynamicModel, Logger logger) {
111 BaseIndexOptions options = new BaseIndexOptions(dynamicModel, wildcardMode ? IndexingLevel.FULL : IndexingLevel.NONE);
112 return createNavigationHelper(emfRoot, options, logger);
113 }
114
115 /**
116 * The method creates a {@link NavigationHelper} index for the given EMF model root.
117 * A new instance will be created on every call.
118 * <p>
119 * For details of base index options including wildcard and dynamic EMF mode, see {@link BaseIndexOptions}.
120 *
121 * @see NavigationHelper
122 *
123 * @param emfRoot
124 * the root of the EMF tree to be indexed. Recommended: Resource or ResourceSet. Can be null - you can
125 * add a root later using {@link NavigationHelper#addRoot(Notifier)}
126 * @param options the options used by the index
127 * @param logger
128 * the log output where errors will be logged if encountered during the operation of the
129 * NavigationHelper; if null, the default logger for {@link NavigationHelper} is used.
130 * @return the NavigationHelper instance
131 * @throws ViatraQueryRuntimeException
132 */
133 public NavigationHelper createNavigationHelper(Notifier emfRoot, BaseIndexOptions options, Logger logger) {
134 Logger l = logger;
135 if (l == null)
136 l = Logger.getLogger(NavigationHelper.class);
137 return new NavigationHelperImpl(emfRoot, options, l);
138 }
139
140
141
142 /**
143 * The method creates a TransitiveClosureHelper instance for the given EMF model root.
144 * A new instance will be created on every call.
145 *
146 * <p>
147 * One must specify the set of EReferences that will be considered as edges. The set can contain multiple elements;
148 * this way one can query forward and backward reachability information along heterogenous paths.
149 *
150 * @param emfRoot
151 * the root of the EMF tree to be processed. Recommended: Resource or ResourceSet.
152 * @param referencesToObserve
153 * the set of references to observe
154 * @return the TransitiveClosureHelper instance
155 * @throws ViatraQueryRuntimeException if the creation of the internal NavigationHelper failed
156 */
157 public TransitiveClosureHelper createTransitiveClosureHelper(Notifier emfRoot, Set<EReference> referencesToObserve) {
158 return new TransitiveClosureHelperImpl(getInstance().createNavigationHelper(emfRoot, false, null), true, referencesToObserve);
159 }
160
161 /**
162 * The method creates a TransitiveClosureHelper instance built on an existing NavigationHelper.
163 * A new instance will be created on every call.
164 *
165 * <p>
166 * One must specify the set of EReferences that will be considered as edges. The set can contain multiple elements;
167 * this way one can query forward and backward reachability information along heterogenous paths.
168 *
169 * @param baseIndex
170 * the already existing NavigationHelper index on the model
171 * @param referencesToObserve
172 * the set of references to observe
173 * @return the TransitiveClosureHelper instance
174 */
175 public TransitiveClosureHelper createTransitiveClosureHelper(NavigationHelper baseIndex, Set<EReference> referencesToObserve) {
176 return new TransitiveClosureHelperImpl(baseIndex, false, referencesToObserve);
177 }
178
179
180}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/IBaseIndexFeatureFilter.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/IBaseIndexFeatureFilter.java
new file mode 100644
index 00000000..8929b2ab
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/IBaseIndexFeatureFilter.java
@@ -0,0 +1,38 @@
1/**
2 * Copyright (c) 2010-2016, Peter Lunk, 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.base.api.filters;
10
11import org.eclipse.emf.ecore.EStructuralFeature;
12
13/**
14 *
15 * Defines if an {@link EStructuralFeature} should not be indexed by VIATRA Base. This filtering
16 * method should only be used if the input metamodel has certain features, that the base indexer
17 * cannot handle. If the filtered feature is a containment feature, the whole sub-tree accessible
18 * through the said feature will be filtered.
19 *
20 * Note: This API feature is for advanced users only. Usage of this feature is not encouraged,
21 * unless the filtering task is impossible via using the more straightforward
22 * {@link IBaseIndexResourceFilter} or {@link IBaseIndexObjectFilter}.
23 *
24 * @author Peter Lunk
25 * @since 1.5
26 *
27 */
28public interface IBaseIndexFeatureFilter {
29
30 /**
31 * Decides whether the selected {@link EStructuralFeature} is filtered.
32 *
33 * @param feature
34 * @return true, if the feature should not be indexed
35 */
36 boolean isFiltered(EStructuralFeature feature);
37
38} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/IBaseIndexObjectFilter.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/IBaseIndexObjectFilter.java
new file mode 100644
index 00000000..e1e46bbd
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/IBaseIndexObjectFilter.java
@@ -0,0 +1,30 @@
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.base.api.filters;
10
11import org.eclipse.emf.common.notify.Notifier;
12
13/**
14 *
15 * Stores a collection of {@link Notifier} instances that need not to be indexed by VIATRA Base.
16 *
17 * @author Zoltan Ujhelyi
18 *
19 */
20public interface IBaseIndexObjectFilter {
21
22 /**
23 * Decides whether the selected notifier is filtered.
24 *
25 * @param notifier
26 * @return true, if the notifier should not be indexed
27 */
28 boolean isFiltered(Notifier notifier);
29
30} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/IBaseIndexResourceFilter.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/IBaseIndexResourceFilter.java
new file mode 100644
index 00000000..73d3e961
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/IBaseIndexResourceFilter.java
@@ -0,0 +1,27 @@
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.base.api.filters;
10
11import org.eclipse.emf.ecore.resource.Resource;
12
13/**
14 * Defines a filter for indexing resources
15 * @author Zoltan Ujhelyi
16 *
17 */
18public interface IBaseIndexResourceFilter {
19
20 /**
21 * Decides whether a selected resource needs to be indexed
22 * @param resource
23 * @return true, if the selected resource is filtered
24 */
25 boolean isResourceFiltered(Resource resource);
26
27} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/SimpleBaseIndexFilter.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/SimpleBaseIndexFilter.java
new file mode 100644
index 00000000..9ae88a1a
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/filters/SimpleBaseIndexFilter.java
@@ -0,0 +1,46 @@
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.base.api.filters;
10
11import org.eclipse.emf.common.notify.Notifier;
12
13import java.util.Collection;
14import java.util.HashSet;
15import java.util.Set;
16
17/**
18 * An index filter that is based on a collection of {@link Notifier} instances.
19 *
20 * @author Zoltan Ujhelyi
21 *
22 */
23public class SimpleBaseIndexFilter implements IBaseIndexObjectFilter {
24
25 Set<Notifier> filters;
26
27 /**
28 * Creates a filter using a collection of (Resource and) Notifier instances. Every containment subtree, selected by
29 * the given Notifiers are filtered out.
30 *
31 * @param filterConfiguration
32 */
33 public SimpleBaseIndexFilter(Collection<Notifier> filterConfiguration) {
34 filters = new HashSet<>(filterConfiguration);
35 }
36
37 public SimpleBaseIndexFilter(SimpleBaseIndexFilter other) {
38 this(other.filters);
39 }
40
41 @Override
42 public boolean isFiltered(Notifier notifier) {
43 return filters.contains(notifier);
44 }
45
46}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/profiler/BaseIndexProfiler.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/profiler/BaseIndexProfiler.java
new file mode 100644
index 00000000..d3cc152e
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/profiler/BaseIndexProfiler.java
@@ -0,0 +1,79 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, 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.base.api.profiler;
10
11import tools.refinery.viatra.runtime.base.api.NavigationHelper;
12import tools.refinery.viatra.runtime.base.core.NavigationHelperContentAdapter;
13import tools.refinery.viatra.runtime.base.core.NavigationHelperImpl;
14import tools.refinery.viatra.runtime.base.core.profiler.ProfilingNavigationHelperContentAdapter;
15import tools.refinery.viatra.runtime.matchers.util.Preconditions;
16
17/**
18 * An index profiler can be attached to an existing navigation helper instance to access the profiling data and control
19 * the profiler itself. If the NavigationHelper was not started in profiling mode, the profiler cannot be initialized.
20 *
21 * @since 2.3
22 */
23public class BaseIndexProfiler {
24
25 ProfilingNavigationHelperContentAdapter adapter;
26
27 /**
28 *
29 * @throws IllegalArgumentException if the profiler cannot be attached to the base index instance
30 */
31 public BaseIndexProfiler(NavigationHelper navigationHelper) {
32 if (navigationHelper instanceof NavigationHelperImpl) {
33 final NavigationHelperContentAdapter contentAdapter = ((NavigationHelperImpl) navigationHelper).getContentAdapter();
34 if (contentAdapter instanceof ProfilingNavigationHelperContentAdapter) {
35 adapter = (ProfilingNavigationHelperContentAdapter)contentAdapter;
36 }
37 }
38 Preconditions.checkArgument(adapter != null, "Cannot attach profiler to Base Index");
39 }
40
41 /**
42 * Returns the number of external request (e.g. model changes) the profiler recorded.
43 */
44 public long getNotificationCount() {
45 return adapter.getNotificationCount();
46 }
47
48 /**
49 * Return the total time base index profiler recorded for reacting to model operations.
50 */
51 public long getTotalMeasuredTimeInMS() {
52 return adapter.getTotalMeasuredTimeInMS();
53 }
54
55 /**
56 * Returns whether the profiler is turned on (e.g. measured values are increased).
57 */
58 public boolean isEnabled() {
59 return adapter.isEnabled();
60 }
61
62 /**
63 * Enables the base index profiling (e.g. measured values are increased)
64 */
65 public void setEnabled(boolean isEnabled) {
66 adapter.setEnabled(isEnabled);
67 }
68
69 /**
70 * Resets all measurements to 0, regardless whether the profiler is enabled or not.
71 * </p>
72 *
73 * <strong>Note</strong>: The behavior of the profiler is undefined when the measurements are reset while an EMF
74 * notification is being processed and the profiler is enabled.
75 */
76 public void resetMeasurement() {
77 adapter.resetMeasurement();
78 }
79}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/profiler/ProfilerMode.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/profiler/ProfilerMode.java
new file mode 100644
index 00000000..74901263
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/api/profiler/ProfilerMode.java
@@ -0,0 +1,22 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, 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.base.api.profiler;
10
11/**
12 * @since 2.3
13 */
14public enum ProfilerMode {
15
16 /** The base index profiler is not available */
17 OFF,
18 /** The profiler is initialized but not started until necessary */
19 START_DISABLED,
20 /** The profiler is initialized and started by default */
21 START_ENABLED
22}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFModelComprehension.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFModelComprehension.java
new file mode 100644
index 00000000..bde93367
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFModelComprehension.java
@@ -0,0 +1,356 @@
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.base.comprehension;
11
12import java.util.ArrayList;
13import java.util.Iterator;
14import java.util.List;
15
16import org.eclipse.emf.common.notify.Notifier;
17import org.eclipse.emf.common.util.EList;
18import org.eclipse.emf.ecore.EAttribute;
19import org.eclipse.emf.ecore.EObject;
20import org.eclipse.emf.ecore.EReference;
21import org.eclipse.emf.ecore.EStructuralFeature;
22import org.eclipse.emf.ecore.EcorePackage;
23import org.eclipse.emf.ecore.InternalEObject;
24import org.eclipse.emf.ecore.resource.Resource;
25import org.eclipse.emf.ecore.resource.ResourceSet;
26import org.eclipse.emf.ecore.util.EcoreUtil;
27import org.eclipse.emf.ecore.util.ExtendedMetaData;
28import org.eclipse.emf.ecore.util.FeatureMap;
29import org.eclipse.emf.ecore.util.FeatureMap.Entry;
30import org.eclipse.emf.ecore.util.InternalEList;
31import tools.refinery.viatra.runtime.base.api.BaseIndexOptions;
32import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexFeatureFilter;
33import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexObjectFilter;
34import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexResourceFilter;
35
36/**
37 * @author Bergmann Gábor
38 *
39 * Does not directly visit derived links, unless marked as a WellBehavingFeature. Derived edges are
40 * automatically interpreted correctly in these cases: - EFeatureMaps - eOpposites of containments
41 *
42 * @noextend This class is not intended to be subclassed by clients.
43 */
44public class EMFModelComprehension {
45
46 /**
47 * @since 2.3
48 */
49 protected BaseIndexOptions options;
50
51 /**
52 * Creates a model comprehension with the specified options. The options are copied, therefore subsequent changes
53 * will not affect the comprehension.
54 */
55 public EMFModelComprehension(BaseIndexOptions options) {
56 this.options = options.copy();
57 }
58
59 /**
60 * Should not traverse this feature directly. It is still possible that it can be represented in IQBase if
61 * {@link #representable(EStructuralFeature)} is true.
62 */
63 public boolean untraversableDirectly(EStructuralFeature feature) {
64
65 if((feature instanceof EReference && ((EReference)feature).isContainer())) {
66 // container features are always represented through their opposite
67 return true;
68 }
69
70 //If the feature is filtered by the feature filter specified in the BaseIndexOptions, return true
71 final IBaseIndexFeatureFilter featureFilter = options.getFeatureFilterConfiguration();
72 if(featureFilter != null && featureFilter.isFiltered(feature)){
73 return true;
74 }
75
76 boolean suspect = onlySamplingFeature(feature);
77 if(suspect) {
78 // even if the feature can only be sampled, it may be used if the proper base index option is set
79 suspect = options.isTraverseOnlyWellBehavingDerivedFeatures();
80 }
81 return suspect;
82 }
83
84 /**
85 * Decides whether a feature can only be sampled as there is no guarantee that proper notifications will be
86 * delivered by their implementation.
87 *
88 * <p/> Such features are derived (and/or volatile) features that are not well-behaving.
89 */
90 public boolean onlySamplingFeature(EStructuralFeature feature) {
91 boolean suspect =
92 feature.isDerived() ||
93 feature.isVolatile();
94 if (suspect) {
95 // override support here
96 // (e.g. if manual notifications available, or no changes expected afterwards)
97 suspect = !WellbehavingDerivedFeatureRegistry.isWellbehavingFeature(feature);
98 // TODO verbose flag somewhere to ease debugging (for such warnings)
99 // TODO add warning about not visited subtree (containment, FeatureMap and annotation didn't define
100 // otherwise)
101 }
102 return suspect;
103 }
104
105 /**
106 * This feature can be represented in IQBase.
107 */
108 public boolean representable(EStructuralFeature feature) {
109 if (!untraversableDirectly(feature))
110 return true;
111
112 if (feature instanceof EReference) {
113 final EReference reference = (EReference) feature;
114 if (reference.isContainer() && representable(reference.getEOpposite()))
115 return true;
116 }
117
118 boolean isMixed = "mixed".equals(EcoreUtil.getAnnotation(feature.getEContainingClass(),
119 ExtendedMetaData.ANNOTATION_URI, "kind"));
120 if (isMixed)
121 return true; // TODO maybe check the "name"=":mixed" or ":group" feature for representability?
122
123 // Group features are alternative features that are used when the ecore is derived from an xsd schema containing
124 // choices; in that case instead of the asked feature we should index the corresponding group feature
125 final EStructuralFeature groupFeature = ExtendedMetaData.INSTANCE.getGroup(feature);
126 if (groupFeature != null) {
127 return representable(groupFeature);
128 }
129
130 return false;
131 }
132
133 /**
134 * Resource filters not consulted here (for performance), because model roots are assumed to be pre-filtered.
135 */
136 public void traverseModel(EMFVisitor visitor, Notifier source) {
137 if (source == null)
138 return;
139 if (source instanceof EObject) {
140 final EObject sourceObject = (EObject) source;
141 if (sourceObject.eIsProxy())
142 throw new IllegalArgumentException("Proxy EObject cannot act as model roots for VIATRA: " + source);
143 traverseObject(visitor, sourceObject);
144 } else if (source instanceof Resource) {
145 traverseResource(visitor, (Resource) source);
146 } else if (source instanceof ResourceSet) {
147 traverseResourceSet(visitor, (ResourceSet) source);
148 }
149 }
150
151 public void traverseResourceSet(EMFVisitor visitor, ResourceSet source) {
152 if (source == null)
153 return;
154 final List<Resource> resources = new ArrayList<Resource>(source.getResources());
155 for (Resource resource : resources) {
156 traverseResourceIfUnfiltered(visitor, resource);
157 }
158 }
159
160 public void traverseResourceIfUnfiltered(EMFVisitor visitor, Resource resource) {
161 final IBaseIndexResourceFilter resourceFilter = options.getResourceFilterConfiguration();
162 if (resourceFilter != null && resourceFilter.isResourceFiltered(resource))
163 return;
164 final IBaseIndexObjectFilter objectFilter = options.getObjectFilterConfiguration();
165 if (objectFilter != null && objectFilter.isFiltered(resource))
166 return;
167
168 traverseResource(visitor, resource);
169 }
170
171 public void traverseResource(EMFVisitor visitor, Resource source) {
172 if (source == null)
173 return;
174 if (visitor.pruneSubtrees(source))
175 return;
176 final EList<EObject> contents = source.getContents();
177 for (EObject eObject : contents) {
178 traverseObjectIfUnfiltered(visitor, eObject);
179 }
180 }
181
182
183 public void traverseObjectIfUnfiltered(EMFVisitor visitor, EObject targetObject) {
184 final IBaseIndexObjectFilter objectFilter = options.getObjectFilterConfiguration();
185 if (objectFilter != null && objectFilter.isFiltered(targetObject))
186 return;
187
188 traverseObject(visitor, targetObject);
189 }
190
191 public void traverseObject(EMFVisitor visitor, EObject source) {
192 if (source == null)
193 return;
194
195 if (visitor.preOrder()) visitor.visitElement(source);
196 for (EStructuralFeature feature : source.eClass().getEAllStructuralFeatures()) {
197 if (untraversableDirectly(feature))
198 continue;
199 final boolean visitorPrunes = visitor.pruneFeature(feature);
200 if (visitorPrunes && !unprunableFeature(visitor, source, feature))
201 continue;
202
203 traverseFeatureTargets(visitor, source, feature, visitorPrunes);
204 }
205 if (!visitor.preOrder()) visitor.visitElement(source);
206 }
207
208 protected void traverseFeatureTargets(EMFVisitor visitor, EObject source, EStructuralFeature feature,
209 final boolean visitorPrunes) {
210 boolean attemptResolve = (feature instanceof EAttribute) || visitor.attemptProxyResolutions(source, (EReference)feature);
211 if (feature.isMany()) {
212 EList<?> targets = (EList<?>) source.eGet(feature);
213 int position = 0;
214 Iterator<?> iterator = attemptResolve ? targets.iterator() : ((InternalEList<?>)targets).basicIterator();
215 while (iterator.hasNext()) {
216 Object target = iterator.next();
217 traverseFeatureInternal(visitor, source, feature, target, visitorPrunes, position++);
218 }
219 } else {
220 Object target = source.eGet(feature, attemptResolve);
221 if (target != null)
222 traverseFeatureInternal(visitor, source, feature, target, visitorPrunes, null);
223 }
224 }
225 /**
226 * @since 2.3
227 */
228 protected boolean unprunableFeature(EMFVisitor visitor, EObject source, EStructuralFeature feature) {
229 return (feature instanceof EAttribute && EcorePackage.eINSTANCE.getEFeatureMapEntry().equals(
230 ((EAttribute) feature).getEAttributeType()))
231 || (feature instanceof EReference && ((EReference) feature).isContainment() && (!visitor
232 .pruneSubtrees(source) || ((EReference) feature).getEOpposite() != null));
233 }
234
235 /**
236 * @param position optional: known position in multivalued collection (for more efficient proxy resolution)
237 */
238 public void traverseFeature(EMFVisitor visitor, EObject source, EStructuralFeature feature, Object target, Integer position) {
239 if (target == null)
240 return;
241 if (untraversableDirectly(feature))
242 return;
243 traverseFeatureInternalSimple(visitor, source, feature, target, position);
244 }
245
246 /**
247 * @param position optional: known position in multivalued collection (for more efficient proxy resolution)
248 * @since 2.3
249 */
250 protected void traverseFeatureInternalSimple(EMFVisitor visitor, EObject source, EStructuralFeature feature,
251 Object target, Integer position) {
252 final boolean visitorPrunes = visitor.pruneFeature(feature);
253 if (visitorPrunes && !unprunableFeature(visitor, source, feature))
254 return;
255
256 traverseFeatureInternal(visitor, source, feature, target, visitorPrunes, position);
257 }
258
259 /**
260 * @pre target != null
261 * @param position optional: known position in multivalued collection (for more efficient proxy resolution)
262 * @since 2.3
263 */
264 protected void traverseFeatureInternal(EMFVisitor visitor, EObject source, EStructuralFeature feature,
265 Object target, boolean visitorPrunes, Integer position) {
266 if (feature instanceof EAttribute) {
267 if (!visitorPrunes)
268 visitor.visitAttribute(source, (EAttribute) feature, target);
269 if (target instanceof FeatureMap.Entry) { // emulated derived edge based on FeatureMap
270 Entry entry = (FeatureMap.Entry) target;
271 final EStructuralFeature emulated = entry.getEStructuralFeature();
272 final Object emulatedTarget = entry.getValue();
273
274 emulateUntraversableFeature(visitor, source, emulated, emulatedTarget);
275 }
276 } else if (feature instanceof EReference) {
277 EReference reference = (EReference) feature;
278 EObject targetObject = (EObject) target;
279 if (reference.isContainment()) {
280 if (!visitor.avoidTransientContainmentLink(source, reference, targetObject)) {
281 if (!visitorPrunes)
282 visitor.visitInternalContainment(source, reference, targetObject);
283 if (!visitor.pruneSubtrees(source)) {
284 // Recursively follow containment...
285 // unless cross-resource containment (in which case we may skip)
286 Resource targetResource = (targetObject instanceof InternalEObject)?
287 ((InternalEObject)targetObject).eDirectResource() : null;
288 boolean crossResourceContainment = targetResource != null;
289 if (!crossResourceContainment || visitor.descendAlongCrossResourceContainments()) {
290 // in-resource containment shall be followed
291 // as well as cross-resource containment for an object scope
292 traverseObjectIfUnfiltered(visitor, targetObject);
293 } else {
294 // do not follow
295 // target will be traversed separately from its resource (resourceSet scope)
296 // or left out of scope (resource scope)
297 }
298 }
299
300 final EReference opposite = reference.getEOpposite();
301 if (opposite != null) { // emulated derived edge based on container opposite
302 emulateUntraversableFeature(visitor, targetObject, opposite, source);
303 }
304 }
305 } else {
306 // if (containedElements.contains(target))
307 if (!visitorPrunes)
308 visitor.visitNonContainmentReference(source, reference, targetObject);
309 }
310 if (targetObject.eIsProxy()) {
311 if (!reference.isResolveProxies()) {
312 throw new IllegalStateException(String.format(
313 "EReference '%s' of EClass %s is set as proxy-non-resolving (i.e. it should never point to a proxy, and never lead cross-resource), " +
314 "yet VIATRA Base encountered a proxy object %s referenced from %s.",
315 reference.getName(), reference.getEContainingClass().getInstanceTypeName(),
316 targetObject, source));
317 }
318 visitor.visitProxyReference(source, reference, targetObject, position);
319 }
320 }
321
322 }
323
324
325 /**
326 * Emulates a derived edge, if it is not visited otherwise
327 *
328 * @pre target != null
329 * @since 2.3
330 */
331 protected void emulateUntraversableFeature(EMFVisitor visitor, EObject source,
332 final EStructuralFeature emulated, final Object target) {
333 if (untraversableDirectly(emulated))
334 traverseFeatureInternalSimple(visitor, source, emulated, target, null);
335 }
336
337 /**
338 * Can be called to attempt to resolve a reference pointing to one or more proxies, using eGet().
339 */
340 @SuppressWarnings("unchecked")
341 public void tryResolveReference(EObject source, EReference reference) {
342 final Object result = source.eGet(reference, true);
343 if (reference.isMany()) {
344 // no idea which element to get, have to iterate through
345 ((Iterable<EObject>) result).forEach(EObject -> {/*proxy resolution as a side-effect of traversal*/});
346 }
347 }
348
349 /**
350 * Finds out whether the Resource is currently loading
351 */
352 public boolean isLoading(Resource resource) {
353 return !resource.isLoaded() || ((Resource.Internal)resource).isLoading();
354 }
355
356} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFVisitor.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFVisitor.java
new file mode 100644
index 00000000..6029bb02
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/EMFVisitor.java
@@ -0,0 +1,145 @@
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.base.comprehension;
11
12import org.eclipse.emf.ecore.EAttribute;
13import org.eclipse.emf.ecore.EObject;
14import org.eclipse.emf.ecore.EReference;
15import org.eclipse.emf.ecore.EStructuralFeature;
16import org.eclipse.emf.ecore.resource.Resource;
17
18/**
19 * Use EMFModelComprehension to visit an EMF model.
20 *
21 * @author Bergmann Gábor
22 *
23 */
24// FIXME:
25// - handle boundary of active emfRoot subtree
26// - more efficient traversal
27public class EMFVisitor {
28
29 boolean preOrder;
30
31 public EMFVisitor(boolean preOrder) {
32 super();
33 this.preOrder = preOrder;
34 }
35
36 /**
37 * @param resource
38 * @param element
39 */
40 public void visitTopElementInResource(Resource resource, EObject element) {
41 }
42
43 /**
44 * @param resource
45 */
46 public void visitResource(Resource resource) {
47 }
48
49 /**
50 * @param source
51 */
52 public void visitElement(EObject source) {
53 }
54
55 /**
56 * @param source
57 * @param feature
58 * @param target
59 */
60 public void visitNonContainmentReference(EObject source, EReference feature, EObject target) {
61 }
62
63 /**
64 * @param source
65 * @param feature
66 * @param target
67 */
68 public void visitInternalContainment(EObject source, EReference feature, EObject target) {
69 }
70
71 /**
72 * @param source
73 * @param feature
74 * @param target
75 */
76 public void visitAttribute(EObject source, EAttribute feature, Object target) {
77 }
78
79 /**
80 * Returns true if the given feature should not be traversed (interesting esp. if multi-valued)
81 */
82 public boolean pruneFeature(EStructuralFeature feature) {
83 return false;
84 }
85
86 /**
87 * Returns true if the contents of an object should be pruned (and not explored by the visitor)
88 */
89 public boolean pruneSubtrees(EObject source) {
90 return false;
91 }
92
93 /**
94 * Returns true if the contents of a resource should be pruned (and not explored by the visitor)
95 */
96 public boolean pruneSubtrees(Resource source) {
97 return false;
98 }
99
100 /**
101 * An opportunity for the visitor to indicate that the containment link is considered in a transient state, and the
102 * model comprehension should avoid following it.
103 *
104 * A containment is in a transient state from the point of view of the visitor if it connects a subtree that is
105 * being inserted <em>during</em> a full-model traversal, and a separate notification handler will deal with it
106 * later.
107 */
108 public boolean avoidTransientContainmentLink(EObject source, EReference reference, EObject targetObject) {
109 return false;
110 }
111
112 /**
113 * @return if objects should be visited before their outgoing edges
114 */
115 public boolean preOrder() {
116 return preOrder;
117 }
118
119 /**
120 * Called after visiting the reference, if the target is a proxy.
121 *
122 * @param position
123 * optional: known position in multivalued collection (for more efficient proxy resolution)
124 */
125 public void visitProxyReference(EObject source, EReference reference, EObject targetObject, Integer position) {
126 }
127
128 /**
129 * Whether the given reference of the given object should be resolved when it is a proxy
130 */
131 public boolean attemptProxyResolutions(EObject source, EReference feature) {
132 return true;
133 }
134
135 /**
136 * @return true if traversing visitors shall descend along cross-resource containments
137 * (this only makes sense for traversing visitors on an object scope)
138 *
139 * @since 1.7
140 */
141 public boolean descendAlongCrossResourceContainments() {
142 return false;
143 }
144
145} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/WellbehavingDerivedFeatureRegistry.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/WellbehavingDerivedFeatureRegistry.java
new file mode 100644
index 00000000..d696ddd6
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/comprehension/WellbehavingDerivedFeatureRegistry.java
@@ -0,0 +1,154 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2011 Abel Hegedus 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.base.comprehension;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.WeakHashMap;
14
15import org.apache.log4j.Logger;
16import org.eclipse.core.runtime.IConfigurationElement;
17import org.eclipse.core.runtime.IExtension;
18import org.eclipse.core.runtime.IExtensionPoint;
19import org.eclipse.core.runtime.IExtensionRegistry;
20import org.eclipse.core.runtime.Platform;
21import org.eclipse.emf.ecore.EClass;
22import org.eclipse.emf.ecore.EClassifier;
23import org.eclipse.emf.ecore.EPackage;
24import org.eclipse.emf.ecore.EStructuralFeature;
25import tools.refinery.viatra.runtime.base.ViatraBasePlugin;
26
27/**
28 * @author Abel Hegedus
29 *
30 */
31public class WellbehavingDerivedFeatureRegistry {
32
33
34 private static Collection<EStructuralFeature> contributedWellbehavingDerivedFeatures = Collections.newSetFromMap(new WeakHashMap<EStructuralFeature, Boolean>());
35 private static Collection<EClass> contributedWellbehavingDerivedClasses = Collections.newSetFromMap(new WeakHashMap<EClass, Boolean>());
36 private static Collection<EPackage> contributedWellbehavingDerivedPackages = Collections.newSetFromMap(new WeakHashMap<EPackage, Boolean>());
37
38 private WellbehavingDerivedFeatureRegistry() {
39 }
40
41 /**
42 * Called by ViatraBasePlugin.
43 */
44 public static void initRegistry() {
45 getContributedWellbehavingDerivedFeatures().clear();
46 getContributedWellbehavingDerivedClasses().clear();
47 getContributedWellbehavingDerivedPackages().clear();
48
49 IExtensionRegistry reg = Platform.getExtensionRegistry();
50 IExtensionPoint poi;
51
52 poi = reg.getExtensionPoint(ViatraBasePlugin.WELLBEHAVING_DERIVED_FEATURE_EXTENSION_POINT_ID);
53 if (poi != null) {
54 IExtension[] exts = poi.getExtensions();
55
56 for (IExtension ext : exts) {
57
58 IConfigurationElement[] els = ext.getConfigurationElements();
59 for (IConfigurationElement el : els) {
60 if (el.getName().equals("wellbehaving-derived-feature")) {
61 processWellbehavingExtension(el);
62 } else {
63 throw new UnsupportedOperationException("Unknown configuration element " + el.getName()
64 + " in plugin.xml of " + el.getDeclaringExtension().getUniqueIdentifier());
65 }
66 }
67 }
68 }
69 }
70
71 private static void processWellbehavingExtension(IConfigurationElement el) {
72 try {
73 String packageUri = el.getAttribute("package-nsUri");
74 String featureName = el.getAttribute("feature-name");
75 String classifierName = el.getAttribute("classifier-name");
76 String contributorName = el.getContributor().getName();
77 StringBuilder featureIdBuilder = new StringBuilder();
78 if (packageUri != null) {
79 EPackage pckg = EPackage.Registry.INSTANCE.getEPackage(packageUri);
80 featureIdBuilder.append(packageUri);
81 if (pckg != null) {
82 if (classifierName != null) {
83 EClassifier clsr = pckg.getEClassifier(classifierName);
84 featureIdBuilder.append("##").append(classifierName);
85 if (clsr instanceof EClass) {
86 if (featureName != null) {
87 EClass cls = (EClass) clsr;
88 EStructuralFeature feature = cls.getEStructuralFeature(featureName);
89 featureIdBuilder.append("##").append(featureName);
90 if (feature != null) {
91 registerWellbehavingDerivedFeature(feature);
92 } else {
93 throw new IllegalStateException(String.format("Feature %s of EClass %s in package %s not found! (plug-in %s)", featureName, classifierName, packageUri, contributorName));
94 }
95 } else {
96 registerWellbehavingDerivedClass((EClass) clsr);
97 }
98 } else {
99 throw new IllegalStateException(String.format("EClassifier %s does not exist in package %s! (plug-in %s)", classifierName, packageUri, contributorName));
100 }
101 } else {
102 if(featureName != null){
103 throw new IllegalStateException(String.format("Feature name must be empty if classifier name is not set! (package %s, plug-in %s)", packageUri, contributorName));
104 }
105 registerWellbehavingDerivedPackage(pckg);
106 }
107 }
108 }
109 } catch (Exception e) {
110 final Logger logger = Logger.getLogger(WellbehavingDerivedFeatureRegistry.class);
111 logger.error("Well-behaving feature registration failed", e);
112 }
113 }
114
115 /**
116 *
117 * @param feature
118 * @return true if the feature (or its defining EClass or ) is registered as well-behaving
119 */
120 public static boolean isWellbehavingFeature(EStructuralFeature feature) {
121 if(feature == null){
122 return false;
123 } else if (contributedWellbehavingDerivedFeatures.contains(feature)) {
124 return true;
125 } else if (contributedWellbehavingDerivedClasses.contains(feature.getEContainingClass())) {
126 return true;
127 } else return contributedWellbehavingDerivedPackages.contains(feature.getEContainingClass().getEPackage());
128 }
129
130 public static void registerWellbehavingDerivedFeature(EStructuralFeature feature) {
131 contributedWellbehavingDerivedFeatures.add(feature);
132 }
133
134 public static void registerWellbehavingDerivedClass(EClass cls) {
135 contributedWellbehavingDerivedClasses.add(cls);
136 }
137
138 public static void registerWellbehavingDerivedPackage(EPackage pkg) {
139 contributedWellbehavingDerivedPackages.add(pkg);
140 }
141
142 public static Collection<EStructuralFeature> getContributedWellbehavingDerivedFeatures() {
143 return contributedWellbehavingDerivedFeatures;
144 }
145
146 public static Collection<EClass> getContributedWellbehavingDerivedClasses() {
147 return contributedWellbehavingDerivedClasses;
148 }
149
150 public static Collection<EPackage> getContributedWellbehavingDerivedPackages() {
151 return contributedWellbehavingDerivedPackages;
152 }
153
154}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/AbstractBaseIndexStore.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/AbstractBaseIndexStore.java
new file mode 100644
index 00000000..3d61b1bf
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/AbstractBaseIndexStore.java
@@ -0,0 +1,28 @@
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.base.core;
10
11import org.apache.log4j.Logger;
12import tools.refinery.viatra.runtime.base.api.BaseIndexOptions;
13
14/**
15 * @since 1.6
16 */
17public class AbstractBaseIndexStore {
18
19 protected final NavigationHelperImpl navigationHelper;
20 protected final Logger logger;
21 protected final BaseIndexOptions options;
22
23 public AbstractBaseIndexStore(NavigationHelperImpl navigationHelper, Logger logger) {
24 this.navigationHelper = navigationHelper;
25 this.logger = logger;
26 this.options = navigationHelper.getBaseIndexOptions();
27 }
28}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFBaseIndexInstanceStore.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFBaseIndexInstanceStore.java
new file mode 100644
index 00000000..2094bbbe
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFBaseIndexInstanceStore.java
@@ -0,0 +1,451 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.base.core;
10
11import java.util.Collections;
12import java.util.HashSet;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Set;
16
17import org.apache.log4j.Logger;
18import org.eclipse.emf.ecore.EClass;
19import org.eclipse.emf.ecore.EClassifier;
20import org.eclipse.emf.ecore.EObject;
21import tools.refinery.viatra.runtime.base.api.IStructuralFeatureInstanceProcessor;
22import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
23import tools.refinery.viatra.runtime.matchers.util.IMultiset;
24
25/**
26 * Stores the indexed contents of an EMF model
27 * (includes instance model information).
28 *
29 * @author Gabor Bergmann
30 * @noextend This class is not intended to be subclassed by clients.
31 */
32public class EMFBaseIndexInstanceStore extends AbstractBaseIndexStore {
33
34 public EMFBaseIndexInstanceStore(NavigationHelperImpl navigationHelper, Logger logger) {
35 super(navigationHelper, logger);
36 }
37
38 /**
39 * since last run of after-update callbacks
40 */
41 boolean isDirty = false;
42
43 /**
44 * feature (EAttribute or EReference or equivalent String key) -> FeatureData
45 * @since 1.7
46 */
47 private Map<Object, FeatureData> featureDataMap = CollectionsFactory.createMap();
48
49 /**
50 * value -> featureKey(s);
51 * constructed on-demand, null if unused (hopefully most of the time)
52 */
53 private Map<Object, IMultiset<Object>> valueToFeatureMap = null;
54
55
56 /**
57 * key (String id or EClass instance) -> instance(s)
58 */
59 private final Map<Object, Set<EObject>> instanceMap = CollectionsFactory.createMap();
60
61 /**
62 * key (String id or EDataType instance) -> multiset of value(s)
63 */
64 private final Map<Object, IMultiset<Object>> dataTypeMap = CollectionsFactory.createMap();
65
66 /**
67 * Bundles all instance store data specific to a given binary feature.
68 *
69 * <p> TODO: specialize for to-one features and unique to-many features
70 * <p> TODO: on-demand construction of valueToHolderMap
71 *
72 * @author Gabor Bergmann
73 * @since 1.7
74 */
75 class FeatureData {
76 /** value -> holder(s) */
77 private Map<Object, IMultiset<EObject>> valueToHolderMap = CollectionsFactory.createMap();
78 /**
79 * holder -> value(s);
80 * constructed on-demand, null if unused
81 */
82 private Map<EObject, IMultiset<Object>> holderToValueMap;
83
84 /**
85 * feature (EAttribute or EReference) or its string key (in dynamic EMF mode)
86 */
87 private Object featureKey;
88
89 /**
90 * @return feature (EAttribute or EReference) or its string key (in dynamic EMF mode)
91 */
92 public Object getFeatureKey() {
93 return featureKey;
94 }
95
96 @Override
97 public String toString() {
98 return this.getClass().getSimpleName() + ":" + featureKey;
99 }
100
101 /**
102 * @return true if this was the first time the value was added to this feature of this holder (false is only
103 * expected for non-unique features)
104 */
105 boolean insertFeatureTuple(boolean unique, final Object value, final EObject holder) {
106 // TODO we currently assume V2H map exists
107 boolean changed = addToValueToHolderMap(value, holder);
108 if (holderToValueMap != null) {
109 addToHolderToValueMap(value, holder);
110 }
111
112 if (unique && !changed) {
113 navigationHelper.logIncidentFeatureTupleInsertion(value, holder, featureKey);
114 }
115 return changed;
116 }
117
118 /**
119 * @return true if this was the last duplicate of the value added to this feature of this holder (false is only
120 * expected for non-unique features)
121 */
122 boolean removeFeatureTuple(boolean unique, final Object value, final EObject holder) {
123 Object featureKey = getFeatureKey();
124 try {
125 // TODO we currently assume V2H map exists
126 boolean changed = removeFromValueToHolderMap(value, holder);
127 if (holderToValueMap != null) {
128 removeFromHolderToValueMap(value, holder);
129 }
130
131 if (unique && !changed) {
132 navigationHelper.logIncidentFeatureTupleRemoval(value, holder, featureKey);
133 }
134 return changed;
135 } catch (IllegalStateException ex) {
136 navigationHelper.logIncidentFeatureTupleRemoval(value, holder, featureKey);
137 return false;
138 }
139 }
140
141
142 protected boolean addToHolderToValueMap(Object value, EObject holder) {
143 IMultiset<Object> values = holderToValueMap.computeIfAbsent(holder,
144 CollectionsFactory::emptyMultiset);
145 boolean changed = values.addOne(value);
146 return changed;
147 }
148
149 protected boolean addToValueToHolderMap(final Object value, final EObject holder) {
150 IMultiset<EObject> holders = valueToHolderMap.computeIfAbsent(value,
151 CollectionsFactory::emptyMultiset);
152 boolean changed = holders.addOne(holder);
153 return changed;
154 }
155
156 protected boolean removeFromHolderToValueMap(Object value, EObject holder) throws IllegalStateException {
157 IMultiset<Object> values = holderToValueMap.get(holder);
158 if (values == null)
159 throw new IllegalStateException();
160 boolean changed = values.removeOne(value);
161 if (changed && values.isEmpty())
162 holderToValueMap.remove(holder);
163 return changed;
164 }
165 protected boolean removeFromValueToHolderMap(final Object value, final EObject holder) throws IllegalStateException {
166 IMultiset<EObject> holders = valueToHolderMap.get(value);
167 if (holders == null)
168 throw new IllegalStateException();
169 boolean changed = holders.removeOne(holder);
170 if (changed && holders.isEmpty())
171 valueToHolderMap.remove(value);
172 return changed;
173 }
174
175 protected Map<EObject, IMultiset<Object>> getHolderToValueMap() {
176 if (holderToValueMap == null) {
177 holderToValueMap = CollectionsFactory.createMap();
178
179 // TODO we currently assume V2H map exists
180 for (Entry<Object, IMultiset<EObject>> entry : valueToHolderMap.entrySet()) {
181 Object value = entry.getKey();
182 IMultiset<EObject> holders = entry.getValue();
183 for (EObject holder : holders.distinctValues()) {
184 int count = holders.getCount(holder);
185
186 IMultiset<Object> valuesOfHolder = holderToValueMap.computeIfAbsent(holder,
187 CollectionsFactory::emptyMultiset);
188 valuesOfHolder.addPositive(value, count);
189 }
190 }
191 }
192 return holderToValueMap;
193 }
194 protected Map<Object, IMultiset<EObject>> getValueToHolderMap() {
195 // TODO we currently assume V2H map exists
196 return valueToHolderMap;
197 }
198
199 public void forEach(IStructuralFeatureInstanceProcessor processor) {
200 // TODO we currently assume V2H map exists
201 if (valueToHolderMap != null) {
202 for (Entry<Object, IMultiset<EObject>> entry : valueToHolderMap.entrySet()) {
203 Object value = entry.getKey();
204 for (EObject eObject : entry.getValue().distinctValues()) {
205 processor.process(eObject, value);
206 }
207 }
208 } else throw new UnsupportedOperationException("TODO implement");
209 }
210
211 public Set<EObject> getAllDistinctHolders() {
212 return getHolderToValueMap().keySet();
213 }
214
215 public Set<Object> getAllDistinctValues() {
216 return getValueToHolderMap().keySet();
217 }
218
219 public Set<EObject> getDistinctHoldersOfValue(Object value) {
220 IMultiset<EObject> holdersMultiset = getValueToHolderMap().get(value);
221 if (holdersMultiset == null)
222 return Collections.emptySet();
223 else return holdersMultiset.distinctValues();
224 }
225
226
227 public Set<Object> getDistinctValuesOfHolder(EObject holder) {
228 IMultiset<Object> valuesMultiset = getHolderToValueMap().get(holder);
229 if (valuesMultiset == null)
230 return Collections.emptySet();
231 else return valuesMultiset.distinctValues();
232 }
233
234 public boolean isInstance(EObject source, Object target) {
235 // TODO we currently assume V2H map exists
236 if (valueToHolderMap != null) {
237 IMultiset<EObject> holders = valueToHolderMap.get(target);
238 return holders != null && holders.containsNonZero(source);
239 } else throw new UnsupportedOperationException("TODO implement");
240 }
241
242
243 }
244
245
246 FeatureData getFeatureData(Object featureKey) {
247 FeatureData data = featureDataMap.get(featureKey);
248 if (data == null) {
249 data = createFeatureData(featureKey);
250 featureDataMap.put(featureKey, data);
251 }
252 return data;
253 }
254
255 /**
256 * TODO: specialize for to-one features and unique to-many features
257 */
258 protected FeatureData createFeatureData(Object featureKey) {
259 FeatureData data = new FeatureData();
260 data.featureKey = featureKey;
261 return data;
262 }
263
264
265
266
267 protected void insertFeatureTuple(final Object featureKey, boolean unique, final Object value, final EObject holder) {
268 boolean changed = getFeatureData(featureKey).insertFeatureTuple(unique, value, holder);
269 if (changed) { // if not duplicated
270
271 if (valueToFeatureMap != null) {
272 insertIntoValueToFeatureMap(featureKey, value);
273 }
274
275 isDirty = true;
276 navigationHelper.notifyFeatureListeners(holder, featureKey, value, true);
277 }
278 }
279
280 protected void removeFeatureTuple(final Object featureKey, boolean unique, final Object value, final EObject holder) {
281 boolean changed = getFeatureData(featureKey).removeFeatureTuple(unique, value, holder);
282 if (changed) { // if not duplicated
283
284 if (valueToFeatureMap != null) {
285 removeFromValueToFeatureMap(featureKey, value);
286 }
287
288 isDirty = true;
289 navigationHelper.notifyFeatureListeners(holder, featureKey, value, false);
290 }
291 }
292
293
294 public Set<Object> getFeatureKeysPointingTo(Object target) {
295 final IMultiset<Object> sources = getValueToFeatureMap().get(target);
296 return sources == null ? Collections.emptySet() : sources.distinctValues();
297 }
298
299 protected Map<Object, IMultiset<Object>> getValueToFeatureMap() {
300 if (valueToFeatureMap == null) { // must be inverted from feature data
301 valueToFeatureMap = CollectionsFactory.createMap();
302 for (FeatureData featureData : featureDataMap.values()) {
303 final Object featureKey = featureData.getFeatureKey();
304 featureData.forEach((source, target) -> insertIntoValueToFeatureMap(featureKey, target));
305 }
306 }
307 return valueToFeatureMap;
308 }
309
310 protected void insertIntoValueToFeatureMap(final Object featureKey, Object target) {
311 IMultiset<Object> featureKeys = valueToFeatureMap.computeIfAbsent(target, CollectionsFactory::emptyMultiset);
312 featureKeys.addOne(featureKey);
313 }
314 protected void removeFromValueToFeatureMap(final Object featureKey, final Object value) {
315 IMultiset<Object> featureKeys = valueToFeatureMap.get(value);
316 if (featureKeys == null)
317 throw new IllegalStateException();
318 featureKeys.removeOne(featureKey);
319 if (featureKeys.isEmpty())
320 valueToFeatureMap.remove(value);
321 }
322
323 // START ********* InstanceSet *********
324 public Set<EObject> getInstanceSet(final Object keyClass) {
325 return instanceMap.get(keyClass);
326 }
327
328 public void removeInstanceSet(final Object keyClass) {
329 instanceMap.remove(keyClass);
330 }
331
332 public void insertIntoInstanceSet(final Object keyClass, final EObject value) {
333 Set<EObject> set = instanceMap.computeIfAbsent(keyClass, CollectionsFactory::emptySet);
334
335 if (!set.add(value)) {
336 navigationHelper.logIncidentInstanceInsertion(keyClass, value);
337 } else {
338 isDirty = true;
339 navigationHelper.notifyInstanceListeners(keyClass, value, true);
340 }
341 }
342
343 public void removeFromInstanceSet(final Object keyClass, final EObject value) {
344 final Set<EObject> set = instanceMap.get(keyClass);
345 if (set != null) {
346 if(!set.remove(value)) {
347 navigationHelper.logIncidentInstanceRemoval(keyClass, value);
348 } else {
349 if (set.isEmpty()) {
350 instanceMap.remove(keyClass);
351 }
352 isDirty = true;
353 navigationHelper.notifyInstanceListeners(keyClass, value, false);
354 }
355 } else {
356 navigationHelper.logIncidentInstanceRemoval(keyClass, value);
357 }
358
359 }
360
361
362
363 // END ********* InstanceSet *********
364
365 // START ********* DataTypeMap *********
366 public Set<Object> getDistinctDataTypeInstances(final Object keyType) {
367 IMultiset<Object> values = dataTypeMap.get(keyType);
368 return values == null ? Collections.emptySet() : values.distinctValues();
369 }
370
371 public void removeDataTypeMap(final Object keyType) {
372 dataTypeMap.remove(keyType);
373 }
374
375 public void insertIntoDataTypeMap(final Object keyType, final Object value) {
376 IMultiset<Object> valMap = dataTypeMap.computeIfAbsent(keyType, CollectionsFactory::emptyMultiset);
377 final boolean firstOccurrence = valMap.addOne(value);
378
379 isDirty = true;
380 navigationHelper.notifyDataTypeListeners(keyType, value, true, firstOccurrence);
381 }
382
383 public void removeFromDataTypeMap(final Object keyType, final Object value) {
384 final IMultiset<Object> valMap = dataTypeMap.get(keyType);
385 if (valMap != null) {
386 final boolean lastOccurrence = valMap.removeOne(value);
387 if (lastOccurrence && valMap.isEmpty()) {
388 dataTypeMap.remove(keyType);
389 }
390
391 isDirty = true;
392 navigationHelper.notifyDataTypeListeners(keyType, value, false, lastOccurrence);
393 }
394 }
395
396 // END ********* DataTypeMap *********
397
398 protected Set<EObject> getHoldersOfFeature(Object featureKey) {
399 FeatureData featureData = getFeatureData(featureKey);
400 return featureData.getAllDistinctHolders();
401 }
402 protected Set<Object> getValuesOfFeature(Object featureKey) {
403 FeatureData featureData = getFeatureData(featureKey);
404 return featureData.getAllDistinctValues();
405 }
406
407 /**
408 * Returns all EClasses that currently have direct instances cached by the index.
409 * <p>
410 * Supertypes will not be returned, unless they have direct instances in the model as well. If not in
411 * <em>wildcard mode</em>, only registered EClasses and their subtypes will be returned.
412 * <p>
413 * Note for advanced users: if a type is represented by multiple EClass objects, one of them is chosen as
414 * representative and returned.
415 */
416 public Set<EClass> getAllCurrentClasses() {
417 final Set<EClass> result = CollectionsFactory.createSet();
418 final Set<Object> classifierKeys = instanceMap.keySet();
419 for (final Object classifierKey : classifierKeys) {
420 final EClassifier knownClassifier = navigationHelper.metaStore.getKnownClassifierForKey(classifierKey);
421 if (knownClassifier instanceof EClass) {
422 result.add((EClass) knownClassifier);
423 }
424 }
425 return result;
426 }
427
428 Set<Object> getOldValuesForHolderAndFeature(EObject source, Object featureKey) {
429 // while this is slower than using the holderToFeatureToValueMap, we do not want to construct that to avoid
430 // memory overhead
431 Map<Object, IMultiset<EObject>> oldValuesToHolders = getFeatureData(featureKey).valueToHolderMap;
432 Set<Object> oldValues = new HashSet<Object>();
433 for (Entry<Object, IMultiset<EObject>> entry : oldValuesToHolders.entrySet()) {
434 if (entry.getValue().containsNonZero(source)) {
435 oldValues.add(entry.getKey());
436 }
437 }
438 return oldValues;
439 }
440
441 protected void forgetFeature(Object featureKey) {
442 FeatureData removed = featureDataMap.remove(featureKey);
443 if (valueToFeatureMap != null) {
444 for (Object value : removed.getAllDistinctValues()) {
445 removeFromValueToFeatureMap(featureKey, value);
446 }
447 }
448 }
449
450
451}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFBaseIndexMetaStore.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFBaseIndexMetaStore.java
new file mode 100644
index 00000000..52ca3a53
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFBaseIndexMetaStore.java
@@ -0,0 +1,380 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.base.core;
10
11import org.eclipse.emf.common.util.Enumerator;
12import org.eclipse.emf.ecore.*;
13import tools.refinery.viatra.runtime.base.api.BaseIndexOptions;
14import tools.refinery.viatra.runtime.base.api.IndexingLevel;
15import tools.refinery.viatra.runtime.base.api.InstanceListener;
16import tools.refinery.viatra.runtime.base.exception.ViatraBaseException;
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;
22
23import java.util.*;
24import java.util.Map.Entry;
25
26/**
27 * Stores the indexed metamodel information.
28 *
29 * @author Gabor Bergmann
30 * @noextend This class is not intended to be subclassed by clients.
31 */
32public class EMFBaseIndexMetaStore {
33
34 private static final EClass EOBJECT_CLASS = EcorePackage.eINSTANCE.getEObject();
35 private final boolean isDynamicModel;
36 private NavigationHelperImpl navigationHelper;
37
38 /**
39 *
40 */
41 public EMFBaseIndexMetaStore(final NavigationHelperImpl navigationHelper) {
42 this.navigationHelper = navigationHelper;
43 final BaseIndexOptions options = navigationHelper.getBaseIndexOptions();
44 this.isDynamicModel = options.isDynamicEMFMode();
45 }
46
47 /**
48 * Supports collision detection and EEnum canonicalization. Used for all EPackages that have types whose instances
49 * were encountered at least once.
50 */
51 private final Set<EPackage> knownPackages = new HashSet<EPackage>();
52
53 /**
54 * Field variable because it is needed for collision detection. Used for all EClasses whose instances were
55 * encountered at least once.
56 */
57 private final Set<EClassifier> knownClassifiers = new HashSet<EClassifier>();
58 /**
59 * Field variable because it is needed for collision detection. Used for all EStructuralFeatures whose instances
60 * were encountered at least once.
61 */
62 private final Set<EStructuralFeature> knownFeatures = new HashSet<EStructuralFeature>();
63
64 /**
65 * (EClass or String ID) -> all subtypes in knownClasses
66 */
67 private final Map<Object, Set<Object>> subTypeMap = new HashMap<Object, Set<Object>>();
68 /**
69 * (EClass or String ID) -> all supertypes in knownClasses
70 */
71 private final Map<Object, Set<Object>> superTypeMap = new HashMap<Object, Set<Object>>();
72
73 /**
74 * EPacakge NsURI -> EPackage instances; this is instance-level to detect collisions
75 */
76 private final IMultiLookup<String, EPackage> uniqueIDToPackage = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class);
77
78 /**
79 * static maps between metamodel elements and their unique IDs
80 */
81 private final Map<EClassifier, String> uniqueIDFromClassifier = new HashMap<EClassifier, String>();
82 private final Map<ETypedElement, String> uniqueIDFromTypedElement = new HashMap<ETypedElement, String>();
83 private final Map<Enumerator, String> uniqueIDFromEnumerator = new HashMap<Enumerator, String>();
84 private final IMultiLookup<String, EClassifier> uniqueIDToClassifier = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class);
85 private final IMultiLookup<String, ETypedElement> uniqueIDToTypedElement = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class);
86 private final IMultiLookup<String, Enumerator> uniqueIDToEnumerator = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class);
87 private final Map<String, Enumerator> uniqueIDToCanonicalEnumerator = new HashMap<String, Enumerator>();
88
89 /**
90 * Map from enum classes generated for {@link EEnum}s to the actual EEnum.
91 */
92 private Map<Class<?>, EEnum> generatedEENumClasses = new HashMap<Class<?>, EEnum>();
93
94 /**
95 * @return the eObjectClassKey
96 */
97 public Object getEObjectClassKey() {
98 if (eObjectClassKey == null) {
99 eObjectClassKey = toKey(EOBJECT_CLASS);
100 }
101 return eObjectClassKey;
102 }
103 private Object eObjectClassKey = null;
104
105 protected Object toKey(final EClassifier classifier) {
106 if (isDynamicModel) {
107 return toKeyDynamicInternal(classifier);
108 } else {
109 maintainMetamodel(classifier);
110 return classifier;
111 }
112 }
113
114 protected String toKeyDynamicInternal(final EClassifier classifier) {
115 String id = uniqueIDFromClassifier.get(classifier);
116 if (id == null) {
117 Preconditions.checkArgument(!classifier.eIsProxy(),
118 "Classifier %s is an unresolved proxy", classifier);
119 id = classifier.getEPackage().getNsURI() + "##" + classifier.getName();
120 uniqueIDFromClassifier.put(classifier, id);
121 uniqueIDToClassifier.addPair(id, classifier);
122 // metamodel maintenance will call back toKey(), but now the ID maps are already filled
123 maintainMetamodel(classifier);
124 }
125 return id;
126 }
127
128 protected String enumToKeyDynamicInternal(Enumerator enumerator) {
129 String id = uniqueIDFromEnumerator.get(enumerator);
130 if (id == null) {
131 if (enumerator instanceof EEnumLiteral) {
132 EEnumLiteral enumLiteral = (EEnumLiteral) enumerator;
133 final EEnum eEnum = enumLiteral.getEEnum();
134 maintainMetamodel(eEnum);
135
136 id = constructEnumID(eEnum.getEPackage().getNsURI(), eEnum.getName(), enumLiteral.getLiteral());
137
138 // there might be a generated enum for this enum literal!
139 // generated enum should pre-empt the ecore enum literal as canonical enumerator
140 Enumerator instanceEnum = enumLiteral.getInstance();
141 if (instanceEnum != null && !uniqueIDToCanonicalEnumerator.containsKey(id)) {
142 uniqueIDToCanonicalEnumerator.put(id, instanceEnum);
143 }
144 // if generated enum not found... delay selection of canonical enumerator
145 } else { // generated enum
146 final EEnum eEnum = generatedEENumClasses.get(enumerator.getClass());
147 if (eEnum != null)
148 id = constructEnumID(eEnum.getEPackage().getNsURI(), eEnum.getName(), enumerator.getLiteral());
149 else
150 id = constructEnumID("unkownPackage URI", enumerator.getClass().getSimpleName(),
151 enumerator.getLiteral());
152
153 // generated enum should pre-empt the ecore enum literal as canonical enumerator
154 if (!uniqueIDToCanonicalEnumerator.containsKey(id)) {
155 uniqueIDToCanonicalEnumerator.put(id, enumerator);
156 }
157 }
158 uniqueIDFromEnumerator.put(enumerator, id);
159 uniqueIDToEnumerator.addPair(id, enumerator);
160 }
161 return id;
162 }
163
164 protected String constructEnumID(String nsURI, String name, String literal) {
165 return String.format("%s##%s##%s", nsURI, name, literal);
166 }
167
168 protected Object toKey(final EStructuralFeature feature) {
169 if (isDynamicModel) {
170 String id = uniqueIDFromTypedElement.get(feature);
171 if (id == null) {
172 Preconditions.checkArgument(!feature.eIsProxy(),
173 "Element %s is an unresolved proxy", feature);
174 id = toKeyDynamicInternal((EClassifier) feature.eContainer()) + "##" + feature.getEType().getName()
175 + "##" + feature.getName();
176 uniqueIDFromTypedElement.put(feature, id);
177 uniqueIDToTypedElement.addPair(id, feature);
178 // metamodel maintenance will call back toKey(), but now the ID maps are already filled
179 maintainMetamodel(feature);
180 }
181 return id;
182 } else {
183 maintainMetamodel(feature);
184 return feature;
185 }
186 }
187
188 protected Enumerator enumToCanonicalDynamicInternal(final Enumerator value) {
189 final String key = enumToKeyDynamicInternal(value);
190 Enumerator canonicalEnumerator = uniqueIDToCanonicalEnumerator.computeIfAbsent(key,
191 // if no canonical version appointed yet, appoint first version
192 k -> uniqueIDToEnumerator.lookup(k).iterator().next());
193 return canonicalEnumerator;
194 }
195
196 /**
197 * If in dynamic EMF mode, substitutes enum literals with a canonical version of the enum literal.
198 */
199 protected Object toInternalValueRepresentation(final Object value) {
200 if (isDynamicModel) {
201 if (value instanceof Enumerator)
202 return enumToCanonicalDynamicInternal((Enumerator) value);
203 else
204 return value;
205 } else {
206 return value;
207 }
208 }
209
210 /**
211 * Checks the {@link EStructuralFeature}'s source and target {@link EPackage} for NsURI collision. An error message
212 * will be logged if a model element from an other {@link EPackage} instance with the same NsURI has been already
213 * processed. The error message will be logged only for the first time for a given {@link EPackage} instance.
214 *
215 * @param classifier
216 * the classifier instance
217 */
218 protected void maintainMetamodel(final EStructuralFeature feature) {
219 if (!knownFeatures.contains(feature)) {
220 knownFeatures.add(feature);
221 maintainMetamodel(feature.getEContainingClass());
222 maintainMetamodel(feature.getEType());
223 }
224 }
225
226 /**
227 * put subtype information into cache
228 */
229 protected void maintainMetamodel(final EClassifier classifier) {
230 if (!knownClassifiers.contains(classifier)) {
231 checkEPackage(classifier);
232 knownClassifiers.add(classifier);
233
234 if (classifier instanceof EClass) {
235 final EClass clazz = (EClass) classifier;
236 final Object clazzKey = toKey(clazz);
237 for (final EClass superType : clazz.getEAllSuperTypes()) {
238 maintainTypeHierarhyInternal(clazzKey, toKey(superType));
239 }
240 maintainTypeHierarhyInternal(clazzKey, getEObjectClassKey());
241 } else if (classifier instanceof EEnum) {
242 EEnum eEnum = (EEnum) classifier;
243
244 if (isDynamicModel) {
245 // if there is a generated enum class, save this model element for describing that class
246 if (eEnum.getInstanceClass() != null)
247 generatedEENumClasses.put(eEnum.getInstanceClass(), eEnum);
248
249 for (EEnumLiteral eEnumLiteral : eEnum.getELiterals()) {
250 // create string ID; register generated enum values
251 enumToKeyDynamicInternal(eEnumLiteral);
252 }
253 }
254 }
255 }
256 }
257
258 /**
259 * Checks the {@link EClassifier}'s {@link EPackage} for NsURI collision. An error message will be logged if a model
260 * element from an other {@link EPackage} instance with the same NsURI has been already processed. The error message
261 * will be logged only for the first time for a given {@link EPackage} instance.
262 *
263 * @param classifier
264 * the classifier instance
265 */
266 protected void checkEPackage(final EClassifier classifier) {
267 final EPackage ePackage = classifier.getEPackage();
268 if (knownPackages.add(ePackage)) { // this is a new EPackage
269 final String nsURI = ePackage.getNsURI();
270 final IMemoryView<EPackage> packagesOfURI = uniqueIDToPackage.lookupOrEmpty(nsURI);
271 if (!packagesOfURI.containsNonZero(ePackage)) { // this should be true
272 uniqueIDToPackage.addPair(nsURI, ePackage);
273 // collision detection between EPackages (disabled in dynamic model mode)
274 if (!isDynamicModel && packagesOfURI.size() == 2) { // only report the issue if the new EPackage
275 // instance is the second for the same URI
276 navigationHelper.processingError(
277 new ViatraBaseException("NsURI (" + nsURI
278 + ") collision detected between different instances of EPackages. If this is normal, try using dynamic EMF mode."),
279 "process new metamodel elements.");
280 }
281 }
282 }
283 }
284
285 /**
286 * Maintains subtype hierarchy
287 *
288 * @param subClassKey
289 * EClass or String id of subclass
290 * @param superClassKey
291 * EClass or String id of superclass
292 */
293 protected void maintainTypeHierarhyInternal(final Object subClassKey, final Object superClassKey) {
294 // update observed class and instance listener tables according to new subtype information
295 Map<Object, IndexingLevel> allObservedClasses = navigationHelper.getAllObservedClassesInternal();
296 if (allObservedClasses.containsKey(superClassKey)) {
297 // we know that there are no known subtypes of subClassKey at this point, so a single insert should suffice
298 allObservedClasses.put(subClassKey, allObservedClasses.get(superClassKey));
299 }
300 final Map<Object, Map<InstanceListener, Set<EClass>>> instanceListeners = navigationHelper.peekInstanceListeners();
301 if (instanceListeners != null) { // table already constructed
302 for (final Entry<InstanceListener, Set<EClass>> entry : instanceListeners.getOrDefault(superClassKey, Collections.emptyMap()).entrySet()) {
303 final InstanceListener listener = entry.getKey();
304 for (final EClass subscriptionType : entry.getValue()) {
305 navigationHelper.addInstanceListenerInternal(listener, subscriptionType, subClassKey);
306 }
307 }
308 }
309
310 // update subtype maps
311 Set<Object> subTypes = subTypeMap.computeIfAbsent(superClassKey, k -> new HashSet<>());
312 subTypes.add(subClassKey);
313 Set<Object> superTypes = superTypeMap.computeIfAbsent(subClassKey, k -> new HashSet<>());
314 superTypes.add(superClassKey);
315 }
316
317 /**
318 * @return the subTypeMap
319 */
320 protected Map<Object, Set<Object>> getSubTypeMap() {
321 return subTypeMap;
322 }
323
324 protected Map<Object, Set<Object>> getSuperTypeMap() {
325 return superTypeMap;
326 }
327
328 /**
329 * Returns the corresponding {@link EStructuralFeature} instance for the id.
330 *
331 * @param featureId
332 * the id of the feature
333 * @return the {@link EStructuralFeature} instance
334 */
335 public EStructuralFeature getKnownFeature(final String featureId) {
336 final IMemoryView<ETypedElement> features = uniqueIDToTypedElement.lookup(featureId);
337 if (features != null && !features.isEmpty()) {
338 final ETypedElement next = features.iterator().next();
339 if (next instanceof EStructuralFeature) {
340 return (EStructuralFeature) next;
341 }
342 }
343 return null;
344
345 }
346
347 public EStructuralFeature getKnownFeatureForKey(Object featureKey) {
348 EStructuralFeature feature;
349 if (isDynamicModel) {
350 feature = getKnownFeature((String) featureKey);
351 } else {
352 feature = (EStructuralFeature) featureKey;
353 }
354 return feature;
355 }
356
357 /**
358 * Returns the corresponding {@link EClassifier} instance for the id.
359 */
360 public EClassifier getKnownClassifier(final String key) {
361 final IMemoryView<EClassifier> classifiersOfThisID = uniqueIDToClassifier.lookup(key);
362 if (classifiersOfThisID != null && !classifiersOfThisID.isEmpty()) {
363 return classifiersOfThisID.iterator().next();
364 } else {
365 return null;
366 }
367 }
368
369 public EClassifier getKnownClassifierForKey(Object classifierKey) {
370 EClassifier cls;
371 if (isDynamicModel) {
372 cls = getKnownClassifier((String) classifierKey);
373 } else {
374 cls = (EClassifier) classifierKey;
375 }
376 return cls;
377 }
378
379
380}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFBaseIndexStatisticsStore.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFBaseIndexStatisticsStore.java
new file mode 100644
index 00000000..de66de78
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFBaseIndexStatisticsStore.java
@@ -0,0 +1,71 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.base.core;
10
11import java.util.HashMap;
12import java.util.Map;
13
14import org.apache.log4j.Logger;
15import org.eclipse.emf.ecore.EClassifier;
16import org.eclipse.emf.ecore.EStructuralFeature;
17
18/**
19 * @author Grill Balázs
20 * @noextend This class is not intended to be subclassed by clients.
21 */
22public class EMFBaseIndexStatisticsStore extends AbstractBaseIndexStore {
23
24 /**
25 * A common map is used to store instance/value statistics. The key can be an {@link EClassifier},
26 * {@link EStructuralFeature} or a String ID.
27 */
28 private final Map<Object, Integer> stats = new HashMap<Object, Integer>();
29
30 public EMFBaseIndexStatisticsStore(NavigationHelperImpl navigationHelper, Logger logger) {
31 super(navigationHelper, logger);
32 }
33 public void addFeature(Object element, Object feature){
34 addInstance(feature);
35 }
36
37 public void removeFeature(Object element, Object feature){
38 removeInstance(feature);
39 }
40
41 public void addInstance(Object key){
42 Integer v = stats.get(key);
43 stats.put(key, v == null ? 1 : v+1);
44 }
45
46 public void removeInstance(Object key){
47 Integer v = stats.get(key);
48 if(v == null || v <= 0) {
49 navigationHelper.logIncidentStatRemoval(key);
50 return;
51 }
52 if (v.intValue() == 1){
53 stats.remove(key);
54 }else{
55 stats.put(key, v-1);
56 }
57 }
58 public int countInstances(Object key){
59 Integer v = stats.get(key);
60 return v == null ? 0 : v.intValue();
61 }
62
63 public void removeType(Object key){
64 stats.remove(key);
65 }
66
67 public int countFeatures(Object feature) {
68 return countInstances(feature);
69 }
70
71}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFDataSource.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFDataSource.java
new file mode 100644
index 00000000..35e590f2
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/EMFDataSource.java
@@ -0,0 +1,137 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.core;
11
12import java.util.LinkedList;
13import java.util.List;
14import java.util.Set;
15
16import org.eclipse.emf.ecore.EClass;
17import org.eclipse.emf.ecore.EObject;
18import org.eclipse.emf.ecore.EReference;
19import tools.refinery.viatra.runtime.base.api.NavigationHelper;
20import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource;
21import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver;
22import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
23import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
24import tools.refinery.viatra.runtime.matchers.util.IMultiset;
25
26// TODO IBiDirectionalGraphDataSource
27public class EMFDataSource implements IGraphDataSource<EObject> {
28
29 private List<IGraphObserver<EObject>> observers;
30 private Set<EReference> references;
31 private Set<EClass> classes;
32 private NavigationHelper navigationHelper;
33 private IMultiset<EObject> allEObjects; // contains objects even if only appearing as sources or targets
34
35 /**
36 * @param navigationHelper
37 * @param references
38 * @param classes
39 * additional classes to treat as nodes. Source and target classes of references need not be added.
40 */
41 public EMFDataSource(NavigationHelper navigationHelper, Set<EReference> references, Set<EClass> classes) {
42 this.references = references;
43 this.classes = classes;
44 this.observers = new LinkedList<IGraphObserver<EObject>>();
45 this.navigationHelper = navigationHelper;
46 }
47
48 @Override
49 public Set<EObject> getAllNodes() {
50 return getAllEObjects().distinctValues();
51 }
52
53 @Override
54 public IMemoryView<EObject> getTargetNodes(EObject source) {
55 IMultiset<EObject> targetNodes = CollectionsFactory.createMultiset();
56
57 for (EReference ref : references) {
58 final Set<EObject> referenceValues = navigationHelper.getReferenceValues(source, ref);
59 for (EObject referenceValue : referenceValues) {
60 targetNodes.addOne(referenceValue);
61 }
62 }
63
64 return targetNodes;
65 }
66
67 @Override
68 public void attachObserver(IGraphObserver<EObject> go) {
69 observers.add(go);
70 }
71
72 @Override
73 public void attachAsFirstObserver(IGraphObserver<EObject> observer) {
74 observers.add(0, observer);
75 }
76
77 @Override
78 public void detachObserver(IGraphObserver<EObject> go) {
79 observers.remove(go);
80 }
81
82 public void notifyEdgeInserted(EObject source, EObject target) {
83 nodeAdditionInternal(source);
84 nodeAdditionInternal(target);
85 for (IGraphObserver<EObject> o : observers) {
86 o.edgeInserted(source, target);
87 }
88 }
89
90 public void notifyEdgeDeleted(EObject source, EObject target) {
91 for (IGraphObserver<EObject> o : observers) {
92 o.edgeDeleted(source, target);
93 }
94 nodeRemovalInternal(source);
95 nodeRemovalInternal(target);
96 }
97
98 public void notifyNodeInserted(EObject node) {
99 nodeAdditionInternal(node);
100 }
101
102 public void notifyNodeDeleted(EObject node) {
103 nodeRemovalInternal(node);
104 }
105
106 private void nodeAdditionInternal(EObject node) {
107 if (allEObjects.addOne(node))
108 for (IGraphObserver<EObject> o : observers) {
109 o.nodeInserted(node);
110 }
111 }
112
113 private void nodeRemovalInternal(EObject node) {
114 if (getAllEObjects().removeOne(node))
115 for (IGraphObserver<EObject> o : observers) {
116 o.nodeDeleted(node);
117 }
118 }
119
120 protected IMultiset<EObject> getAllEObjects() {
121 if (allEObjects == null) {
122 allEObjects = CollectionsFactory.createMultiset();
123 for (EClass clazz : classes) {
124 for (EObject obj : navigationHelper.getAllInstances(clazz)) {
125 allEObjects.addOne(obj);
126 }
127 }
128 for (EReference ref : references) {
129 navigationHelper.processAllFeatureInstances(ref, (source, target) -> {
130 allEObjects.addOne(source);
131 allEObjects.addOne((EObject) target);
132 });
133 }
134 }
135 return allEObjects;
136 }
137}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperContentAdapter.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperContentAdapter.java
new file mode 100644
index 00000000..39271c5b
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperContentAdapter.java
@@ -0,0 +1,750 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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 * Note: this file contains methods copied from EContentAdapter.java of the EMF project
10 *******************************************************************************/
11package tools.refinery.viatra.runtime.base.core;
12
13import java.lang.reflect.InvocationTargetException;
14import java.util.Collection;
15import java.util.List;
16import java.util.Objects;
17import java.util.concurrent.Callable;
18
19import org.eclipse.emf.common.notify.Adapter;
20import org.eclipse.emf.common.notify.Notification;
21import org.eclipse.emf.common.notify.Notifier;
22import org.eclipse.emf.common.notify.impl.AdapterImpl;
23import org.eclipse.emf.common.util.EList;
24import org.eclipse.emf.ecore.EObject;
25import org.eclipse.emf.ecore.EReference;
26import org.eclipse.emf.ecore.EStructuralFeature;
27import org.eclipse.emf.ecore.InternalEObject;
28import org.eclipse.emf.ecore.resource.Resource;
29import org.eclipse.emf.ecore.resource.ResourceSet;
30import org.eclipse.emf.ecore.util.EContentAdapter;
31import tools.refinery.viatra.runtime.base.api.BaseIndexOptions;
32import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexObjectFilter;
33import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexResourceFilter;
34import tools.refinery.viatra.runtime.base.comprehension.EMFModelComprehension;
35import tools.refinery.viatra.runtime.base.comprehension.EMFVisitor;
36import tools.refinery.viatra.runtime.base.core.NavigationHelperVisitor.ChangeVisitor;
37
38/**
39 * Content Adapter that recursively attaches itself to the containment hierarchy of an EMF model.
40 * The purpose is to gather the contents of the model, and to subscribe to model change notifications.
41 *
42 * <p> Originally, this was implemented as a subclass of {@link EContentAdapter}.
43 * Because of Bug 490105, EContentAdapter is no longer a superclass; its code is copied over with modifications.
44 * See {@link EContentAdapter} header for original authorship and copyright information.
45 *
46 * @author Gabor Bergmann
47 * @see EContentAdapter
48 * @noextend This class is not intended to be subclassed by clients.
49 */
50public class NavigationHelperContentAdapter extends AdapterImpl {
51
52 private final NavigationHelperImpl navigationHelper;
53
54
55
56 // move optimization to avoid removing and re-adding entire subtrees
57 EObject ignoreInsertionAndDeletion;
58 // Set<EObject> ignoreRootInsertion = new HashSet<EObject>();
59 // Set<EObject> ignoreRootDeletion = new HashSet<EObject>();
60
61 private final EMFModelComprehension comprehension;
62
63 private IBaseIndexObjectFilter objectFilterConfiguration;
64 private IBaseIndexResourceFilter resourceFilterConfiguration;
65
66
67
68 private EMFVisitor removalVisitor;
69 private EMFVisitor insertionVisitor;
70
71 public NavigationHelperContentAdapter(final NavigationHelperImpl navigationHelper) {
72 this.navigationHelper = navigationHelper;
73 final BaseIndexOptions options = this.navigationHelper.getBaseIndexOptions();
74 objectFilterConfiguration = options.getObjectFilterConfiguration();
75 resourceFilterConfiguration = options.getResourceFilterConfiguration();
76 this.comprehension = navigationHelper.getComprehension();
77
78 removalVisitor = initChangeVisitor(false);
79 insertionVisitor = initChangeVisitor(true);
80 }
81
82 /**
83 * Point of customization, called by constructor.
84 */
85 protected ChangeVisitor initChangeVisitor(boolean isInsertion) {
86 return new NavigationHelperVisitor.ChangeVisitor(navigationHelper, isInsertion);
87 }
88
89 // key representative of the EObject class
90
91
92 @Override
93 public void notifyChanged(final Notification notification) {
94 try {
95 this.navigationHelper.coalesceTraversals(new Callable<Void>() {
96 @Override
97 public Void call() throws Exception {
98 simpleNotifyChanged(notification);
99
100 final Object oFeature = notification.getFeature();
101 final Object oNotifier = notification.getNotifier();
102 if (oNotifier instanceof EObject && oFeature instanceof EStructuralFeature) {
103 final EObject notifier = (EObject) oNotifier;
104 final EStructuralFeature feature = (EStructuralFeature) oFeature;
105
106 final boolean notifyLightweightObservers = handleNotification(notification, notifier, feature);
107
108 if (notifyLightweightObservers) {
109 navigationHelper.notifyLightweightObservers(notifier, feature, notification);
110 }
111 } else if (oNotifier instanceof Resource) {
112 if (notification.getFeatureID(Resource.class) == Resource.RESOURCE__IS_LOADED) {
113 final Resource resource = (Resource) oNotifier;
114 if (comprehension.isLoading(resource))
115 navigationHelper.resolutionDelayingResources.add(resource);
116 else
117 navigationHelper.resolutionDelayingResources.remove(resource);
118 }
119 }
120 return null;
121 }
122 });
123 } catch (final InvocationTargetException ex) {
124 navigationHelper.processingFatal(ex.getCause(), "handling the following update notification: " + notification);
125 } catch (final Exception ex) {
126 navigationHelper.processingFatal(ex, "handling the following update notification: " + notification);
127 }
128
129 navigationHelper.notifyBaseIndexChangeListeners();
130 }
131
132 @SuppressWarnings("deprecation")
133 protected boolean handleNotification(final Notification notification, final EObject notifier,
134 final EStructuralFeature feature) {
135 final Object oldValue = notification.getOldValue();
136 final Object newValue = notification.getNewValue();
137 final int positionInt = notification.getPosition();
138 final Integer position = positionInt == Notification.NO_INDEX ? null : positionInt;
139 final int eventType = notification.getEventType();
140 boolean notifyLightweightObservers = true;
141 switch (eventType) {
142 case Notification.ADD:
143 featureUpdate(true, notifier, feature, newValue, position);
144 break;
145 case Notification.ADD_MANY:
146 for (final Object newElement : (Collection<?>) newValue) {
147 featureUpdate(true, notifier, feature, newElement, position);
148 }
149 break;
150 case Notification.CREATE:
151 notifyLightweightObservers = false;
152 break;
153 case Notification.MOVE:
154 // lightweight observers should be notified on MOVE
155 break; // currently no support for ordering
156 case Notification.REMOVE:
157 featureUpdate(false, notifier, feature, oldValue, position);
158 break;
159 case Notification.REMOVE_MANY:
160 for (final Object oldElement : (Collection<?>) oldValue) {
161 featureUpdate(false, notifier, feature, oldElement, position);
162 }
163 break;
164 case Notification.REMOVING_ADAPTER:
165 notifyLightweightObservers = false;
166 break;
167 case Notification.RESOLVE: // must be EReference
168 if (navigationHelper.isFeatureResolveIgnored(feature))
169 break; // otherwise same as SET
170 if (!feature.isMany()) { // if single-valued, can be removed from delayed resolutions
171 navigationHelper.delayedProxyResolutions.removePairOrNop(notifier, (EReference) feature);
172 }
173 featureUpdate(false, notifier, feature, oldValue, position);
174 featureUpdate(true, notifier, feature, newValue, position);
175 break;
176 case Notification.UNSET:
177 case Notification.SET:
178 if(feature.isMany() && position == null){
179 // spurious UNSET notification of entire collection
180 notifyLightweightObservers = false;
181 } else {
182 featureUpdate(false, notifier, feature, oldValue, position);
183 featureUpdate(true, notifier, feature, newValue, position);
184 }
185 break;
186 default:
187 notifyLightweightObservers = false;
188 break;
189 }
190 return notifyLightweightObservers;
191 }
192
193 protected void featureUpdate(final boolean isInsertion, final EObject notifier, final EStructuralFeature feature,
194 final Object value, final Integer position) {
195 // this is a safe visitation, no reads will happen, thus no danger of notifications or matcher construction
196 comprehension.traverseFeature(getVisitorForChange(isInsertion), notifier, feature, value, position);
197 }
198
199 // OFFICIAL ENTRY POINT OF BASE INDEX RELATED PARTS
200 protected void addAdapter(final Notifier notifier) {
201 if (notifier == ignoreInsertionAndDeletion) {
202 return;
203 }
204 try {
205 // cross-resource containment workaround, see Bug 483089 and Bug 483086.
206 if (notifier.eAdapters().contains(this))
207 return;
208
209 if (objectFilterConfiguration != null && objectFilterConfiguration.isFiltered(notifier)) {
210 return;
211 }
212 this.navigationHelper.coalesceTraversals(new Callable<Void>() {
213 @Override
214 public Void call() throws Exception {
215 // the object is really traversed BEFORE the notification listener is added,
216 // so that if a proxy is resolved due to the traversal, we do not get notified about it
217 if (notifier instanceof EObject) {
218 comprehension.traverseObject(getVisitorForChange(true), (EObject) notifier);
219 } else if (notifier instanceof Resource) {
220 Resource resource = (Resource) notifier;
221 if (resourceFilterConfiguration != null
222 && resourceFilterConfiguration.isResourceFiltered(resource)) {
223 return null;
224 }
225 if (comprehension.isLoading(resource))
226 navigationHelper.resolutionDelayingResources.add(resource);
227 }
228 // subscribes to the adapter list, will receive setTarget callback that will spread addAdapter to
229 // children
230 simpleAddAdapter(notifier);
231 return null;
232 }
233 });
234 } catch (final InvocationTargetException ex) {
235 navigationHelper.processingFatal(ex.getCause(), "add the object: " + notifier);
236 } catch (final Exception ex) {
237 navigationHelper.processingFatal(ex, "add the object: " + notifier);
238 }
239 }
240
241 // OFFICIAL ENTRY POINT OF BASE INDEX RELATED PARTS
242 protected void removeAdapter(final Notifier notifier) {
243 if (notifier == ignoreInsertionAndDeletion) {
244 return;
245 }
246 try {
247 removeAdapterInternal(notifier);
248 } catch (final InvocationTargetException ex) {
249 navigationHelper.processingFatal(ex.getCause(), "remove the object: " + notifier);
250 } catch (final Exception ex) {
251 navigationHelper.processingFatal(ex, "remove the object: " + notifier);
252 }
253 }
254
255 // The additional boolean options are there to save the cost of extra checks, see Bug 483089 and Bug 483086.
256 protected void removeAdapter(final Notifier notifier, boolean additionalObjectContainerPossible,
257 boolean additionalResourceContainerPossible) {
258 if (notifier == ignoreInsertionAndDeletion) {
259 return;
260 }
261 try {
262
263 // cross-resource containment workaround, see Bug 483089 and Bug 483086.
264 if (notifier instanceof InternalEObject) {
265 InternalEObject internalEObject = (InternalEObject) notifier;
266 if (additionalResourceContainerPossible) {
267 Resource eDirectResource = internalEObject.eDirectResource();
268 if (eDirectResource != null && eDirectResource.eAdapters().contains(this)) {
269 return;
270 }
271 }
272 if (additionalObjectContainerPossible) {
273 InternalEObject eInternalContainer = internalEObject.eInternalContainer();
274 if (eInternalContainer != null && eInternalContainer.eAdapters().contains(this)) {
275 return;
276 }
277 }
278 }
279
280 removeAdapterInternal(notifier);
281 } catch (final InvocationTargetException ex) {
282 navigationHelper.processingFatal(ex.getCause(), "remove the object: " + notifier);
283 } catch (final Exception ex) {
284 navigationHelper.processingFatal(ex, "remove the object: " + notifier);
285 }
286 }
287
288 /**
289 * @throws InvocationTargetException
290 */
291 protected void removeAdapterInternal(final Notifier notifier) throws InvocationTargetException {
292 // some non-standard EMF implementations send these
293 if (!notifier.eAdapters().contains(this)) {
294 // the adapter was not even attached to the notifier
295 navigationHelper.logIncidentAdapterRemoval(notifier);
296
297 // skip the rest of the method, do not traverse contents
298 // as they have either never been added to the index or already removed
299 return;
300 }
301
302 if (objectFilterConfiguration != null && objectFilterConfiguration.isFiltered(notifier)) {
303 return;
304 }
305 this.navigationHelper.coalesceTraversals(new Callable<Void>() {
306 @Override
307 public Void call() throws Exception {
308 if (notifier instanceof EObject) {
309 final EObject eObject = (EObject) notifier;
310 comprehension.traverseObject(getVisitorForChange(false), eObject);
311 navigationHelper.delayedProxyResolutions.lookupAndRemoveAll(eObject);
312 } else if (notifier instanceof Resource) {
313 if (resourceFilterConfiguration != null
314 && resourceFilterConfiguration.isResourceFiltered((Resource) notifier)) {
315 return null;
316 }
317 navigationHelper.resolutionDelayingResources.remove(notifier);
318 }
319 // unsubscribes from the adapter list, will receive unsetTarget callback that will spread
320 // removeAdapter to children
321 simpleRemoveAdapter(notifier);
322 return null;
323 }
324 });
325 }
326
327 protected EMFVisitor getVisitorForChange(final boolean isInsertion) {
328 return isInsertion ? insertionVisitor : removalVisitor;
329 }
330
331
332 // WORKAROUND (TMP) for eContents vs. derived features bug
333 protected void setTarget(final EObject target) {
334 basicSetTarget(target);
335 spreadToChildren(target, true);
336 }
337
338 protected void unsetTarget(final EObject target) {
339 basicUnsetTarget(target);
340 spreadToChildren(target, false);
341 }
342
343 // Spread adapter removal/addition to children of EObject
344 protected void spreadToChildren(final EObject target, final boolean add) {
345 final EList<EReference> features = target.eClass().getEAllReferences();
346 for (final EReference feature : features) {
347 if (!feature.isContainment()) {
348 continue;
349 }
350 if (!comprehension.representable(feature)) {
351 continue;
352 }
353 if (feature.isMany()) {
354 final Collection<?> values = (Collection<?>) target.eGet(feature);
355 for (final Object value : values) {
356 final Notifier notifier = (Notifier) value;
357 if (add) {
358 addAdapter(notifier);
359 } else {
360 removeAdapter(notifier, false, true);
361 }
362 }
363 } else {
364 final Object value = target.eGet(feature);
365 if (value != null) {
366 final Notifier notifier = (Notifier) value;
367 if (add) {
368 addAdapter(notifier);
369 } else {
370 removeAdapter(notifier, false, true);
371 }
372 }
373 }
374 }
375 }
376
377
378 //
379 // ***********************************************************
380 // RENAMED METHODS COPIED OVER FROM EContentAdapter DOWN BELOW
381 // ***********************************************************
382 //
383
384 /**
385 * Handles a notification by calling {@link #selfAdapt selfAdapter}.
386 */
387 public void simpleNotifyChanged(Notification notification)
388 {
389 selfAdapt(notification);
390
391 super.notifyChanged(notification);
392 }
393
394 protected void simpleAddAdapter(Notifier notifier)
395 {
396 EList<Adapter> eAdapters = notifier.eAdapters();
397 if (!eAdapters.contains(this))
398 {
399 eAdapters.add(this);
400 }
401 }
402
403 protected void simpleRemoveAdapter(Notifier notifier)
404 {
405 notifier.eAdapters().remove(this);
406 }
407
408
409 //
410 // *********************************************************
411 // CODE COPIED OVER VERBATIM FROM EContentAdapter DOWN BELOW
412 // *********************************************************
413 //
414
415
416 /**
417 * Handles a notification by calling {@link #handleContainment handleContainment}
418 * for any containment-based notification.
419 */
420 protected void selfAdapt(Notification notification)
421 {
422 Object notifier = notification.getNotifier();
423 if (notifier instanceof ResourceSet)
424 {
425 if (notification.getFeatureID(ResourceSet.class) == ResourceSet.RESOURCE_SET__RESOURCES)
426 {
427 handleContainment(notification);
428 }
429 }
430 else if (notifier instanceof Resource)
431 {
432 if (notification.getFeatureID(Resource.class) == Resource.RESOURCE__CONTENTS)
433 {
434 handleContainment(notification);
435 }
436 }
437 else if (notifier instanceof EObject)
438 {
439 Object feature = notification.getFeature();
440 if (feature instanceof EReference)
441 {
442 EReference eReference = (EReference)feature;
443 if (eReference.isContainment())
444 {
445 handleContainment(notification);
446 }
447 }
448 }
449 }
450
451 /**
452 * Handles a containment change by adding and removing the adapter as appropriate.
453 */
454 protected void handleContainment(Notification notification)
455 {
456 switch (notification.getEventType())
457 {
458 case Notification.RESOLVE:
459 {
460 // We need to be careful that the proxy may be resolved while we are attaching this adapter.
461 // We need to avoid attaching the adapter during the resolve
462 // and also attaching it again as we walk the eContents() later.
463 // Checking here avoids having to check during addAdapter.
464 //
465 Notifier oldValue = (Notifier)notification.getOldValue();
466 if (oldValue.eAdapters().contains(this))
467 {
468 removeAdapter(oldValue);
469 Notifier newValue = (Notifier)notification.getNewValue();
470 addAdapter(newValue);
471 }
472 break;
473 }
474 case Notification.UNSET:
475 {
476 Object oldValue = notification.getOldValue();
477 if (!Objects.equals(oldValue, Boolean.TRUE) && !Objects.equals(oldValue, Boolean.FALSE))
478 {
479 if (oldValue != null)
480 {
481 removeAdapter((Notifier)oldValue, false, true);
482 }
483 Notifier newValue = (Notifier)notification.getNewValue();
484 if (newValue != null)
485 {
486 addAdapter(newValue);
487 }
488 }
489 break;
490 }
491 case Notification.SET:
492 {
493 Notifier oldValue = (Notifier)notification.getOldValue();
494 if (oldValue != null)
495 {
496 removeAdapter(oldValue, false, true);
497 }
498 Notifier newValue = (Notifier)notification.getNewValue();
499 if (newValue != null)
500 {
501 addAdapter(newValue);
502 }
503 break;
504 }
505 case Notification.ADD:
506 {
507 Notifier newValue = (Notifier)notification.getNewValue();
508 if (newValue != null)
509 {
510 addAdapter(newValue);
511 }
512 break;
513 }
514 case Notification.ADD_MANY:
515 {
516 @SuppressWarnings("unchecked") Collection<Notifier> newValues = (Collection<Notifier>)notification.getNewValue();
517 for (Notifier newValue : newValues)
518 {
519 addAdapter(newValue);
520 }
521 break;
522 }
523 case Notification.REMOVE:
524 {
525 Notifier oldValue = (Notifier)notification.getOldValue();
526 if (oldValue != null)
527 {
528 boolean checkContainer = notification.getNotifier() instanceof Resource;
529 boolean checkResource = notification.getFeature() != null;
530 removeAdapter(oldValue, checkContainer, checkResource);
531 }
532 break;
533 }
534 case Notification.REMOVE_MANY:
535 {
536 boolean checkContainer = notification.getNotifier() instanceof Resource;
537 boolean checkResource = notification.getFeature() != null;
538 @SuppressWarnings("unchecked") Collection<Notifier> oldValues = (Collection<Notifier>)notification.getOldValue();
539 for ( Notifier oldContentValue : oldValues)
540 {
541 removeAdapter(oldContentValue, checkContainer, checkResource);
542 }
543 break;
544 }
545 }
546 }
547
548 /**
549 * Handles installation of the adapter
550 * by adding the adapter to each of the directly contained objects.
551 */
552 @Override
553 public void setTarget(Notifier target)
554 {
555 if (target instanceof EObject)
556 {
557 setTarget((EObject)target);
558 }
559 else if (target instanceof Resource)
560 {
561 setTarget((Resource)target);
562 }
563 else if (target instanceof ResourceSet)
564 {
565 setTarget((ResourceSet)target);
566 }
567 else
568 {
569 basicSetTarget(target);
570 }
571 }
572
573 /**
574 * Actually sets the target by calling super.
575 */
576 protected void basicSetTarget(Notifier target)
577 {
578 super.setTarget(target);
579 }
580
581 /**
582 * Handles installation of the adapter on a Resource
583 * by adding the adapter to each of the directly contained objects.
584 */
585 protected void setTarget(Resource target)
586 {
587 basicSetTarget(target);
588 List<EObject> contents = target.getContents();
589 for (int i = 0, size = contents.size(); i < size; ++i)
590 {
591 Notifier notifier = contents.get(i);
592 addAdapter(notifier);
593 }
594 }
595
596 /**
597 * Handles installation of the adapter on a ResourceSet
598 * by adding the adapter to each of the directly contained objects.
599 */
600 protected void setTarget(ResourceSet target)
601 {
602 basicSetTarget(target);
603 List<Resource> resources = target.getResources();
604 for (int i = 0; i < resources.size(); ++i)
605 {
606 Notifier notifier = resources.get(i);
607 addAdapter(notifier);
608 }
609 }
610
611 /**
612 * Handles undoing the installation of the adapter
613 * by removing the adapter from each of the directly contained objects.
614 */
615 @Override
616 public void unsetTarget(Notifier target)
617 {
618 Object target1 = target;
619 if (target1 instanceof EObject)
620 {
621 unsetTarget((EObject)target1);
622 }
623 else if (target1 instanceof Resource)
624 {
625 unsetTarget((Resource)target1);
626 }
627 else if (target1 instanceof ResourceSet)
628 {
629 unsetTarget((ResourceSet)target1);
630 }
631 else
632 {
633 basicUnsetTarget((Notifier)target1);
634 }
635 }
636
637 /**
638 * Actually unsets the target by calling super.
639 */
640 protected void basicUnsetTarget(Notifier target)
641 {
642 super.unsetTarget(target);
643 }
644
645 /**
646 * Handles undoing the installation of the adapter from a Resource
647 * by removing the adapter from each of the directly contained objects.
648 */
649 protected void unsetTarget(Resource target)
650 {
651 basicUnsetTarget(target);
652 List<EObject> contents = target.getContents();
653 for (int i = 0, size = contents.size(); i < size; ++i)
654 {
655 Notifier notifier = contents.get(i);
656 removeAdapter(notifier, true, false);
657 }
658 }
659
660 /**
661 * Handles undoing the installation of the adapter from a ResourceSet
662 * by removing the adapter from each of the directly contained objects.
663 */
664 protected void unsetTarget(ResourceSet target)
665 {
666 basicUnsetTarget(target);
667 List<Resource> resources = target.getResources();
668 for (int i = 0; i < resources.size(); ++i)
669 {
670 Notifier notifier = resources.get(i);
671 removeAdapter(notifier, false, false);
672 }
673 }
674
675 protected boolean resolve()
676 {
677 return true;
678 }
679
680 //
681 // *********************************************************
682 // OBSOLETE CODE COPIED OVER FROM EContentAdapter DOWN BELOW
683 // *********************************************************
684 //
685 // *** Preserved on purpose as comments,
686 // *** in order to more easily follow future changes to EContentAdapter.
687 //
688
689
690// protected void removeAdapter(Notifier notifier, boolean checkContainer, boolean checkResource)
691// {
692// if (checkContainer || checkResource)
693// {
694// InternalEObject internalEObject = (InternalEObject) notifier;
695// if (checkResource)
696// {
697// Resource eDirectResource = internalEObject.eDirectResource();
698// if (eDirectResource != null && eDirectResource.eAdapters().contains(this))
699// {
700// return;
701// }
702// }
703// if (checkContainer)
704// {
705// InternalEObject eInternalContainer = internalEObject.eInternalContainer();
706// if (eInternalContainer != null && eInternalContainer.eAdapters().contains(this))
707// {
708// return;
709// }
710// }
711// }
712//
713// removeAdapter(notifier);
714// }
715
716// /**
717// * Handles undoing the installation of the adapter from an EObject
718// * by removing the adapter from each of the directly contained objects.
719// */
720// protected void unsetTarget(EObject target)
721// {
722// basicUnsetTarget(target);
723// for (Iterator<? extends Notifier> i = resolve() ?
724// target.eContents().iterator() :
725// ((InternalEList<EObject>)target.eContents()).basicIterator();
726// i.hasNext(); )
727// {
728// Notifier notifier = i.next();
729// removeAdapter(notifier, false, true);
730// }
731// }
732
733// /**
734// * Handles installation of the adapter on an EObject
735// * by adding the adapter to each of the directly contained objects.
736// */
737// protected void setTarget(EObject target)
738// {
739// basicSetTarget(target);
740// for (Iterator<? extends Notifier> i = resolve() ?
741// target.eContents().iterator() :
742// ((InternalEList<? extends Notifier>)target.eContents()).basicIterator();
743// i.hasNext(); )
744// {
745// Notifier notifier = i.next();
746// addAdapter(notifier);
747// }
748// }
749
750}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperImpl.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperImpl.java
new file mode 100644
index 00000000..2b5d74b5
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperImpl.java
@@ -0,0 +1,1702 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.base.core;
10
11import org.apache.log4j.Logger;
12import org.eclipse.emf.common.notify.Notification;
13import org.eclipse.emf.common.notify.Notifier;
14import org.eclipse.emf.common.notify.NotifyingList;
15import org.eclipse.emf.common.util.EList;
16import org.eclipse.emf.ecore.*;
17import org.eclipse.emf.ecore.EStructuralFeature.Setting;
18import org.eclipse.emf.ecore.impl.ENotificationImpl;
19import org.eclipse.emf.ecore.resource.Resource;
20import org.eclipse.emf.ecore.resource.ResourceSet;
21import org.eclipse.emf.ecore.util.EcoreUtil;
22import tools.refinery.viatra.runtime.base.api.*;
23import tools.refinery.viatra.runtime.base.api.IEClassifierProcessor.IEClassProcessor;
24import tools.refinery.viatra.runtime.base.api.IEClassifierProcessor.IEDataTypeProcessor;
25import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexObjectFilter;
26import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexResourceFilter;
27import tools.refinery.viatra.runtime.base.comprehension.EMFModelComprehension;
28import tools.refinery.viatra.runtime.base.comprehension.EMFVisitor;
29import tools.refinery.viatra.runtime.base.core.EMFBaseIndexInstanceStore.FeatureData;
30import tools.refinery.viatra.runtime.base.core.NavigationHelperVisitor.TraversingVisitor;
31import tools.refinery.viatra.runtime.base.core.profiler.ProfilingNavigationHelperContentAdapter;
32import tools.refinery.viatra.runtime.base.exception.ViatraBaseException;
33import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
34import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
35import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
36import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
37import tools.refinery.viatra.runtime.matchers.util.Preconditions;
38
39import java.lang.reflect.InvocationTargetException;
40import java.util.*;
41import java.util.Map.Entry;
42import java.util.concurrent.Callable;
43import java.util.function.Function;
44import java.util.function.Supplier;
45import java.util.stream.Collectors;
46import java.util.stream.Stream;
47
48import static java.util.function.Function.identity;
49
50/**
51 * @noextend This class is not intended to be subclassed by clients.
52 * @author Gabor Bergmann and Tamas Szabo
53 */
54public class NavigationHelperImpl implements NavigationHelper {
55
56 /**
57 * This is never null.
58 */
59 protected IndexingLevel wildcardMode;
60
61
62 protected Set<Notifier> modelRoots;
63 private boolean expansionAllowed;
64 private boolean traversalDescendsAlongCrossResourceContainment;
65 // protected NavigationHelperVisitor visitor;
66 protected NavigationHelperContentAdapter contentAdapter;
67
68 protected final Logger logger;
69
70 // type object or String id
71 protected Map<Object, IndexingLevel> directlyObservedClasses = new HashMap<Object, IndexingLevel>();
72 // including subclasses; if null, must be recomputed
73 protected Map<Object, IndexingLevel> allObservedClasses = null;
74 protected Map<Object, IndexingLevel> observedDataTypes;
75 protected Map<Object, IndexingLevel> observedFeatures;
76 // ignore RESOLVE for these features, as they are just starting to be observed - see [428458]
77 protected Set<Object> ignoreResolveNotificationFeatures;
78
79 /**
80 * Feature registration and model traversal is delayed while true
81 */
82 protected boolean delayTraversals = false;
83 /**
84 * Classes (or String ID in dynamic mode) to be registered once the coalescing period is over
85 */
86 protected Map<Object, IndexingLevel> delayedClasses = new HashMap<>();
87 /**
88 * EStructuralFeatures (or String ID in dynamic mode) to be registered once the coalescing period is over
89 */
90 protected Map<Object, IndexingLevel> delayedFeatures = new HashMap<>();
91 /**
92 * EDataTypes (or String ID in dynamic mode) to be registered once the coalescing period is over
93 */
94 protected Map<Object, IndexingLevel> delayedDataTypes = new HashMap<>();
95
96 /**
97 * Features per EObject to be resolved later (towards the end of a coalescing period when no Resources are loading)
98 */
99 protected IMultiLookup<EObject, EReference> delayedProxyResolutions = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class);
100 /**
101 * Reasources that are currently loading, implying the proxy resolution attempts should be delayed
102 */
103 protected Set<Resource> resolutionDelayingResources = new HashSet<Resource>();
104
105 protected Queue<Runnable> traversalCallbacks = new LinkedList<Runnable>();
106
107 /**
108 * These global listeners will be called after updates.
109 */
110 // private final Set<Runnable> afterUpdateCallbacks;
111 private final Set<EMFBaseIndexChangeListener> baseIndexChangeListeners;
112 private final Map<EObject, Set<LightweightEObjectObserver>> lightweightObservers;
113
114 // These are the user subscriptions to notifications
115 private final Map<InstanceListener, Set<EClass>> subscribedInstanceListeners;
116 private final Map<FeatureListener, Set<EStructuralFeature>> subscribedFeatureListeners;
117 private final Map<DataTypeListener, Set<EDataType>> subscribedDataTypeListeners;
118
119 // these are the internal notification tables
120 // (element Type or String id) -> listener -> (subscription types)
121 // if null, must be recomputed from subscriptions
122 // potentially multiple subscription types for each element type because (a) nsURI collisions, (b) multiple
123 // supertypes
124 private Map<Object, Map<InstanceListener, Set<EClass>>> instanceListeners;
125 private Map<Object, Map<FeatureListener, Set<EStructuralFeature>>> featureListeners;
126 private Map<Object, Map<DataTypeListener, Set<EDataType>>> dataTypeListeners;
127
128 private final Set<IEMFIndexingErrorListener> errorListeners;
129 private final BaseIndexOptions baseIndexOptions;
130
131 private EMFModelComprehension comprehension;
132
133 private boolean loggedRegistrationMessage = false;
134
135 EMFBaseIndexMetaStore metaStore;
136 EMFBaseIndexInstanceStore instanceStore;
137 EMFBaseIndexStatisticsStore statsStore;
138
139 <T> Set<T> setMinus(Collection<? extends T> a, Collection<T> b) {
140 Set<T> result = new HashSet<T>(a);
141 result.removeAll(b);
142 return result;
143 }
144
145 @SuppressWarnings("unchecked")
146 <T extends EObject> Set<T> resolveAllInternal(Set<? extends T> a) {
147 if (a == null)
148 a = Collections.emptySet();
149 Set<T> result = new HashSet<T>();
150 for (T t : a) {
151 if (t.eIsProxy()) {
152 result.add((T) EcoreUtil.resolve(t, (ResourceSet) null));
153 } else {
154 result.add(t);
155 }
156 }
157 return result;
158 }
159
160 Set<Object> resolveClassifiersToKey(Set<? extends EClassifier> classes) {
161 Set<? extends EClassifier> resolveds = resolveAllInternal(classes);
162 Set<Object> result = new HashSet<Object>();
163 for (EClassifier resolved : resolveds) {
164 result.add(toKey(resolved));
165 }
166 return result;
167 }
168
169 Set<Object> resolveFeaturesToKey(Set<? extends EStructuralFeature> features) {
170 Set<EStructuralFeature> resolveds = resolveAllInternal(features);
171 Set<Object> result = new HashSet<Object>();
172 for (EStructuralFeature resolved : resolveds) {
173 result.add(toKey(resolved));
174 }
175 return result;
176 }
177
178 @Override
179 public boolean isInWildcardMode() {
180 return isInWildcardMode(IndexingLevel.FULL);
181 }
182
183 @Override
184 public boolean isInWildcardMode(IndexingLevel level) {
185 return wildcardMode.providesLevel(level);
186 }
187
188 @Override
189 public boolean isInDynamicEMFMode() {
190 return baseIndexOptions.isDynamicEMFMode();
191 }
192
193 /**
194 * @return the baseIndexOptions
195 */
196 public BaseIndexOptions getBaseIndexOptions() {
197 return baseIndexOptions.copy();
198 }
199
200 /**
201 * @return the comprehension
202 */
203 public EMFModelComprehension getComprehension() {
204 return comprehension;
205 }
206
207 /**
208 * @throws ViatraQueryRuntimeException
209 */
210 public NavigationHelperImpl(Notifier emfRoot, BaseIndexOptions options, Logger logger) {
211 this.baseIndexOptions = options.copy();
212 this.logger = logger;
213 assert (logger != null);
214
215 this.comprehension = initModelComprehension();
216 this.wildcardMode = baseIndexOptions.getWildcardLevel();
217 this.subscribedInstanceListeners = new HashMap<InstanceListener, Set<EClass>>();
218 this.subscribedFeatureListeners = new HashMap<FeatureListener, Set<EStructuralFeature>>();
219 this.subscribedDataTypeListeners = new HashMap<DataTypeListener, Set<EDataType>>();
220 this.lightweightObservers = CollectionsFactory.createMap();
221 this.observedFeatures = new HashMap<Object, IndexingLevel>();
222 this.ignoreResolveNotificationFeatures = new HashSet<Object>();
223 this.observedDataTypes = new HashMap<Object, IndexingLevel>();
224
225 metaStore = initMetaStore();
226 instanceStore = initInstanceStore();
227 statsStore = initStatStore();
228
229 this.contentAdapter = initContentAdapter();
230 this.baseIndexChangeListeners = new HashSet<EMFBaseIndexChangeListener>();
231 this.errorListeners = new LinkedHashSet<IEMFIndexingErrorListener>();
232
233 this.modelRoots = new HashSet<Notifier>();
234 this.expansionAllowed = false;
235 this.traversalDescendsAlongCrossResourceContainment = false;
236
237 if (emfRoot != null) {
238 addRootInternal(emfRoot);
239 }
240
241 }
242
243 @Override
244 public IndexingLevel getWildcardLevel() {
245 return wildcardMode;
246 }
247
248 @Override
249 public void setWildcardLevel(final IndexingLevel level) {
250 try{
251 IndexingLevel mergedLevel = NavigationHelperImpl.this.wildcardMode.merge(level);
252 if (mergedLevel != NavigationHelperImpl.this.wildcardMode){
253 NavigationHelperImpl.this.wildcardMode = mergedLevel;
254
255 // force traversal upon change of wildcard level
256 final NavigationHelperVisitor visitor = initTraversingVisitor(
257 Collections.<Object, IndexingLevel>emptyMap(), Collections.<Object, IndexingLevel>emptyMap(), Collections.<Object, IndexingLevel>emptyMap(), Collections.<Object, IndexingLevel>emptyMap());
258 coalesceTraversals(() -> traverse(visitor));
259 }
260 } catch (InvocationTargetException ex) {
261 processingFatal(ex.getCause(), "Setting wildcard level: " + level);
262 } catch (Exception ex) {
263 processingFatal(ex, "Setting wildcard level: " + level);
264 }
265 }
266
267 public NavigationHelperContentAdapter getContentAdapter() {
268 return contentAdapter;
269 }
270
271 public Map<Object, IndexingLevel> getObservedFeaturesInternal() {
272 return observedFeatures;
273 }
274
275 public boolean isFeatureResolveIgnored(EStructuralFeature feature) {
276 return ignoreResolveNotificationFeatures.contains(toKey(feature));
277 }
278
279 @Override
280 public void dispose() {
281 ensureNoListenersForDispose();
282 for (Notifier root : modelRoots) {
283 contentAdapter.removeAdapter(root);
284 }
285 }
286
287 @Override
288 public Set<Object> getDataTypeInstances(EDataType type) {
289 Object typeKey = toKey(type);
290 return Collections.unmodifiableSet(instanceStore.getDistinctDataTypeInstances(typeKey));
291 }
292
293 @Override
294 public boolean isInstanceOfDatatype(Object value, EDataType type) {
295 Object typeKey = toKey(type);
296 Set<Object> valMap = instanceStore.getDistinctDataTypeInstances(typeKey);
297 return valMap.contains(value);
298 }
299
300 protected FeatureData featureData(EStructuralFeature feature) {
301 return instanceStore.getFeatureData(toKey(feature));
302 }
303
304 @Override
305 public Set<Setting> findByAttributeValue(Object value_) {
306 Object value = toCanonicalValueRepresentation(value_);
307 return getSettingsForTarget(value);
308 }
309
310 @Override
311 public Set<Setting> findByAttributeValue(Object value_, Collection<EAttribute> attributes) {
312 Object value = toCanonicalValueRepresentation(value_);
313 Set<Setting> retSet = new HashSet<Setting>();
314
315 for (EAttribute attr : attributes) {
316 for (EObject holder : featureData(attr).getDistinctHoldersOfValue(value)) {
317 retSet.add(new NavigationHelperSetting(attr, holder, value));
318 }
319 }
320
321 return retSet;
322 }
323
324 @Override
325 public Set<EObject> findByAttributeValue(Object value_, EAttribute attribute) {
326 Object value = toCanonicalValueRepresentation(value_);
327 final Set<EObject> holders = featureData(attribute).getDistinctHoldersOfValue(value);
328 return Collections.unmodifiableSet(holders);
329 }
330
331 @Override
332 public void processAllFeatureInstances(EStructuralFeature feature, IStructuralFeatureInstanceProcessor processor) {
333 featureData(feature).forEach(processor);
334 }
335
336 @Override
337 public void processDirectInstances(EClass type, IEClassProcessor processor) {
338 Object typeKey = toKey(type);
339 processDirectInstancesInternal(type, processor, typeKey);
340 }
341
342 @Override
343 public void processAllInstances(EClass type, IEClassProcessor processor) {
344 Object typeKey = toKey(type);
345 Set<Object> subTypes = metaStore.getSubTypeMap().get(typeKey);
346 if (subTypes != null) {
347 for (Object subTypeKey : subTypes) {
348 processDirectInstancesInternal(type, processor, subTypeKey);
349 }
350 }
351 processDirectInstancesInternal(type, processor, typeKey);
352 }
353
354 @Override
355 public void processDataTypeInstances(EDataType type, IEDataTypeProcessor processor) {
356 Object typeKey = toKey(type);
357 for (Object value : instanceStore.getDistinctDataTypeInstances(typeKey)) {
358 processor.process(type, value);
359 }
360 }
361
362 protected void processDirectInstancesInternal(EClass type, IEClassProcessor processor, Object typeKey) {
363 final Set<EObject> instances = instanceStore.getInstanceSet(typeKey);
364 if (instances != null) {
365 for (EObject eObject : instances) {
366 processor.process(type, eObject);
367 }
368 }
369 }
370
371 @Override
372 public Set<Setting> getInverseReferences(EObject target) {
373 return getSettingsForTarget(target);
374 }
375
376 protected Set<Setting> getSettingsForTarget(Object target) {
377 Set<Setting> retSet = new HashSet<Setting>();
378 for (Object featureKey : instanceStore.getFeatureKeysPointingTo(target)) {
379 Set<EObject> holders = instanceStore.getFeatureData(featureKey).getDistinctHoldersOfValue(target);
380 for (EObject holder : holders) {
381 EStructuralFeature feature = metaStore.getKnownFeatureForKey(featureKey);
382 retSet.add(new NavigationHelperSetting(feature, holder, target));
383 }
384 }
385 return retSet;
386 }
387
388 @Override
389 public Set<Setting> getInverseReferences(EObject target, Collection<EReference> references) {
390 Set<Setting> retSet = new HashSet<>();
391 for (EReference ref : references) {
392 final Set<EObject> holders = featureData(ref).getDistinctHoldersOfValue(target);
393 for (EObject source : holders) {
394 retSet .add(new NavigationHelperSetting(ref, source, target));
395 }
396 }
397
398 return retSet;
399 }
400
401 @Override
402 public Set<EObject> getInverseReferences(EObject target, EReference reference) {
403 final Set<EObject> holders = featureData(reference).getDistinctHoldersOfValue(target);
404 return Collections.unmodifiableSet(holders);
405 }
406
407 @Override
408 @SuppressWarnings("unchecked")
409 public Set<EObject> getReferenceValues(EObject source, EReference reference) {
410 Set<Object> targets = getFeatureTargets(source, reference);
411 return (Set<EObject>) (Set<?>) targets; // this is known to be safe, as EReferences can only point to EObjects
412 }
413
414 @Override
415 public Set<Object> getFeatureTargets(EObject source, EStructuralFeature _feature) {
416 return Collections.unmodifiableSet(featureData(_feature).getDistinctValuesOfHolder(source));
417 }
418
419 @Override
420 public boolean isFeatureInstance(EObject source, Object target, EStructuralFeature _feature) {
421 return featureData(_feature).isInstance(source, target);
422 }
423
424 @Override
425 public Set<EObject> getDirectInstances(EClass type) {
426 Object typeKey = toKey(type);
427 Set<EObject> valSet = instanceStore.getInstanceSet(typeKey);
428 if (valSet == null) {
429 return Collections.emptySet();
430 } else {
431 return Collections.unmodifiableSet(valSet);
432 }
433 }
434
435 protected Object toKey(EClassifier eClassifier) {
436 return metaStore.toKey(eClassifier);
437 }
438
439 protected Object toKey(EStructuralFeature feature) {
440 return metaStore.toKey(feature);
441 }
442
443 @Override
444 public Object toCanonicalValueRepresentation(Object value) {
445 return metaStore.toInternalValueRepresentation(value);
446 }
447
448 @Override
449 public Set<EObject> getAllInstances(EClass type) {
450 Set<EObject> retSet = new HashSet<EObject>();
451
452 Object typeKey = toKey(type);
453 Set<Object> subTypes = metaStore.getSubTypeMap().get(typeKey);
454 if (subTypes != null) {
455 for (Object subTypeKey : subTypes) {
456 final Set<EObject> instances = instanceStore.getInstanceSet(subTypeKey);
457 if (instances != null) {
458 retSet.addAll(instances);
459 }
460 }
461 }
462 final Set<EObject> instances = instanceStore.getInstanceSet(typeKey);
463 if (instances != null) {
464 retSet.addAll(instances);
465 }
466
467 return retSet;
468 }
469
470 @Override
471 public boolean isInstanceOfUnscoped(EObject object, EClass clazz) {
472 Object candidateTypeKey = toKey(clazz);
473 Object typeKey = toKey(object.eClass());
474
475 return doCalculateInstanceOf(candidateTypeKey, typeKey);
476 }
477
478 @Override
479 public boolean isInstanceOfScoped(EObject object, EClass clazz) {
480 Object typeKey = toKey(object.eClass());
481 if (!doCalculateInstanceOf(toKey(clazz), typeKey)) {
482 return false;
483 }
484 final Set<EObject> instances = instanceStore.getInstanceSet(typeKey);
485 return instances != null && instances.contains(object);
486 }
487
488 protected boolean doCalculateInstanceOf(Object candidateTypeKey, Object typeKey) {
489 if (candidateTypeKey.equals(typeKey)) return true;
490 if (metaStore.getEObjectClassKey().equals(candidateTypeKey)) return true;
491
492 Set<Object> superTypes = metaStore.getSuperTypeMap().get(typeKey);
493 return superTypes.contains(candidateTypeKey);
494 }
495
496 @Override
497 public Set<EObject> findByFeatureValue(Object value_, EStructuralFeature _feature) {
498 Object value = toCanonicalValueRepresentation(value_);
499 return Collections.unmodifiableSet(featureData(_feature).getDistinctHoldersOfValue(value));
500 }
501
502 @Override
503 public Set<EObject> getHoldersOfFeature(EStructuralFeature _feature) {
504 Object feature = toKey(_feature);
505 return Collections.unmodifiableSet(instanceStore.getHoldersOfFeature(feature));
506 }
507 @Override
508 public Set<Object> getValuesOfFeature(EStructuralFeature _feature) {
509 Object feature = toKey(_feature);
510 return Collections.unmodifiableSet(instanceStore.getValuesOfFeature(feature));
511 }
512
513 @Override
514 public void addInstanceListener(Collection<EClass> classes, InstanceListener listener) {
515 Set<EClass> registered = this.subscribedInstanceListeners.computeIfAbsent(listener, l -> new HashSet<>());
516 Set<EClass> delta = setMinus(classes, registered);
517 if (!delta.isEmpty()) {
518 registered.addAll(delta);
519 if (instanceListeners != null) { // if already computed
520 for (EClass subscriptionType : delta) {
521 final Object superElementTypeKey = toKey(subscriptionType);
522 addInstanceListenerInternal(listener, subscriptionType, superElementTypeKey);
523 final Set<Object> subTypeKeys = metaStore.getSubTypeMap().get(superElementTypeKey);
524 if (subTypeKeys != null)
525 for (Object subTypeKey : subTypeKeys) {
526 addInstanceListenerInternal(listener, subscriptionType, subTypeKey);
527 }
528 }
529 }
530 }
531 }
532
533 @Override
534 public void removeInstanceListener(Collection<EClass> classes, InstanceListener listener) {
535 Set<EClass> restriction = this.subscribedInstanceListeners.get(listener);
536 if (restriction != null) {
537 boolean changed = restriction.removeAll(classes);
538 if (restriction.size() == 0) {
539 this.subscribedInstanceListeners.remove(listener);
540 }
541 if (changed)
542 instanceListeners = null; // recompute later on demand
543 }
544 }
545
546 @Override
547 public void addFeatureListener(Collection<? extends EStructuralFeature> features, FeatureListener listener) {
548 Set<EStructuralFeature> registered = this.subscribedFeatureListeners.computeIfAbsent(listener, l -> new HashSet<>());
549 Set<EStructuralFeature> delta = setMinus(features, registered);
550 if (!delta.isEmpty()) {
551 registered.addAll(delta);
552 if (featureListeners != null) { // if already computed
553 for (EStructuralFeature subscriptionType : delta) {
554 addFeatureListenerInternal(listener, subscriptionType, toKey(subscriptionType));
555 }
556 }
557 }
558 }
559
560 @Override
561 public void removeFeatureListener(Collection<? extends EStructuralFeature> features, FeatureListener listener) {
562 Collection<EStructuralFeature> restriction = this.subscribedFeatureListeners.get(listener);
563 if (restriction != null) {
564 boolean changed = restriction.removeAll(features);
565 if (restriction.size() == 0) {
566 this.subscribedFeatureListeners.remove(listener);
567 }
568 if (changed)
569 featureListeners = null; // recompute later on demand
570 }
571 }
572
573 @Override
574 public void addDataTypeListener(Collection<EDataType> types, DataTypeListener listener) {
575 Set<EDataType> registered = this.subscribedDataTypeListeners.computeIfAbsent(listener, l -> new HashSet<>());
576 Set<EDataType> delta = setMinus(types, registered);
577 if (!delta.isEmpty()) {
578 registered.addAll(delta);
579 if (dataTypeListeners != null) { // if already computed
580 for (EDataType subscriptionType : delta) {
581 addDatatypeListenerInternal(listener, subscriptionType, toKey(subscriptionType));
582 }
583 }
584 }
585 }
586
587 @Override
588 public void removeDataTypeListener(Collection<EDataType> types, DataTypeListener listener) {
589 Collection<EDataType> restriction = this.subscribedDataTypeListeners.get(listener);
590 if (restriction != null) {
591 boolean changed = restriction.removeAll(types);
592 if (restriction.size() == 0) {
593 this.subscribedDataTypeListeners.remove(listener);
594 }
595 if (changed)
596 dataTypeListeners = null; // recompute later on demand
597 }
598 }
599
600 /**
601 * @return the observedDataTypes
602 */
603 public Map<Object, IndexingLevel> getObservedDataTypesInternal() {
604 return observedDataTypes;
605 }
606
607 @Override
608 public boolean addLightweightEObjectObserver(LightweightEObjectObserver observer, EObject observedObject) {
609 Set<LightweightEObjectObserver> observers = lightweightObservers.computeIfAbsent(observedObject, CollectionsFactory::emptySet);
610 return observers.add(observer);
611 }
612
613 @Override
614 public boolean removeLightweightEObjectObserver(LightweightEObjectObserver observer, EObject observedObject) {
615 boolean result = false;
616 Set<LightweightEObjectObserver> observers = lightweightObservers.get(observedObject);
617 if (observers != null) {
618 result = observers.remove(observer);
619 if (observers.isEmpty()) {
620 lightweightObservers.remove(observedObject);
621 }
622 }
623 return result;
624 }
625
626 public void notifyBaseIndexChangeListeners() {
627 notifyBaseIndexChangeListeners(instanceStore.isDirty);
628 if (instanceStore.isDirty) {
629 instanceStore.isDirty = false;
630 }
631 }
632
633 /**
634 * This will run after updates.
635 */
636 protected void notifyBaseIndexChangeListeners(boolean baseIndexChanged) {
637 if (!baseIndexChangeListeners.isEmpty()) {
638 for (EMFBaseIndexChangeListener listener : new ArrayList<>(baseIndexChangeListeners)) {
639 try {
640 if (!listener.onlyOnIndexChange() || baseIndexChanged) {
641 listener.notifyChanged(baseIndexChanged);
642 }
643 } catch (Exception ex) {
644 notifyFatalListener("VIATRA Base encountered an error in delivering notifications about changes. ",
645 ex);
646 }
647 }
648 }
649 }
650
651 void notifyDataTypeListeners(final Object typeKey, final Object value, final boolean isInsertion,
652 final boolean firstOrLastOccurrence) {
653 for (final Entry<DataTypeListener, Set<EDataType>> entry : getDataTypeListeners().getOrDefault(typeKey, Collections.emptyMap()).entrySet()) {
654 final DataTypeListener listener = entry.getKey();
655 for (final EDataType subscriptionType : entry.getValue()) {
656 if (isInsertion) {
657 listener.dataTypeInstanceInserted(subscriptionType, value, firstOrLastOccurrence);
658 } else {
659 listener.dataTypeInstanceDeleted(subscriptionType, value, firstOrLastOccurrence);
660 }
661 }
662 }
663 }
664
665 void notifyFeatureListeners(final EObject host, final Object featureKey, final Object value,
666 final boolean isInsertion) {
667 for (final Entry<FeatureListener, Set<EStructuralFeature>> entry : getFeatureListeners().getOrDefault(featureKey, Collections.emptyMap())
668 .entrySet()) {
669 final FeatureListener listener = entry.getKey();
670 for (final EStructuralFeature subscriptionType : entry.getValue()) {
671 if (isInsertion) {
672 listener.featureInserted(host, subscriptionType, value);
673 } else {
674 listener.featureDeleted(host, subscriptionType, value);
675 }
676 }
677 }
678 }
679
680 void notifyInstanceListeners(final Object clazzKey, final EObject instance, final boolean isInsertion) {
681 for (final Entry<InstanceListener, Set<EClass>> entry : getInstanceListeners().getOrDefault(clazzKey, Collections.emptyMap()).entrySet()) {
682 final InstanceListener listener = entry.getKey();
683 for (final EClass subscriptionType : entry.getValue()) {
684 if (isInsertion) {
685 listener.instanceInserted(subscriptionType, instance);
686 } else {
687 listener.instanceDeleted(subscriptionType, instance);
688 }
689 }
690 }
691 }
692
693 void notifyLightweightObservers(final EObject host, final EStructuralFeature feature,
694 final Notification notification) {
695 if (lightweightObservers.containsKey(host)) {
696 Set<LightweightEObjectObserver> observers = lightweightObservers.get(host);
697 for (final LightweightEObjectObserver observer : observers) {
698 observer.notifyFeatureChanged(host, feature, notification);
699 }
700 }
701 }
702
703 @Override
704 public void addBaseIndexChangeListener(EMFBaseIndexChangeListener listener) {
705 Preconditions.checkArgument(listener != null, "Cannot add null listener!");
706 baseIndexChangeListeners.add(listener);
707 }
708
709 @Override
710 public void removeBaseIndexChangeListener(EMFBaseIndexChangeListener listener) {
711 Preconditions.checkArgument(listener != null, "Cannot remove null listener!");
712 baseIndexChangeListeners.remove(listener);
713 }
714
715 @Override
716 public boolean addIndexingErrorListener(IEMFIndexingErrorListener listener) {
717 return errorListeners.add(listener);
718 }
719
720 @Override
721 public boolean removeIndexingErrorListener(IEMFIndexingErrorListener listener) {
722 return errorListeners.remove(listener);
723 }
724
725 protected void processingFatal(final Throwable ex, final String task) {
726 notifyFatalListener(logTaskFormat(task), ex);
727 }
728
729 protected void processingError(final Throwable ex, final String task) {
730 notifyErrorListener(logTaskFormat(task), ex);
731 }
732
733 public void notifyErrorListener(String message, Throwable t) {
734 logger.error(message, t);
735 for (IEMFIndexingErrorListener listener : new ArrayList<>(errorListeners)) {
736 listener.error(message, t);
737 }
738 }
739
740 public void notifyFatalListener(String message, Throwable t) {
741 logger.fatal(message, t);
742 for (IEMFIndexingErrorListener listener : new ArrayList<>(errorListeners)) {
743 listener.fatal(message, t);
744 }
745 }
746
747 protected String logTaskFormat(final String task) {
748 return "VIATRA Query encountered an error in processing the EMF model. " + "This happened while trying to "
749 + task;
750 }
751
752 protected void considerForExpansion(EObject obj) {
753 if (expansionAllowed) {
754 Resource eResource = obj.eResource();
755 if (eResource != null && eResource.getResourceSet() == null) {
756 expandToAdditionalRoot(eResource);
757 }
758 }
759 }
760
761 protected void expandToAdditionalRoot(Notifier root) {
762 if (modelRoots.contains(root))
763 return;
764
765 if (root instanceof ResourceSet) {
766 expansionAllowed = true;
767 } else if (root instanceof Resource) {
768 IBaseIndexResourceFilter resourceFilter = baseIndexOptions.getResourceFilterConfiguration();
769 if (resourceFilter != null && resourceFilter.isResourceFiltered((Resource) root))
770 return;
771 } else { // root instanceof EObject
772 traversalDescendsAlongCrossResourceContainment = true;
773 }
774 final IBaseIndexObjectFilter objectFilter = baseIndexOptions.getObjectFilterConfiguration();
775 if (objectFilter != null && objectFilter.isFiltered(root))
776 return;
777
778 // no veto by filters
779 modelRoots.add(root);
780 contentAdapter.addAdapter(root);
781 notifyBaseIndexChangeListeners();
782 }
783
784 /**
785 * @return the expansionAllowed
786 */
787 public boolean isExpansionAllowed() {
788 return expansionAllowed;
789 }
790
791 public boolean traversalDescendsAlongCrossResourceContainment() {
792 return traversalDescendsAlongCrossResourceContainment;
793 }
794
795 /**
796 * @return the directlyObservedClasses
797 */
798 public Set<Object> getDirectlyObservedClassesInternal() {
799 return directlyObservedClasses.keySet();
800 }
801
802 boolean isObservedInternal(Object clazzKey) {
803 return isInWildcardMode() || getAllObservedClassesInternal().containsKey(clazzKey);
804 }
805
806 /**
807 * Add the given item the map with the given indexing level if it wasn't already added with a higher level.
808 * @param level non-null
809 * @return whether actually changed
810 */
811 protected static <V> boolean putIntoMapMerged(Map<V, IndexingLevel> map, V key, IndexingLevel level) {
812 IndexingLevel l = map.get(key);
813 IndexingLevel merged = level.merge(l);
814 if (merged != l) {
815 map.put(key, merged);
816 return true;
817 } else {
818 return false;
819 }
820 }
821
822 /**
823 * @return true if actually changed
824 */
825 protected boolean addObservedClassesInternal(Object eClassKey, IndexingLevel level) {
826 boolean changed = putIntoMapMerged(allObservedClasses, eClassKey, level);
827 if (!changed) return false;
828
829 final Set<Object> subTypes = metaStore.getSubTypeMap().get(eClassKey);
830 if (subTypes != null) {
831 for (Object subType : subTypes) {
832 /*
833 * It is necessary to check if the class has already been added with a higher indexing level as in case
834 * of multiple inheritance, a subclass may be registered for statistics only but full indexing may be
835 * required via one of its super classes.
836 */
837 putIntoMapMerged(allObservedClasses, subType, level);
838 }
839 }
840 return true;
841 }
842
843 /**
844 * not just the directly observed classes, but also their known subtypes
845 */
846 public Map<Object, IndexingLevel> getAllObservedClassesInternal() {
847 if (allObservedClasses == null) {
848 allObservedClasses = new HashMap<Object, IndexingLevel>();
849 for (Entry<Object, IndexingLevel> entry : directlyObservedClasses.entrySet()) {
850 Object eClassKey = entry.getKey();
851 IndexingLevel level = entry.getValue();
852 addObservedClassesInternal(eClassKey, level);
853 }
854 }
855 return allObservedClasses;
856 }
857
858 /**
859 * @return the instanceListeners
860 */
861 Map<Object, Map<InstanceListener, Set<EClass>>> getInstanceListeners() {
862 if (instanceListeners == null) {
863 instanceListeners = CollectionsFactory.createMap();
864 for (Entry<InstanceListener, Set<EClass>> subscription : subscribedInstanceListeners.entrySet()) {
865 final InstanceListener listener = subscription.getKey();
866 for (EClass subscriptionType : subscription.getValue()) {
867 final Object superElementTypeKey = toKey(subscriptionType);
868 addInstanceListenerInternal(listener, subscriptionType, superElementTypeKey);
869 final Set<Object> subTypeKeys = metaStore.getSubTypeMap().get(superElementTypeKey);
870 if (subTypeKeys != null)
871 for (Object subTypeKey : subTypeKeys) {
872 addInstanceListenerInternal(listener, subscriptionType, subTypeKey);
873 }
874 }
875 }
876 }
877 return instanceListeners;
878 }
879
880 Map<Object, Map<InstanceListener, Set<EClass>>> peekInstanceListeners() {
881 return instanceListeners;
882 }
883
884 void addInstanceListenerInternal(final InstanceListener listener, EClass subscriptionType,
885 final Object elementTypeKey) {
886 Set<EClass> subscriptionTypes = instanceListeners
887 .computeIfAbsent(elementTypeKey, k -> CollectionsFactory.createMap())
888 .computeIfAbsent(listener, k -> CollectionsFactory.createSet());
889 subscriptionTypes.add(subscriptionType);
890 }
891
892 /**
893 * @return the featureListeners
894 */
895 Map<Object, Map<FeatureListener, Set<EStructuralFeature>>> getFeatureListeners() {
896 if (featureListeners == null) {
897 featureListeners = CollectionsFactory.createMap();
898 for (Entry<FeatureListener, Set<EStructuralFeature>> subscription : subscribedFeatureListeners.entrySet()) {
899 final FeatureListener listener = subscription.getKey();
900 for (EStructuralFeature subscriptionType : subscription.getValue()) {
901 final Object elementTypeKey = toKey(subscriptionType);
902 addFeatureListenerInternal(listener, subscriptionType, elementTypeKey);
903 }
904 }
905 }
906 return featureListeners;
907 }
908
909 void addFeatureListenerInternal(final FeatureListener listener, EStructuralFeature subscriptionType,
910 final Object elementTypeKey) {
911 Set<EStructuralFeature> subscriptionTypes = featureListeners
912 .computeIfAbsent(elementTypeKey, k -> CollectionsFactory.createMap())
913 .computeIfAbsent(listener, k -> CollectionsFactory.createSet());
914 subscriptionTypes.add(subscriptionType);
915 }
916
917 /**
918 * @return the dataTypeListeners
919 */
920 Map<Object, Map<DataTypeListener, Set<EDataType>>> getDataTypeListeners() {
921 if (dataTypeListeners == null) {
922 dataTypeListeners = CollectionsFactory.createMap();
923 for (Entry<DataTypeListener, Set<EDataType>> subscription : subscribedDataTypeListeners.entrySet()) {
924 final DataTypeListener listener = subscription.getKey();
925 for (EDataType subscriptionType : subscription.getValue()) {
926 final Object elementTypeKey = toKey(subscriptionType);
927 addDatatypeListenerInternal(listener, subscriptionType, elementTypeKey);
928 }
929 }
930 }
931 return dataTypeListeners;
932 }
933
934 void addDatatypeListenerInternal(final DataTypeListener listener, EDataType subscriptionType,
935 final Object elementTypeKey) {
936 Set<EDataType> subscriptionTypes = dataTypeListeners
937 .computeIfAbsent(elementTypeKey, k -> CollectionsFactory.createMap())
938 .computeIfAbsent(listener, k -> CollectionsFactory.createSet());
939 subscriptionTypes.add(subscriptionType);
940 }
941
942 public void registerObservedTypes(Set<EClass> classes, Set<EDataType> dataTypes,
943 Set<? extends EStructuralFeature> features) {
944 registerObservedTypes(classes, dataTypes, features, IndexingLevel.FULL);
945 }
946
947 @Override
948 public void registerObservedTypes(Set<EClass> classes, Set<EDataType> dataTypes,
949 Set<? extends EStructuralFeature> features, final IndexingLevel level) {
950 if (isRegistrationNecessary(level) && (classes != null || features != null || dataTypes != null)) {
951 final Set<Object> resolvedFeatures = resolveFeaturesToKey(features);
952 final Set<Object> resolvedClasses = resolveClassifiersToKey(classes);
953 final Set<Object> resolvedDatatypes = resolveClassifiersToKey(dataTypes);
954
955 try {
956 coalesceTraversals(() -> {
957 Function<Object, IndexingLevel> f = input -> level;
958 delayedFeatures.putAll(resolvedFeatures.stream().collect(Collectors.toMap(identity(), f)));
959 delayedDataTypes.putAll(resolvedDatatypes.stream().collect(Collectors.toMap(identity(), f)));
960 delayedClasses.putAll(resolvedClasses.stream().collect(Collectors.toMap(identity(), f)));
961 });
962 } catch (InvocationTargetException ex) {
963 processingFatal(ex.getCause(), "register en masse the observed EClasses " + resolvedClasses
964 + " and EDatatypes " + resolvedDatatypes + " and EStructuralFeatures " + resolvedFeatures);
965 } catch (Exception ex) {
966 processingFatal(ex, "register en masse the observed EClasses " + resolvedClasses + " and EDatatypes "
967 + resolvedDatatypes + " and EStructuralFeatures " + resolvedFeatures);
968 }
969 }
970 }
971
972 @Override
973 public void unregisterObservedTypes(Set<EClass> classes, Set<EDataType> dataTypes,
974 Set<? extends EStructuralFeature> features) {
975 unregisterEClasses(classes);
976 unregisterEDataTypes(dataTypes);
977 unregisterEStructuralFeatures(features);
978 }
979
980 @Override
981 public void registerEStructuralFeatures(Set<? extends EStructuralFeature> features, final IndexingLevel level) {
982 if (isRegistrationNecessary(level) && features != null) {
983 final Set<Object> resolved = resolveFeaturesToKey(features);
984
985 try {
986 coalesceTraversals(() -> resolved.forEach(o -> delayedFeatures.put(o, level)));
987 } catch (InvocationTargetException ex) {
988 processingFatal(ex.getCause(), "register the observed EStructuralFeatures: " + resolved);
989 } catch (Exception ex) {
990 processingFatal(ex, "register the observed EStructuralFeatures: " + resolved);
991 }
992 }
993 }
994
995 @Override
996 public void unregisterEStructuralFeatures(Set<? extends EStructuralFeature> features) {
997 if (isRegistrationNecessary(IndexingLevel.FULL) && features != null) {
998 final Set<Object> resolved = resolveFeaturesToKey(features);
999 ensureNoListeners(resolved, getFeatureListeners());
1000 observedFeatures.keySet().removeAll(resolved);
1001 delayedFeatures.keySet().removeAll(resolved);
1002 for (Object f : resolved) {
1003 instanceStore.forgetFeature(f);
1004 statsStore.removeType(f);
1005 }
1006 }
1007 }
1008
1009 @Override
1010 public void registerEClasses(Set<EClass> classes, final IndexingLevel level) {
1011 if (isRegistrationNecessary(level) && classes != null) {
1012 final Set<Object> resolvedClasses = resolveClassifiersToKey(classes);
1013
1014 try {
1015 coalesceTraversals(() -> resolvedClasses.forEach(o -> delayedClasses.put(o, level)));
1016 } catch (InvocationTargetException ex) {
1017 processingFatal(ex.getCause(), "register the observed EClasses: " + resolvedClasses);
1018 } catch (Exception ex) {
1019 processingFatal(ex, "register the observed EClasses: " + resolvedClasses);
1020 }
1021 }
1022 }
1023
1024 /**
1025 * @return true if there is an actual change in the transitively computed observation levels,
1026 * warranting an actual traversal
1027 */
1028 protected boolean startObservingClasses(Map<Object, IndexingLevel> requestedClassObservations) {
1029 boolean warrantsTraversal = false;
1030 getAllObservedClassesInternal(); // pre-populate
1031 for (Entry<Object, IndexingLevel> request : requestedClassObservations.entrySet()) {
1032 if (putIntoMapMerged(directlyObservedClasses, request.getKey(), request.getValue())) {
1033 // maybe already observed for the sake of a supertype?
1034 if (addObservedClassesInternal(request.getKey(), request.getValue())) {
1035 warrantsTraversal = true;
1036 };
1037 }
1038 }
1039 return warrantsTraversal;
1040 }
1041
1042 @Override
1043 public void unregisterEClasses(Set<EClass> classes) {
1044 if (isRegistrationNecessary(IndexingLevel.FULL) && classes != null) {
1045 final Set<Object> resolved = resolveClassifiersToKey(classes);
1046 ensureNoListeners(resolved, getInstanceListeners());
1047 directlyObservedClasses.keySet().removeAll(resolved);
1048 allObservedClasses = null;
1049 delayedClasses.keySet().removeAll(resolved);
1050 for (Object c : resolved) {
1051 instanceStore.removeInstanceSet(c);
1052 statsStore.removeType(c);
1053 }
1054 }
1055 }
1056
1057 @Override
1058 public void registerEDataTypes(Set<EDataType> dataTypes, final IndexingLevel level) {
1059 if (isRegistrationNecessary(level) && dataTypes != null) {
1060 final Set<Object> resolved = resolveClassifiersToKey(dataTypes);
1061
1062 try {
1063 coalesceTraversals(() -> resolved.forEach(o -> delayedDataTypes.put(o, level)));
1064 } catch (InvocationTargetException ex) {
1065 processingFatal(ex.getCause(), "register the observed EDataTypes: " + resolved);
1066 } catch (Exception ex) {
1067 processingFatal(ex, "register the observed EDataTypes: " + resolved);
1068 }
1069 }
1070 }
1071
1072 @Override
1073 public void unregisterEDataTypes(Set<EDataType> dataTypes) {
1074 if (isRegistrationNecessary(IndexingLevel.FULL) && dataTypes != null) {
1075 final Set<Object> resolved = resolveClassifiersToKey(dataTypes);
1076 ensureNoListeners(resolved, getDataTypeListeners());
1077 observedDataTypes.keySet().removeAll(resolved);
1078 delayedDataTypes.keySet().removeAll(resolved);
1079 for (Object dataType : resolved) {
1080 instanceStore.removeDataTypeMap(dataType);
1081 statsStore.removeType(dataType);
1082 }
1083 }
1084 }
1085
1086 @Override
1087 public boolean isCoalescing() {
1088 return delayTraversals;
1089 }
1090
1091 public void coalesceTraversals(final Runnable runnable) throws InvocationTargetException {
1092 coalesceTraversals(() -> {
1093 runnable.run();
1094 return null;
1095 });
1096 }
1097
1098 @Override
1099 public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException {
1100 V finalResult = null;
1101
1102 if (delayTraversals) { // reentrant case, no special action needed
1103 try {
1104 finalResult = callable.call();
1105 } catch (Exception e) {
1106 throw new InvocationTargetException(e);
1107 }
1108 return finalResult;
1109 }
1110
1111 boolean firstRun = true;
1112 while (callable != null) { // repeat if post-processing needed
1113
1114 try {
1115 delayTraversals = true;
1116
1117 V result = callable.call();
1118 if (firstRun) {
1119 firstRun = false;
1120 finalResult = result;
1121 }
1122
1123 // are there proxies left to be resolved? are we allowed to resolve them now?
1124 while ((!delayedProxyResolutions.isEmpty()) && resolutionDelayingResources.isEmpty()) {
1125 // pop first entry
1126 EObject toResolveObject = delayedProxyResolutions.distinctKeys().iterator().next();
1127 EReference toResolveReference = delayedProxyResolutions.lookup(toResolveObject).iterator().next();
1128 delayedProxyResolutions.removePair(toResolveObject, toResolveReference);
1129
1130 // see if we can resolve proxies
1131 comprehension.tryResolveReference(toResolveObject, toResolveReference);
1132 }
1133
1134 delayTraversals = false;
1135 callable = considerRevisit();
1136 } catch (Exception e) {
1137 // since this is a fatal error, it is OK if delayTraversals remains true,
1138 // hence no need for a try-finally block
1139
1140 notifyFatalListener(
1141 "VIATRA Base encountered an error while traversing the EMF model to gather new information. ",
1142 e);
1143 throw new InvocationTargetException(e);
1144 }
1145 }
1146 executeTraversalCallbacks();
1147 return finalResult;
1148 }
1149
1150 protected <V> Callable<V> considerRevisit() {
1151 // has there been any requests for a retraversal at all?
1152 if (!delayedClasses.isEmpty() || !delayedFeatures.isEmpty() || !delayedDataTypes.isEmpty()) {
1153 // make copies of requested types so that
1154 // (a) original accumulators can be cleaned for the next cycle, also
1155 // (b) to remove entries that are already covered, or
1156 // (c) for the rare case that a coalesced traversal is invoked during visitation,
1157 // e.g. by a derived feature implementation
1158 // initialize the collections empty (but with capacity), fill with new entries
1159 final Map<Object, IndexingLevel> toGatherClasses = new HashMap<Object, IndexingLevel>(delayedClasses.size());
1160 final Map<Object, IndexingLevel> toGatherFeatures = new HashMap<Object, IndexingLevel>(delayedFeatures.size());
1161 final Map<Object, IndexingLevel> toGatherDataTypes = new HashMap<Object, IndexingLevel>(delayedDataTypes.size());
1162
1163 for (Entry<Object, IndexingLevel> requested : delayedFeatures.entrySet()) {
1164 Object typekey = requested.getKey();
1165 IndexingLevel old = observedFeatures.get(typekey);
1166 IndexingLevel merged = requested.getValue().merge(old);
1167 if (merged != old) toGatherFeatures.put(typekey, merged);
1168 }
1169 for (Entry<Object, IndexingLevel> requested : delayedClasses.entrySet()) {
1170 Object typekey = requested.getKey();
1171 IndexingLevel old = directlyObservedClasses.get(typekey);
1172 IndexingLevel merged = requested.getValue().merge(old);
1173 if (merged != old) toGatherClasses.put(typekey, merged);
1174 }
1175 for (Entry<Object, IndexingLevel> requested : delayedDataTypes.entrySet()) {
1176 Object typekey = requested.getKey();
1177 IndexingLevel old = observedDataTypes.get(typekey);
1178 IndexingLevel merged = requested.getValue().merge(old);
1179 if (merged != old) toGatherDataTypes.put(typekey, merged);
1180 }
1181
1182 delayedClasses.clear();
1183 delayedFeatures.clear();
1184 delayedDataTypes.clear();
1185
1186 // check if the filtered request sets are empty
1187 // - could be false alarm if we already observe all of them
1188 if (!toGatherClasses.isEmpty() || !toGatherFeatures.isEmpty() || !toGatherDataTypes.isEmpty()) {
1189 final HashMap<Object, IndexingLevel> oldClasses = new HashMap<Object, IndexingLevel>(
1190 directlyObservedClasses);
1191
1192 /* Instance indexing would add extra entries to the statistics store, so we have to clean the
1193 * appropriate entries. If no re-traversal is required, it is detected earlier; at this point we
1194 * only have to consider the target indexing level. See bug
1195 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=518356 for more details.
1196 *
1197 * This has to be executed before the old observed types are updated to check whether the indexing level increased.
1198 *
1199 * Technically, the statsStore cleanup seems only necessary for EDataTypes; otherwise everything
1200 * works as expected, but it seems a better idea to do the cleanup for all types in the same way */
1201 toGatherClasses.forEach((key, value) -> {
1202 IndexingLevel oldIndexingLevel = getIndexingLevel(metaStore.getKnownClassifierForKey(key));
1203 if (value.hasInstances() && oldIndexingLevel.hasStatistics() && !oldIndexingLevel.hasInstances()) {
1204 statsStore.removeType(key);
1205 }
1206
1207 });
1208 toGatherFeatures.forEach((key, value) -> {
1209 IndexingLevel oldIndexingLevel = getIndexingLevel(metaStore.getKnownFeatureForKey(key));
1210 if (value.hasInstances() && oldIndexingLevel.hasStatistics() && !oldIndexingLevel.hasInstances()) {
1211 statsStore.removeType(key);
1212 }
1213
1214 });
1215 toGatherDataTypes.forEach((key, value) -> {
1216 IndexingLevel oldIndexingLevel = getIndexingLevel(metaStore.getKnownClassifierForKey(key));
1217 if (value.hasInstances() && oldIndexingLevel.hasStatistics() && !oldIndexingLevel.hasInstances()) {
1218 statsStore.removeType(key);
1219 }
1220
1221 });
1222
1223 // Are there new classes to be observed that are not available via superclasses?
1224 // (at sufficient level)
1225 // if yes, model traversal needed
1226 // if not, index can be updated without retraversal
1227 boolean classesWarrantTraversal = startObservingClasses(toGatherClasses);
1228 observedDataTypes.putAll(toGatherDataTypes);
1229 observedFeatures.putAll(toGatherFeatures);
1230
1231
1232 // So, is an actual traversal needed, or are we done?
1233 if (classesWarrantTraversal || !toGatherFeatures.isEmpty() || !toGatherDataTypes.isEmpty()) {
1234 // repeat the cycle with this visit
1235 final NavigationHelperVisitor visitor = initTraversingVisitor(toGatherClasses, toGatherFeatures, toGatherDataTypes, oldClasses);
1236
1237 return new Callable<V>() {
1238 @Override
1239 public V call() throws Exception {
1240 // temporarily ignoring RESOLVE on these features, as they were not observed before
1241 ignoreResolveNotificationFeatures.addAll(toGatherFeatures.keySet());
1242 try {
1243 traverse(visitor);
1244 } finally {
1245 ignoreResolveNotificationFeatures.removeAll(toGatherFeatures.keySet());
1246 }
1247 return null;
1248 }
1249 };
1250
1251 }
1252 }
1253 }
1254
1255 return null; // no callable -> no further action
1256 }
1257
1258 protected void executeTraversalCallbacks() throws InvocationTargetException{
1259 final Runnable[] callbacks = traversalCallbacks.toArray(new Runnable[traversalCallbacks.size()]);
1260 traversalCallbacks.clear();
1261 if (callbacks.length > 0){
1262 coalesceTraversals(() -> Arrays.stream(callbacks).forEach(Runnable::run));
1263 }
1264 }
1265
1266 protected void traverse(final NavigationHelperVisitor visitor) {
1267 // Cloning model roots avoids a concurrent modification exception
1268 for (Notifier root : new HashSet<Notifier>(modelRoots)) {
1269 comprehension.traverseModel(visitor, root);
1270 }
1271 notifyBaseIndexChangeListeners();
1272 }
1273
1274 /**
1275 * Returns a stream of model roots registered to the navigation helper instance
1276 * @since 2.3
1277 */
1278 protected Stream<Notifier> getModelRoots() {
1279 return modelRoots.stream();
1280 }
1281
1282 @Override
1283 public void addRoot(Notifier emfRoot) {
1284 addRootInternal(emfRoot);
1285 }
1286
1287 /**
1288 * Supports removing model roots
1289 * </p>
1290 * Note: for now this API is considered experimental thus it is not added to the {@link NavigationHelper} interface.
1291 * @since 2.3
1292 */
1293 protected void removeRoot(Notifier root) {
1294 if (!((root instanceof EObject) || (root instanceof Resource) || (root instanceof ResourceSet))) {
1295 throw new ViatraBaseException(ViatraBaseException.INVALID_EMFROOT);
1296 }
1297
1298 if (!modelRoots.contains(root))
1299 return;
1300
1301 if (root instanceof Resource) {
1302 IBaseIndexResourceFilter resourceFilter = getBaseIndexOptions().getResourceFilterConfiguration();
1303 if (resourceFilter != null && resourceFilter.isResourceFiltered((Resource) root))
1304 return;
1305 }
1306 final IBaseIndexObjectFilter objectFilter = getBaseIndexOptions().getObjectFilterConfiguration();
1307 if (objectFilter != null && objectFilter.isFiltered(root))
1308 return;
1309
1310 // no veto by filters
1311 modelRoots.remove(root);
1312 contentAdapter.removeAdapter(root);
1313 notifyBaseIndexChangeListeners();
1314 }
1315
1316 @Override
1317 public <T extends EObject> void cheapMoveTo(T element, EList<T> targetContainmentReferenceList) {
1318 if (element.eAdapters().contains(contentAdapter)
1319 && targetContainmentReferenceList instanceof NotifyingList<?>) {
1320 final Object listNotifier = ((NotifyingList<?>) targetContainmentReferenceList).getNotifier();
1321 if (listNotifier instanceof Notifier && ((Notifier) listNotifier).eAdapters().contains(contentAdapter)) {
1322 contentAdapter.ignoreInsertionAndDeletion = element;
1323 try {
1324 targetContainmentReferenceList.add(element);
1325 } finally {
1326 contentAdapter.ignoreInsertionAndDeletion = null;
1327 }
1328 } else {
1329 targetContainmentReferenceList.add(element);
1330 }
1331 } else {
1332 targetContainmentReferenceList.add(element);
1333 }
1334 }
1335
1336 @SuppressWarnings({ "unchecked", "rawtypes" })
1337 @Override
1338 public void cheapMoveTo(EObject element, EObject parent, EReference containmentFeature) {
1339 metaStore.maintainMetamodel(containmentFeature);
1340 if (containmentFeature.isMany())
1341 cheapMoveTo(element, (EList) parent.eGet(containmentFeature));
1342 else if (element.eAdapters().contains(contentAdapter) && parent.eAdapters().contains(contentAdapter)) {
1343 contentAdapter.ignoreInsertionAndDeletion = element;
1344 try {
1345 parent.eSet(containmentFeature, element);
1346 } finally {
1347 contentAdapter.ignoreInsertionAndDeletion = null;
1348 }
1349 } else {
1350 parent.eSet(containmentFeature, element);
1351 }
1352 }
1353
1354 protected void addRootInternal(Notifier emfRoot) {
1355 if (!((emfRoot instanceof EObject) || (emfRoot instanceof Resource) || (emfRoot instanceof ResourceSet))) {
1356 throw new ViatraBaseException(ViatraBaseException.INVALID_EMFROOT);
1357 }
1358 expandToAdditionalRoot(emfRoot);
1359 }
1360
1361 @Override
1362 public Set<EClass> getAllCurrentClasses() {
1363 return instanceStore.getAllCurrentClasses();
1364 }
1365
1366 protected boolean isRegistrationNecessary(IndexingLevel level) {
1367 boolean inWildcardMode = isInWildcardMode(level);
1368 if (inWildcardMode && !loggedRegistrationMessage) {
1369 loggedRegistrationMessage = true;
1370 logger.warn("Type registration/unregistration not required in wildcard mode. This message will not be repeated for future occurences.");
1371 }
1372 return !inWildcardMode;
1373 }
1374
1375 protected <X, Y> void ensureNoListeners(Set<Object> unobservedTypes,
1376 final Map<Object, Map<X, Set<Y>>> listenerRegistry) {
1377 if (!Collections.disjoint(unobservedTypes, listenerRegistry.keySet()))
1378 throw new IllegalStateException("Cannot unregister observed types for which there are active listeners");
1379 }
1380
1381 protected void ensureNoListenersForDispose() {
1382 if (!(baseIndexChangeListeners.isEmpty() && subscribedFeatureListeners.isEmpty()
1383 && subscribedDataTypeListeners.isEmpty() && subscribedInstanceListeners.isEmpty()))
1384 throw new IllegalStateException("Cannot dispose while there are active listeners");
1385 }
1386
1387 /**
1388 * Resamples the values of not well-behaving derived features if those features are also indexed.
1389 */
1390 @Override
1391 public void resampleDerivedFeatures() {
1392 // otherwise notifications are delivered anyway
1393 if (!baseIndexOptions.isTraverseOnlyWellBehavingDerivedFeatures()) {
1394 // get all required classes
1395 Set<EClass> allCurrentClasses = instanceStore.getAllCurrentClasses();
1396 Set<EStructuralFeature> featuresToSample = new HashSet<>();
1397 // collect features to sample
1398 for (EClass cls : allCurrentClasses) {
1399 EList<EStructuralFeature> features = cls.getEAllStructuralFeatures();
1400 for (EStructuralFeature f : features) {
1401 // is feature only sampled?
1402 if (comprehension.onlySamplingFeature(f)) {
1403 featuresToSample.add(f);
1404 }
1405 }
1406 }
1407
1408 final EMFVisitor removalVisitor = contentAdapter.getVisitorForChange(false);
1409 final EMFVisitor insertionVisitor = contentAdapter.getVisitorForChange(true);
1410
1411 // iterate on instances
1412 for (final EStructuralFeature f : featuresToSample) {
1413 EClass containingClass = f.getEContainingClass();
1414 processAllInstances(containingClass, (type, instance) ->
1415 resampleFeatureValueForHolder(instance, f, insertionVisitor, removalVisitor));
1416 }
1417 notifyBaseIndexChangeListeners();
1418 }
1419 }
1420
1421 protected void resampleFeatureValueForHolder(EObject source, EStructuralFeature feature,
1422 EMFVisitor insertionVisitor, EMFVisitor removalVisitor) {
1423 // traverse features and update value
1424 Object newValue = source.eGet(feature);
1425 Set<Object> oldValues = instanceStore.getOldValuesForHolderAndFeature(source, toKey(feature));
1426 if (feature.isMany()) {
1427 resampleManyFeatureValueForHolder(source, feature, newValue, oldValues, insertionVisitor, removalVisitor);
1428 } else {
1429 resampleSingleFeatureValueForHolder(source, feature, newValue, oldValues, insertionVisitor, removalVisitor);
1430 }
1431
1432 }
1433
1434 protected void resampleManyFeatureValueForHolder(EObject source, EStructuralFeature feature, Object newValue,
1435 Set<Object> oldValues, EMFVisitor insertionVisitor, EMFVisitor removalVisitor) {
1436 InternalEObject internalEObject = (InternalEObject) source;
1437 Collection<?> newValues = (Collection<?>) newValue;
1438 // add those that are in new but not in old
1439 Set<Object> newValueSet = new HashSet<Object>(newValues);
1440 newValueSet.removeAll(oldValues);
1441 // remove those that are in old but not in new
1442 oldValues.removeAll(newValues);
1443 if (!oldValues.isEmpty()) {
1444 for (Object ov : oldValues) {
1445 comprehension.traverseFeature(removalVisitor, source, feature, ov, null);
1446 }
1447 ENotificationImpl removeNotification = new ENotificationImpl(internalEObject, Notification.REMOVE_MANY,
1448 feature, oldValues, null);
1449 notifyLightweightObservers(source, feature, removeNotification);
1450 }
1451 if (!newValueSet.isEmpty()) {
1452 for (Object nv : newValueSet) {
1453 comprehension.traverseFeature(insertionVisitor, source, feature, nv, null);
1454 }
1455 ENotificationImpl addNotification = new ENotificationImpl(internalEObject, Notification.ADD_MANY, feature,
1456 null, newValueSet);
1457 notifyLightweightObservers(source, feature, addNotification);
1458 }
1459 }
1460
1461 protected void resampleSingleFeatureValueForHolder(EObject source, EStructuralFeature feature, Object newValue,
1462 Set<Object> oldValues, EMFVisitor insertionVisitor, EMFVisitor removalVisitor) {
1463 InternalEObject internalEObject = (InternalEObject) source;
1464 Object oldValue = oldValues.stream().findFirst().orElse(null);
1465 if (!Objects.equals(oldValue, newValue)) {
1466 // value changed
1467 comprehension.traverseFeature(removalVisitor, source, feature, oldValue, null);
1468 comprehension.traverseFeature(insertionVisitor, source, feature, newValue, null);
1469 ENotificationImpl notification = new ENotificationImpl(internalEObject, Notification.SET, feature, oldValue,
1470 newValue);
1471 notifyLightweightObservers(source, feature, notification);
1472 }
1473 }
1474
1475 @Override
1476 public int countAllInstances(EClass type) {
1477 int result = 0;
1478
1479 Object typeKey = toKey(type);
1480 Set<Object> subTypes = metaStore.getSubTypeMap().get(typeKey);
1481 if (subTypes != null) {
1482 for (Object subTypeKey : subTypes) {
1483 result += statsStore.countInstances(subTypeKey);
1484 }
1485 }
1486 result += statsStore.countInstances(typeKey);
1487
1488 return result;
1489 }
1490
1491 @Override
1492 public int countDataTypeInstances(EDataType dataType) {
1493 return statsStore.countInstances(toKey(dataType));
1494 }
1495
1496 @Override
1497 public int countFeatureTargets(EObject seedSource, EStructuralFeature feature) {
1498 return featureData(feature).getDistinctValuesOfHolder(seedSource).size();
1499 }
1500
1501 @Override
1502 public int countFeatures(EStructuralFeature feature) {
1503 return statsStore.countFeatures(toKey(feature));
1504 }
1505
1506 protected IndexingLevel getIndexingLevel(Object type) {
1507 if (type instanceof EClass) {
1508 return getIndexingLevel((EClass)type);
1509 } else if (type instanceof EDataType) {
1510 return getIndexingLevel((EDataType)type);
1511 } else if (type instanceof EStructuralFeature) {
1512 return getIndexingLevel((EStructuralFeature)type);
1513 } else {
1514 throw new IllegalArgumentException("Unexpected type descriptor " + type.toString());
1515 }
1516 }
1517
1518 @Override
1519 public IndexingLevel getIndexingLevel(EClass type) {
1520 Object key = toKey(type);
1521 IndexingLevel level = directlyObservedClasses.get(key);
1522 if (level == null) {
1523 level = delayedClasses.get(key);
1524 }
1525 // Wildcard mode is never null
1526 return wildcardMode.merge(level);
1527 }
1528
1529 @Override
1530 public IndexingLevel getIndexingLevel(EDataType type) {
1531 Object key = toKey(type);
1532 IndexingLevel level = observedDataTypes.get(key);
1533 if (level == null) {
1534 level = delayedDataTypes.get(key);
1535 }
1536 // Wildcard mode is never null
1537 return wildcardMode.merge(level);
1538 }
1539
1540 @Override
1541 public IndexingLevel getIndexingLevel(EStructuralFeature feature) {
1542 Object key = toKey(feature);
1543 IndexingLevel level = observedFeatures.get(key);
1544 if (level == null) {
1545 level = delayedFeatures.get(key);
1546 }
1547 // Wildcard mode is never null
1548 return wildcardMode.merge(level);
1549 }
1550
1551 @Override
1552 public void executeAfterTraversal(final Runnable traversalCallback) throws InvocationTargetException {
1553 coalesceTraversals(() -> traversalCallbacks.add(traversalCallback));
1554 }
1555
1556 /**
1557 * Records a non-exception incident such as faulty notifications.
1558 * Depending on the strictness setting {@link BaseIndexOptions#isStrictNotificationMode()} and log levels,
1559 * this may be treated as a fatal error, merely logged, or just ignored.
1560 *
1561 * @param msgProvider message supplier that only invoked if the message actually gets logged.
1562 *
1563 * @since 2.3
1564 */
1565 protected void logIncident(Supplier<String> msgProvider) {
1566 if (baseIndexOptions.isStrictNotificationMode()) {
1567 // This will cause e.g. query engine to become tainted
1568 String msg = msgProvider.get();
1569 notifyFatalListener(msg, new IllegalStateException(msg));
1570 } else {
1571 if (notificationErrorReported) {
1572 if (logger.isDebugEnabled()) {
1573 String msg = msgProvider.get();
1574 logger.debug(msg);
1575 }
1576 } else {
1577 notificationErrorReported = true;
1578 String msg = msgProvider.get();
1579 logger.error(msg);
1580 }
1581 }
1582 }
1583 boolean notificationErrorReported = false;
1584
1585
1586// DESIGNATED CUSTOMIZATION POINTS FOR SUBCLASSES
1587
1588 /**
1589 * Point of customization, called by constructor
1590 * @since 2.3
1591 */
1592 protected NavigationHelperContentAdapter initContentAdapter() {
1593 switch (baseIndexOptions.getIndexerProfilerMode()) {
1594 case START_DISABLED:
1595 return new ProfilingNavigationHelperContentAdapter(this, false);
1596 case START_ENABLED:
1597 return new ProfilingNavigationHelperContentAdapter(this, true);
1598 case OFF:
1599 default:
1600 return new NavigationHelperContentAdapter(this);
1601 }
1602 }
1603
1604 /**
1605 * Point of customization, called by constructor
1606 * @since 2.3
1607 */
1608 protected EMFBaseIndexStatisticsStore initStatStore() {
1609 return new EMFBaseIndexStatisticsStore(this, logger);
1610 }
1611
1612 /**
1613 * Point of customization, called by constructor
1614 * @since 2.3
1615 */
1616 protected EMFBaseIndexInstanceStore initInstanceStore() {
1617 return new EMFBaseIndexInstanceStore(this, logger);
1618 }
1619
1620 /**
1621 * Point of customization, called by constructor
1622 * @since 2.3
1623 */
1624 protected EMFBaseIndexMetaStore initMetaStore() {
1625 return new EMFBaseIndexMetaStore(this);
1626 }
1627
1628 /**
1629 * Point of customization, called by constructor
1630 * @since 2.3
1631 */
1632 protected EMFModelComprehension initModelComprehension() {
1633 return new EMFModelComprehension(baseIndexOptions);
1634 }
1635
1636 /**
1637 * Point of customization, called at runtime
1638 * @since 2.3
1639 */
1640 protected TraversingVisitor initTraversingVisitor(final Map<Object, IndexingLevel> toGatherClasses,
1641 final Map<Object, IndexingLevel> toGatherFeatures, final Map<Object, IndexingLevel> toGatherDataTypes,
1642 final Map<Object, IndexingLevel> oldClasses) {
1643 return new NavigationHelperVisitor.TraversingVisitor(this,
1644 toGatherFeatures, toGatherClasses, oldClasses, toGatherDataTypes);
1645 }
1646
1647
1648
1649 /**
1650 * Point of customization, e.g. override to suppress
1651 * @since 2.3
1652 */
1653 protected void logIncidentAdapterRemoval(final Notifier notifier) {
1654 logIncident(() -> String.format("Erroneous removal of unattached notification adapter from notifier %s", notifier));
1655 }
1656
1657 /**
1658 * Point of customization, e.g. override to suppress
1659 * @since 2.3
1660 */
1661 protected void logIncidentFeatureTupleInsertion(final Object value, final EObject holder, Object featureKey) {
1662 logIncident(() -> String.format(
1663 "Error: trying to add duplicate value %s to the unique feature %s of host object %s. This indicates some errors in underlying model representation.",
1664 value, featureKey, holder));
1665 }
1666
1667 /**
1668 * Point of customization, e.g. override to suppress
1669 * @since 2.3
1670 */
1671 protected void logIncidentFeatureTupleRemoval(final Object value, final EObject holder, Object featureKey) {
1672 logIncident(() -> String.format(
1673 "Error: trying to remove duplicate value %s from the unique feature %s of host object %s. This indicates some errors in underlying model representation.",
1674 value, featureKey, holder));
1675 }
1676
1677 /**
1678 * Point of customization, e.g. override to suppress
1679 * @since 2.3
1680 */
1681 protected void logIncidentInstanceInsertion(final Object keyClass, final EObject value) {
1682 logIncident(() -> String.format("Notification received to index %s as a %s, but it already exists in the index. This indicates some errors in underlying model representation.", value, keyClass));
1683 }
1684
1685 /**
1686 * Point of customization, e.g. override to suppress
1687 * @since 2.3
1688 */
1689 protected void logIncidentInstanceRemoval(final Object keyClass, final EObject value) {
1690 logIncident(() -> String.format("Notification received to remove %s as a %s, but it is missing from the index. This indicates some errors in underlying model representation.", value, keyClass));
1691 }
1692
1693 /**
1694 * Point of customization, e.g. override to suppress
1695 * @since 2.3
1696 */
1697 protected void logIncidentStatRemoval(Object key) {
1698 logIncident(() -> String.format("No instances of %s is registered before calling removeInstance method.", key));
1699 }
1700
1701
1702}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperSetting.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperSetting.java
new file mode 100644
index 00000000..56556ea8
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperSetting.java
@@ -0,0 +1,73 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.core;
11
12import org.eclipse.emf.ecore.EObject;
13import org.eclipse.emf.ecore.EStructuralFeature;
14import org.eclipse.emf.ecore.EStructuralFeature.Setting;
15
16/**
17 * EStructuralFeature.Setting implementation for the NavigationHelper.
18 *
19 * @author Tamás Szabó
20 *
21 */
22public class NavigationHelperSetting implements Setting {
23
24 private EStructuralFeature feature;
25 private EObject holder;
26 private Object value;
27
28 public NavigationHelperSetting() {
29 super();
30 }
31
32 public NavigationHelperSetting(EStructuralFeature feature, EObject holder, Object value) {
33 super();
34 this.feature = feature;
35 this.holder = holder;
36 this.value = value;
37 }
38
39 @Override
40 public EObject getEObject() {
41 return holder;
42 }
43
44 @Override
45 public EStructuralFeature getEStructuralFeature() {
46 return feature;
47 }
48
49 @Override
50 public Object get(boolean resolve) {
51 return value;
52 }
53
54 @Override
55 public void set(Object newValue) {
56 this.value = newValue;
57 }
58
59 @Override
60 public boolean isSet() {
61 return (value != null);
62 }
63
64 @Override
65 public void unset() {
66 this.value = null;
67 }
68
69 @Override
70 public String toString() {
71 return "feature = " + feature + " holder = " + holder + " value = " + value;
72 }
73}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperType.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperType.java
new file mode 100644
index 00000000..2bab4914
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperType.java
@@ -0,0 +1,14 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.core;
11
12public enum NavigationHelperType {
13 REGISTER, ALL
14}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperVisitor.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperVisitor.java
new file mode 100644
index 00000000..b5de8d20
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/NavigationHelperVisitor.java
@@ -0,0 +1,441 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.base.core;
10
11import java.util.Collections;
12import java.util.HashMap;
13import java.util.Map;
14import java.util.Set;
15
16import org.eclipse.emf.ecore.EAttribute;
17import org.eclipse.emf.ecore.EClass;
18import org.eclipse.emf.ecore.EClassifier;
19import org.eclipse.emf.ecore.EObject;
20import org.eclipse.emf.ecore.EReference;
21import org.eclipse.emf.ecore.EStructuralFeature;
22import org.eclipse.emf.ecore.resource.Resource;
23import org.eclipse.emf.ecore.util.EcoreUtil;
24import tools.refinery.viatra.runtime.base.api.IndexingLevel;
25import tools.refinery.viatra.runtime.base.comprehension.EMFModelComprehension;
26import tools.refinery.viatra.runtime.base.comprehension.EMFVisitor;
27
28public abstract class NavigationHelperVisitor extends EMFVisitor {
29
30 /**
31 * A visitor for processing a single change event. Does not traverse the model. Uses all the observed types.
32 */
33 public static class ChangeVisitor extends NavigationHelperVisitor {
34 // local copies to save actual state, in case visitor has to be saved for later due unresolvable proxies
35 private final IndexingLevel wildcardMode;
36 private final Map<Object, IndexingLevel> allObservedClasses;
37 private final Map<Object, IndexingLevel> observedDataTypes;
38 private final Map<Object, IndexingLevel> observedFeatures;
39 private final Map<Object, Boolean> sampledClasses;
40
41 public ChangeVisitor(NavigationHelperImpl navigationHelper, boolean isInsertion) {
42 super(navigationHelper, isInsertion, false);
43 wildcardMode = navigationHelper.getWildcardLevel();
44 allObservedClasses = navigationHelper.getAllObservedClassesInternal(); // new HashSet<EClass>();
45 observedDataTypes = navigationHelper.getObservedDataTypesInternal(); // new HashSet<EDataType>();
46 observedFeatures = navigationHelper.getObservedFeaturesInternal(); // new HashSet<EStructuralFeature>();
47 sampledClasses = new HashMap<Object, Boolean>();
48 }
49
50 @Override
51 protected boolean observesClass(Object eClass) {
52 return wildcardMode.hasInstances() || (IndexingLevel.FULL == allObservedClasses.get(eClass)) || registerSampledClass(eClass);
53 }
54
55 protected boolean registerSampledClass(Object eClass) {
56 Boolean classAlreadyChecked = sampledClasses.get(eClass);
57 if (classAlreadyChecked != null) {
58 return classAlreadyChecked;
59 }
60 boolean isSampledClass = isSampledClass(eClass);
61 sampledClasses.put(eClass, isSampledClass);
62 // do not modify observation configuration during traversal
63 return false;
64 }
65
66 @Override
67 protected boolean observesDataType(Object type) {
68 return wildcardMode.hasInstances() || (IndexingLevel.FULL == observedDataTypes.get(type));
69 }
70
71 @Override
72 protected boolean observesFeature(Object feature) {
73 return wildcardMode.hasInstances() || (IndexingLevel.FULL == observedFeatures.get(feature));
74 }
75
76 @Override
77 protected boolean countsFeature(Object feature) {
78 return wildcardMode.hasStatistics() || observedFeatures.containsKey(feature) && observedFeatures.get(feature).hasStatistics();
79 }
80
81 @Override
82 protected boolean countsDataType(Object type) {
83 return wildcardMode.hasStatistics() || observedDataTypes.containsKey(type) && observedDataTypes.get(type).hasStatistics();
84 }
85
86 @Override
87 protected boolean countsClass(Object eClass) {
88 return wildcardMode.hasStatistics() || allObservedClasses.containsKey(eClass) && allObservedClasses.get(eClass).hasStatistics();
89 }
90 }
91
92 /**
93 * A visitor for a single-pass traversal of the whole model, processing only the given types and inserting them.
94 */
95 public static class TraversingVisitor extends NavigationHelperVisitor {
96 private final IndexingLevel wildcardMode;
97 Map<Object, IndexingLevel> features;
98 Map<Object, IndexingLevel> newClasses;
99 Map<Object, IndexingLevel> oldClasses; // if decends from an old class, no need to add!
100 Map<Object, IndexingLevel> classObservationMap; // true for a class even if only a supertype is included in classes;
101 Map<Object, IndexingLevel> dataTypes;
102
103 public TraversingVisitor(NavigationHelperImpl navigationHelper, Map<Object, IndexingLevel> features, Map<Object, IndexingLevel> newClasses,
104 Map<Object, IndexingLevel> oldClasses, Map<Object, IndexingLevel> dataTypes) {
105 super(navigationHelper, true, true);
106 wildcardMode = navigationHelper.getWildcardLevel();
107 this.features = features;
108 this.newClasses = newClasses;
109 this.oldClasses = oldClasses;
110 this.classObservationMap = new HashMap<Object, IndexingLevel>();
111 this.dataTypes = dataTypes;
112 }
113
114 protected IndexingLevel getExistingIndexingLevel(Object eClass){
115 IndexingLevel result = IndexingLevel.NONE;
116 result = result.merge(oldClasses.get(eClass));
117 result = result.merge(oldClasses.get(metaStore.getEObjectClassKey()));
118 if (IndexingLevel.FULL == result) return result;
119 Set<Object> superTypes = metaStore.getSuperTypeMap().get(eClass);
120 if (superTypes != null){
121 for(Object superType: superTypes){
122 result = result.merge(oldClasses.get(superType));
123 if (IndexingLevel.FULL == result) return result;
124 }
125 }
126 return result;
127 }
128
129 protected IndexingLevel getRequestedIndexingLevel(Object eClass){
130 IndexingLevel result = IndexingLevel.NONE;
131 result = result.merge(newClasses.get(eClass));
132 result = result.merge(newClasses.get(metaStore.getEObjectClassKey()));
133 if (IndexingLevel.FULL == result) return result;
134 Set<Object> superTypes = metaStore.getSuperTypeMap().get(eClass);
135 if (superTypes != null){
136 for(Object superType: superTypes){
137 result = result.merge(newClasses.get(superType));
138 if (IndexingLevel.FULL == result) return result;
139 }
140 }
141 return result;
142 }
143
144 protected IndexingLevel getTraversalIndexing(Object eClass){
145 IndexingLevel level = classObservationMap.get(eClass);
146 if (level == null){
147 IndexingLevel existing = getExistingIndexingLevel(eClass);
148 IndexingLevel requested = getRequestedIndexingLevel(eClass);
149
150 // Calculate the type of indexing which needs to be executed to reach requested indexing state
151 // Considering indexes which are already available
152 if (existing == requested || existing == IndexingLevel.FULL) return IndexingLevel.NONE;
153 if (requested == IndexingLevel.FULL) return IndexingLevel.FULL;
154 if (requested.hasStatistics() == existing.hasStatistics()) return IndexingLevel.NONE;
155 if (requested.hasStatistics()) return IndexingLevel.STATISTICS;
156 return IndexingLevel.NONE;
157 }
158 return level;
159 }
160
161 @Override
162 protected boolean observesClass(Object eClass) {
163 if (wildcardMode.hasInstances()) {
164 return true;
165 }
166 return IndexingLevel.FULL == getTraversalIndexing(eClass);
167 }
168
169 @Override
170 protected boolean countsClass(Object eClass) {
171 return wildcardMode.hasStatistics() || getTraversalIndexing(eClass).hasStatistics();
172 }
173
174 @Override
175 protected boolean observesDataType(Object type) {
176 return wildcardMode.hasInstances() || (IndexingLevel.FULL == dataTypes.get(type));
177 }
178
179 @Override
180 protected boolean observesFeature(Object feature) {
181 return wildcardMode.hasInstances() || (IndexingLevel.FULL == features.get(feature));
182 }
183
184 @Override
185 protected boolean countsDataType(Object type) {
186 return wildcardMode.hasStatistics() || dataTypes.containsKey(type) && dataTypes.get(type).hasStatistics();
187 }
188
189 @Override
190 protected boolean countsFeature(Object feature) {
191 return wildcardMode.hasStatistics() || features.containsKey(feature) && features.get(feature).hasStatistics();
192 }
193
194 @Override
195 public boolean avoidTransientContainmentLink(EObject source, EReference reference, EObject targetObject) {
196 return !targetObject.eAdapters().contains(navigationHelper.contentAdapter);
197 }
198 }
199
200 protected NavigationHelperImpl navigationHelper;
201 boolean isInsertion;
202 boolean descendHierarchy;
203 boolean traverseOnlyWellBehavingDerivedFeatures;
204 EMFBaseIndexInstanceStore instanceStore;
205 EMFBaseIndexStatisticsStore statsStore;
206 EMFBaseIndexMetaStore metaStore;
207
208 NavigationHelperVisitor(NavigationHelperImpl navigationHelper, boolean isInsertion, boolean descendHierarchy) {
209 super(isInsertion /* preOrder iff insertion */);
210 this.navigationHelper = navigationHelper;
211 instanceStore = navigationHelper.instanceStore;
212 metaStore = navigationHelper.metaStore;
213 statsStore = navigationHelper.statsStore;
214 this.isInsertion = isInsertion;
215 this.descendHierarchy = descendHierarchy;
216 this.traverseOnlyWellBehavingDerivedFeatures = navigationHelper.getBaseIndexOptions()
217 .isTraverseOnlyWellBehavingDerivedFeatures();
218 }
219
220 @Override
221 public boolean pruneSubtrees(EObject source) {
222 return !descendHierarchy;
223 }
224
225 @Override
226 public boolean pruneSubtrees(Resource source) {
227 return !descendHierarchy;
228 }
229
230 @Override
231 public boolean pruneFeature(EStructuralFeature feature) {
232 Object featureKey = toKey(feature);
233 if (observesFeature(featureKey) || countsFeature(featureKey)) {
234 return false;
235 }
236 if (feature instanceof EAttribute){
237 Object dataTypeKey = toKey(((EAttribute) feature).getEAttributeType());
238 if (observesDataType(dataTypeKey) || countsDataType(dataTypeKey)) {
239 return false;
240 }
241 }
242 return !(isInsertion && navigationHelper.isExpansionAllowed() && feature instanceof EReference
243 && !((EReference) feature).isContainment());
244 }
245
246 /**
247 * @param feature
248 * key of feature (EStructuralFeature or String id)
249 */
250 protected abstract boolean observesFeature(Object feature);
251
252 /**
253 * @param feature
254 * key of data type (EDatatype or String id)
255 */
256 protected abstract boolean observesDataType(Object type);
257
258 /**
259 * @param feature
260 * key of class (EClass or String id)
261 */
262 protected abstract boolean observesClass(Object eClass);
263
264 protected abstract boolean countsFeature(Object feature);
265
266 protected abstract boolean countsDataType(Object type);
267
268 protected abstract boolean countsClass(Object eClass);
269
270 @Override
271 public void visitElement(EObject source) {
272 EClass eClass = source.eClass();
273 if (eClass.eIsProxy()) {
274 eClass = (EClass) EcoreUtil.resolve(eClass, source);
275 }
276
277 final Object classKey = toKey(eClass);
278 if (observesClass(classKey)) {
279 if (isInsertion) {
280 instanceStore.insertIntoInstanceSet(classKey, source);
281 } else {
282 instanceStore.removeFromInstanceSet(classKey, source);
283 }
284 }
285 if (countsClass(classKey)){
286 if (isInsertion){
287 statsStore.addInstance(classKey);
288 } else {
289 statsStore.removeInstance(classKey);
290 }
291 }
292 }
293
294 @Override
295 public void visitAttribute(EObject source, EAttribute feature, Object target) {
296 Object featureKey = toKey(feature);
297 final Object eAttributeType = toKey(feature.getEAttributeType());
298 Object internalValueRepresentation = null;
299 if (observesFeature(featureKey)) {
300 // if (internalValueRepresentation == null) // always true
301 internalValueRepresentation = metaStore.toInternalValueRepresentation(target);
302 boolean unique = feature.isUnique();
303 if (isInsertion) {
304 instanceStore.insertFeatureTuple(featureKey, unique, internalValueRepresentation, source);
305 } else {
306 instanceStore.removeFeatureTuple(featureKey, unique, internalValueRepresentation, source);
307 }
308 }
309 if (countsFeature(featureKey)){
310 if (isInsertion) {
311 statsStore.addFeature(source, featureKey);
312 }else{
313 statsStore.removeFeature(source, featureKey);
314 }
315 }
316 if (observesDataType(eAttributeType)) {
317 if (internalValueRepresentation == null)
318 internalValueRepresentation = metaStore.toInternalValueRepresentation(target);
319 if (isInsertion) {
320 instanceStore.insertIntoDataTypeMap(eAttributeType, internalValueRepresentation);
321 } else {
322 instanceStore.removeFromDataTypeMap(eAttributeType, internalValueRepresentation);
323 }
324 }
325 if (countsDataType(eAttributeType)){
326 if (isInsertion){
327 statsStore.addInstance(eAttributeType);
328 } else {
329 statsStore.removeInstance(eAttributeType);
330 }
331 }
332 }
333
334 @Override
335 public void visitInternalContainment(EObject source, EReference feature, EObject target) {
336 visitReference(source, feature, target);
337 }
338
339 @Override
340 public void visitNonContainmentReference(EObject source, EReference feature, EObject target) {
341 visitReference(source, feature, target);
342 if (isInsertion) {
343 navigationHelper.considerForExpansion(target);
344 }
345 }
346
347 protected void visitReference(EObject source, EReference feature, EObject target) {
348 Object featureKey = toKey(feature);
349 if (observesFeature(featureKey)) {
350 boolean unique = feature.isUnique();
351 if (isInsertion) {
352 instanceStore.insertFeatureTuple(featureKey, unique, target, source);
353 } else {
354 instanceStore.removeFeatureTuple(featureKey, unique, target, source);
355 }
356 }
357 if (countsFeature(featureKey)){
358 if (isInsertion){
359 statsStore.addFeature(source, featureKey);
360 } else {
361 statsStore.removeFeature(source, featureKey);
362 }
363 }
364 }
365
366 @Override
367 // do not attempt to resolve proxies referenced from resources that are still being loaded
368 public boolean attemptProxyResolutions(EObject source, EReference feature) {
369 // emptyness is checked first to avoid costly resource lookup in most cases
370 if (navigationHelper.resolutionDelayingResources.isEmpty())
371 return true;
372 else
373 return ! navigationHelper.resolutionDelayingResources.contains(source.eResource());
374 }
375
376 @Override
377 public void visitProxyReference(EObject source, EReference reference, EObject targetObject, Integer position) {
378 if (isInsertion) { // only attempt to resolve proxies if they are inserted
379 // final Object result = source.eGet(reference, true);
380 // if (reference.isMany()) {
381 // // no idea which element to get, have to iterate through
382 // for (EObject touch : (Iterable<EObject>) result);
383 // }
384 if (navigationHelper.isFeatureResolveIgnored(reference))
385 return; // skip resolution; would be ignored anyways
386 if (position != null && reference.isMany() && attemptProxyResolutions(source, reference)) {
387 // there is added value in doing the resolution now, when we know the position
388 // this may save an iteration through the EList if successful
389 @SuppressWarnings("unchecked")
390 EObject touch = ((java.util.List<EObject>) source.eGet(reference, true)).get(position);
391 // if resolution successful, no further action needed
392 if (!touch.eIsProxy())
393 return;
394 }
395 // otherwise, attempt resolution later, at the end of the coalesced traversal block
396 navigationHelper.delayedProxyResolutions.addPairOrNop(source, reference);
397 }
398 }
399
400 protected Object toKey(EStructuralFeature feature) {
401 return metaStore.toKey(feature);
402 }
403
404 protected Object toKey(EClassifier eClassifier) {
405 return metaStore.toKey(eClassifier);
406 }
407
408 /**
409 * Decides whether the type must be observed in order to allow re-sampling of any of its features. If not
410 * well-behaving features are traversed and there is such a feature for this class, the class will be registered
411 * into the navigation helper, which may cause a re-traversal.
412 *
413 */
414 protected boolean isSampledClass(Object eClass) {
415 if (!traverseOnlyWellBehavingDerivedFeatures) {
416 // TODO we could save this reverse lookup if the calling method would have the EClass, not just the key
417 EClass knownClass = (EClass) metaStore.getKnownClassifierForKey(eClass);
418 // check features that are traversed, and whether there is any that must be sampled
419 for (EStructuralFeature feature : knownClass.getEAllStructuralFeatures()) {
420 EMFModelComprehension comprehension = navigationHelper.getComprehension();
421 if (comprehension.untraversableDirectly(feature))
422 continue;
423 final boolean visitorPrunes = pruneFeature(feature);
424 if (visitorPrunes)
425 continue;
426 // we found a feature to be visited
427 if (comprehension.onlySamplingFeature(feature)) {
428 // we found a feature that must be sampled
429 navigationHelper.registerEClasses(Collections.singleton(feature.getEContainingClass()), IndexingLevel.FULL);
430 return true;
431 }
432 }
433 }
434 return false;
435 }
436
437 @Override
438 public boolean descendAlongCrossResourceContainments() {
439 return this.navigationHelper.traversalDescendsAlongCrossResourceContainment();
440 }
441}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/TransitiveClosureHelperImpl.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/TransitiveClosureHelperImpl.java
new file mode 100644
index 00000000..552696cb
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/TransitiveClosureHelperImpl.java
@@ -0,0 +1,153 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.core;
11
12import java.util.ArrayList;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Set;
16
17import org.eclipse.emf.ecore.EClass;
18import org.eclipse.emf.ecore.EObject;
19import org.eclipse.emf.ecore.EReference;
20import org.eclipse.emf.ecore.EStructuralFeature;
21import org.eclipse.emf.ecore.util.EContentAdapter;
22import tools.refinery.viatra.runtime.base.api.FeatureListener;
23import tools.refinery.viatra.runtime.base.api.IndexingLevel;
24import tools.refinery.viatra.runtime.base.api.InstanceListener;
25import tools.refinery.viatra.runtime.base.api.NavigationHelper;
26import tools.refinery.viatra.runtime.base.api.TransitiveClosureHelper;
27import tools.refinery.viatra.runtime.base.itc.alg.incscc.IncSCCAlg;
28import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder;
29import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver;
30
31/**
32 * Implementation class for the {@link TransitiveClosureHelper}.
33 * It uses a {@link NavigationHelper} instance to wrap an EMF model
34 * and make it suitable for the {@link IncSCCAlg} algorithm.
35 *
36 * @author Tamas Szabo
37 *
38 */
39public class TransitiveClosureHelperImpl extends EContentAdapter implements TransitiveClosureHelper,
40 ITcObserver<EObject>, FeatureListener, InstanceListener {
41
42 private IncSCCAlg<EObject> sccAlg;
43 private Set<EStructuralFeature> features;
44 private Set<EClass> classes;
45 private EMFDataSource dataSource;
46 private List<ITcObserver<EObject>> tcObservers;
47 private NavigationHelper navigationHelper;
48 private boolean disposeBaseIndexWhenDisposed;
49
50 public TransitiveClosureHelperImpl(final NavigationHelper navigationHelper, boolean disposeBaseIndexWhenDisposed, Set<EReference> references) {
51 this.tcObservers = new ArrayList<ITcObserver<EObject>>();
52 this.navigationHelper = navigationHelper;
53 this.disposeBaseIndexWhenDisposed = disposeBaseIndexWhenDisposed;
54
55 //NavigationHelper only accepts Set<EStructuralFeature> upon registration
56 this.features = new HashSet<EStructuralFeature>(references);
57 this.classes = collectEClasses();
58 /*this.classes = Collections.emptySet();*/
59 if (!navigationHelper.isInWildcardMode())
60 navigationHelper.registerObservedTypes(classes, null, features, IndexingLevel.FULL);
61
62 this.navigationHelper.addFeatureListener(features, this);
63 this.navigationHelper.addInstanceListener(classes, this);
64
65 this.dataSource = new EMFDataSource(navigationHelper, references, classes);
66
67 this.sccAlg = new IncSCCAlg<EObject>(dataSource);
68 this.sccAlg.attachObserver(this);
69 }
70
71 private Set<EClass> collectEClasses() {
72 Set<EClass> classes = new HashSet<EClass>();
73 for (EStructuralFeature ref : features) {
74 classes.add(ref.getEContainingClass());
75 classes.add(((EReference) ref).getEReferenceType());
76 }
77 return classes;
78 }
79
80 @Override
81 public void attachObserver(ITcObserver<EObject> to) {
82 this.tcObservers.add(to);
83 }
84
85 @Override
86 public void detachObserver(ITcObserver<EObject> to) {
87 this.tcObservers.remove(to);
88 }
89
90 @Override
91 public Set<EObject> getAllReachableTargets(EObject source) {
92 return this.sccAlg.getAllReachableTargets(source);
93 }
94
95 @Override
96 public Set<EObject> getAllReachableSources(EObject target) {
97 return this.sccAlg.getAllReachableSources(target);
98 }
99
100 @Override
101 public boolean isReachable(EObject source, EObject target) {
102 return this.sccAlg.isReachable(source, target);
103 }
104
105 @Override
106 public void tupleInserted(EObject source, EObject target) {
107 for (ITcObserver<EObject> to : tcObservers) {
108 to.tupleInserted(source, target);
109 }
110 }
111
112 @Override
113 public void tupleDeleted(EObject source, EObject target) {
114 for (ITcObserver<EObject> to : tcObservers) {
115 to.tupleDeleted(source, target);
116 }
117 }
118
119 @Override
120 public void dispose() {
121 this.sccAlg.dispose();
122 this.navigationHelper.removeInstanceListener(classes, this);
123 this.navigationHelper.removeFeatureListener(features, this);
124
125 if (disposeBaseIndexWhenDisposed)
126 this.navigationHelper.dispose();
127 }
128
129 @Override
130 public void featureInserted(EObject host, EStructuralFeature feature, Object value) {
131 this.dataSource.notifyEdgeInserted(host, (EObject) value);
132 }
133
134 @Override
135 public void featureDeleted(EObject host, EStructuralFeature feature, Object value) {
136 this.dataSource.notifyEdgeDeleted(host, (EObject) value);
137 }
138
139 @Override
140 public void instanceInserted(EClass clazz, EObject instance) {
141 this.dataSource.notifyNodeInserted(instance);
142 }
143
144 @Override
145 public void instanceDeleted(EClass clazz, EObject instance) {
146 this.dataSource.notifyNodeDeleted(instance);
147 }
148
149 @Override
150 public IGraphPathFinder<EObject> getPathFinder() {
151 return this.sccAlg.getPathFinder();
152 }
153}
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/profiler/ProfilingNavigationHelperContentAdapter.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/profiler/ProfilingNavigationHelperContentAdapter.java
new file mode 100644
index 00000000..3ab15430
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/core/profiler/ProfilingNavigationHelperContentAdapter.java
@@ -0,0 +1,155 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Laszlo Gati, 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.base.core.profiler;
10
11import org.eclipse.emf.common.notify.Notification;
12import org.eclipse.emf.common.notify.Notifier;
13import tools.refinery.viatra.runtime.base.core.NavigationHelperContentAdapter;
14import tools.refinery.viatra.runtime.base.core.NavigationHelperImpl;
15
16/**
17 *
18 * @noinstantiate This class is not intended to be instantiated by clients.
19 * @noreference This class is not intended to be referenced by clients.
20 */
21public final class ProfilingNavigationHelperContentAdapter extends NavigationHelperContentAdapter {
22
23 private static class StopWatch {
24
25 private long currentStartTimeNs = 0l;
26 private long totalElapsedTimeNs = 0l;
27 private boolean running = false;
28
29 /**
30 * Puts the timer in running state and saves the current time.
31 */
32 private void start() {
33 currentStartTimeNs = System.nanoTime();
34 running = true;
35
36 }
37
38 /**
39 * Puts the the timer in stopped state and saves the total time spent in started
40 * state between the last reset and now
41 */
42 private void stop() {
43 totalElapsedTimeNs = getTotalElapsedTimeNs();
44 running = false;
45 }
46
47 /**
48 * @return time between the last start and now
49 */
50 private long getCurrentElapsedTimeNs() {
51 return System.nanoTime() - currentStartTimeNs;
52 }
53
54 /**
55 * @return the total time spent in started state between the last reset and now
56 */
57 private long getTotalElapsedTimeNs() {
58 return running ? getCurrentElapsedTimeNs() + totalElapsedTimeNs : totalElapsedTimeNs;
59 }
60
61 /**
62 * Saves the current time and resets all the time spent between the last reset and now.
63 */
64 private void resetTime() {
65 currentStartTimeNs = System.currentTimeMillis();
66 totalElapsedTimeNs = 0;
67 }
68 }
69
70 long notificationCount = 0l;
71 StopWatch watch = new StopWatch();
72 boolean isEnabled = false;
73
74 boolean measurement = false;
75
76 public ProfilingNavigationHelperContentAdapter(NavigationHelperImpl navigationHelper, boolean enabled) {
77 super(navigationHelper);
78 this.isEnabled = enabled;
79 }
80
81 @Override
82 public void notifyChanged(Notification notification) {
83 // Handle possibility of reentrancy
84 if (isEnabled && !measurement) {
85 try {
86 measurement = true;
87 notificationCount++;
88 watch.start();
89 super.notifyChanged(notification);
90 } finally {
91 watch.stop();
92 measurement = false;
93 }
94 } else {
95 super.notifyChanged(notification);
96 }
97 }
98
99 @Override
100 public void setTarget(Notifier target) {
101 // Handle possibility of reentrancy
102 if (isEnabled && !measurement) {
103 try {
104 measurement = true;
105 notificationCount++;
106 watch.start();
107 super.setTarget(target);
108 } finally {
109 watch.stop();
110 measurement = false;
111 }
112 } else {
113 super.setTarget(target);
114 }
115 }
116
117 @Override
118 public void unsetTarget(Notifier target) {
119 // Handle possibility of reentrancy
120 if (isEnabled && !measurement) {
121 try {
122 measurement = true;
123 notificationCount++;
124 watch.start();
125 super.unsetTarget(target);
126 } finally {
127 watch.stop();
128 measurement = false;
129 }
130 } else {
131 super.unsetTarget(target);
132 }
133 }
134
135 public long getNotificationCount() {
136 return notificationCount;
137 }
138
139 public long getTotalMeasuredTimeInMS() {
140 return watch.getTotalElapsedTimeNs() / 1_000_000l;
141 }
142
143 public boolean isEnabled() {
144 return isEnabled;
145 }
146
147 public void setEnabled(boolean isEnabled) {
148 this.isEnabled = isEnabled;
149 }
150
151 public void resetMeasurement() {
152 notificationCount = 0;
153 watch.resetTime();
154 }
155} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/exception/ViatraBaseException.java b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/exception/ViatraBaseException.java
new file mode 100644
index 00000000..fe656c34
--- /dev/null
+++ b/subprojects/viatra-runtime-base/src/main/java/tools/refinery/viatra/runtime/base/exception/ViatraBaseException.java
@@ -0,0 +1,25 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.base.exception;
11
12import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
13
14public class ViatraBaseException extends ViatraQueryRuntimeException {
15
16 private static final long serialVersionUID = -5145445047912938251L;
17
18 public static final String EMPTY_REF_LIST = "At least one EReference must be provided!";
19 public static final String INVALID_EMFROOT = "Emf navigation helper can only be attached on the contents of an EMF EObject, Resource, or ResourceSet.";
20
21 public ViatraBaseException(String s) {
22 super(s);
23 }
24
25}
diff --git a/subprojects/viatra-runtime-localsearch/about.html b/subprojects/viatra-runtime-localsearch/about.html
new file mode 100644
index 00000000..d1d5593a
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/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-localsearch/build.gradle.kts b/subprojects/viatra-runtime-localsearch/build.gradle.kts
new file mode 100644
index 00000000..2d3886a5
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/build.gradle.kts
@@ -0,0 +1,15 @@
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 implementation(project(":refinery-viatra-runtime"))
13 implementation(libs.ecore)
14 implementation(libs.slf4j.log4j)
15}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/ExecutionLoggerAdapter.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/ExecutionLoggerAdapter.java
new file mode 100644
index 00000000..0f7c7b01
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/ExecutionLoggerAdapter.java
@@ -0,0 +1,85 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.localsearch;
10
11import java.util.Optional;
12import java.util.function.Consumer;
13
14import tools.refinery.viatra.runtime.localsearch.matcher.ILocalSearchAdapter;
15import tools.refinery.viatra.runtime.localsearch.matcher.LocalSearchMatcher;
16import tools.refinery.viatra.runtime.localsearch.operations.IPatternMatcherOperation;
17import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
18import tools.refinery.viatra.runtime.localsearch.operations.check.nobase.ScopeCheck;
19import tools.refinery.viatra.runtime.localsearch.plan.SearchPlan;
20
21/**
22 * @since 2.0
23 */
24public final class ExecutionLoggerAdapter implements ILocalSearchAdapter {
25
26 volatile String indentation = "";
27 private final Consumer<String> outputConsumer;
28
29 public ExecutionLoggerAdapter(Consumer<String> outputConsumer) {
30 this.outputConsumer = outputConsumer;
31 }
32
33 private void logMessage(String message) {
34 outputConsumer.accept(message);
35 }
36
37 private void logMessage(String message, Object...args) {
38 outputConsumer.accept(String.format(message, args));
39 }
40
41 @Override
42 public void patternMatchingStarted(LocalSearchMatcher lsMatcher) {
43 logMessage(indentation + "[ START] " + lsMatcher.getQuerySpecification().getFullyQualifiedName());
44 }
45
46 @Override
47 public void noMoreMatchesAvailable(LocalSearchMatcher lsMatcher) {
48 logMessage(indentation + "[FINISH] " + lsMatcher.getQuerySpecification().getFullyQualifiedName());
49 }
50
51 @Override
52 public void planChanged(Optional<SearchPlan> oldPlan, Optional<SearchPlan> newPlan) {
53 logMessage(indentation + "[ PLAN] " + newPlan.map(p -> p.getSourceBody().getPattern().getFullyQualifiedName()).orElse(""));
54 logMessage(indentation + newPlan.map(SearchPlan::toString).map(s -> s.replace("\n", "\n" + indentation)).orElse(""));
55 }
56
57 @Override
58 public void operationSelected(SearchPlan plan, ISearchOperation operation, MatchingFrame frame, boolean isBacktrack) {
59 String category = isBacktrack ? "[ BACK] " : "[SELECT] ";
60 logMessage(indentation + category + operation.toString());
61 if (operation instanceof IPatternMatcherOperation) {
62 indentation = indentation + "\t";
63 }
64 }
65
66 @Override
67 public void operationExecuted(SearchPlan plan, ISearchOperation operation, MatchingFrame frame,
68 boolean isSuccessful) {
69 if (operation instanceof ScopeCheck) return;
70 if (operation instanceof IPatternMatcherOperation && indentation.length() > 0) {
71 indentation = indentation.substring(1);
72 }
73 logMessage(indentation + "[ %s] %s %s", isSuccessful ? "OK" : "NO", operation.toString(), frame.toString());
74 }
75
76 @Override
77 public void matchFound(SearchPlan plan, MatchingFrame frame) {
78 logMessage(indentation + "[ MATCH] " + plan.getSourceBody().getPattern().getFullyQualifiedName() + " " + frame.toString());
79 }
80
81 @Override
82 public void duplicateMatchFound(MatchingFrame frame) {
83 logMessage(indentation + "[ DUPL.] " + frame.toString());
84 }
85} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/MatchingFrame.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/MatchingFrame.java
new file mode 100644
index 00000000..9caf32bb
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/MatchingFrame.java
@@ -0,0 +1,122 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Akos Horvath, Gergely Varro Zoltan Ujhelyi 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.localsearch;
11
12import java.util.Arrays;
13import java.util.stream.Collectors;
14
15import org.eclipse.emf.ecore.EObject;
16import org.eclipse.emf.ecore.EStructuralFeature;
17import tools.refinery.viatra.runtime.matchers.tuple.IModifiableTuple;
18import tools.refinery.viatra.runtime.matchers.tuple.VolatileTuple;
19import tools.refinery.viatra.runtime.matchers.util.Preconditions;
20
21/**
22 * A MatchingFrame is a Volatile Tuple implementation used by the local search engine internally.
23 */
24public class MatchingFrame extends VolatileTuple implements IModifiableTuple {
25
26 /**
27 * The array that physically holds the values.
28 */
29 private Object[] frame;
30
31 /**
32 * @since 1.7
33 */
34 public MatchingFrame(int frameSize) {
35 this.frame = new Object[frameSize];
36 }
37
38 /**
39 * Creates a copy of another matching frame; the two frames can be updated separately
40 * @param other
41 * @since 1.7
42 */
43 public MatchingFrame(MatchingFrame other) {
44 this.frame = Arrays.copyOf(other.frame, other.frame.length);
45 }
46
47
48
49 /**
50 * Returns the value stored inside the matching frame.
51 *
52 * @param position
53 * @return the element stored in the selected position in the frame, or null if it is not yet set
54 * @throws IndexOutOfBoundsException
55 * if position is negative
56 * @throws IllegalArgumentException
57 * if the position is larger then the length of the frame
58 */
59 public Object getValue(int position) {
60 Preconditions.checkElementIndex(position, frame.length);
61 return frame[position];
62 }
63
64 /**
65 * Sets the value of the variable at the given position. For internal use in LS matching only.
66 *
67 * @param position the position of the variable within the frame
68 * @param value the value to be set for the variable
69 */
70 public void setValue(int position, Object value) {
71 Preconditions.checkElementIndex(position, frame.length);
72 frame[position] = value;
73 }
74
75 public boolean testAndSetValue(Integer position, Object value) {
76 Preconditions.checkElementIndex(position, frame.length);
77 if (frame[position] == null) {
78 frame[position] = value;
79 return true;
80 } else {
81 return frame[position].equals(value);
82 }
83 }
84
85 @Override
86 public String toString() {
87 return Arrays.stream(frame).map(this::stringRepresentation).collect(Collectors.joining(", ", "[", "]"));
88 }
89
90 private String stringRepresentation(Object obj) {
91 if (obj == null) {
92 return "_";
93 } else if (obj instanceof EObject) {
94 EObject eObject = (EObject) obj;
95 final EStructuralFeature feature = eObject.eClass().getEStructuralFeature("identifier");
96 if (feature != null) {
97 return String.format("%s : %s", eObject.eGet(feature), eObject.eClass().getName());
98 }
99 }
100 return obj.toString();
101 }
102
103 @Override
104 public int getSize() {
105 return frame.length;
106 }
107
108 @Override
109 public Object get(int index) {
110 return getValue(index);
111 }
112
113 @Override
114 public Object[] getElements() {
115 return Arrays.copyOf(frame, frame.length);
116 }
117
118 @Override
119 public void set(int index, Object value) {
120 frame[index] = value;
121 }
122}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/exceptions/LocalSearchException.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/exceptions/LocalSearchException.java
new file mode 100644
index 00000000..c239e766
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/exceptions/LocalSearchException.java
@@ -0,0 +1,33 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.exceptions;
10
11import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
12
13/**
14 * @author Zoltan Ujhelyi, Akos Horvath
15 *
16 */
17public class LocalSearchException extends ViatraQueryRuntimeException {
18
19 private static final long serialVersionUID = -2585896573351435974L;
20
21 public static final String PLAN_EXECUTION_ERROR = "Error while executing search plan";
22 public static final String TYPE_ERROR = "Invalid type of variable";
23
24 public LocalSearchException(String description, Throwable rootException) {
25 super(description, rootException);
26 }
27
28 public LocalSearchException(String description) {
29 super(description);
30 }
31
32
33}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/CallWithAdornment.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/CallWithAdornment.java
new file mode 100644
index 00000000..0cabeb97
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/CallWithAdornment.java
@@ -0,0 +1,55 @@
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.localsearch.matcher;
10
11import java.util.HashSet;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference;
15import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
16import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
17import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
18
19/**
20 * Immutable data that represents the role of a pattern call within an LS query plan.
21 *
22 * <p> The call is expressed as the {@link PConstraint} {@link #call} (implementing {@link IQueryReference}),
23 * while the stored {@link #adornment} records the way it will be used within a search plan (specifically,
24 * pattern parameters within the adornment will have their values known at the point of evaluating the constraint).
25 *
26 *
27 * @author Gabor Bergmann
28 * @since 2.1
29 */
30public class CallWithAdornment {
31 private final IQueryReference call;
32 private final Set<PParameter> adornment;
33
34 public CallWithAdornment(IQueryReference call, Set<PParameter> adornment) {
35 this.call = call;
36 this.adornment = new HashSet<>(adornment);
37 }
38
39 public IQueryReference getCall() {
40 return call;
41 }
42
43 public Set<PParameter> getAdornment() {
44 return adornment;
45 }
46
47
48 public PQuery getReferredQuery() {
49 return call.getReferredQuery();
50 }
51
52 public MatcherReference getMatcherReference() {
53 return new MatcherReference(getReferredQuery(), adornment);
54 }
55}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/ILocalSearchAdaptable.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/ILocalSearchAdaptable.java
new file mode 100644
index 00000000..f4b28ed0
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/ILocalSearchAdaptable.java
@@ -0,0 +1,29 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Peter Lunk, 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.localsearch.matcher;
10
11import java.util.List;
12
13/**
14 * @author Zoltan Ujhelyi
15 *
16 */
17public interface ILocalSearchAdaptable {
18
19 List<ILocalSearchAdapter> getAdapters();
20
21 void addAdapter(ILocalSearchAdapter adapter);
22
23 void removeAdapter(ILocalSearchAdapter adapter);
24
25 void removeAdapters(List<ILocalSearchAdapter> adapter);
26
27 void addAdapters(List<ILocalSearchAdapter> adapter);
28
29} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/ILocalSearchAdapter.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/ILocalSearchAdapter.java
new file mode 100644
index 00000000..df64b5f1
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/ILocalSearchAdapter.java
@@ -0,0 +1,120 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, 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.localsearch.matcher;
10
11import java.util.Optional;
12
13import tools.refinery.viatra.runtime.localsearch.ExecutionLoggerAdapter;
14import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
15import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
16import tools.refinery.viatra.runtime.localsearch.plan.SearchPlan;
17import tools.refinery.viatra.runtime.localsearch.profiler.LocalSearchProfilerAdapter;
18
19
20/**
21 * A local search adapter allows external code to follow the internal executions of the local search matcher. Possible
22 * implementations of the interface include profilers and debuggers.
23 * <p>
24 * <strong>EXPERIMENTAL</strong>. A few shortcomings have been found for this interface late during the development
25 * lifecycle of version 2.0 whose solution might need breaking possible future implementors. Because of this, right now
26 * it is not recommended to provide implementations outside of VIATRA. If necessary, have a look at the built-in
27 * adapters that should fulfill most cases in the meantime. See bugs https://bugs.eclipse.org/bugs/show_bug.cgi?id=535101
28 * and https://bugs.eclipse.org/bugs/show_bug.cgi?id=535102 for details.
29 *
30 * @author Marton Bur
31 * @see ExecutionLoggerAdapter
32 * @see LocalSearchProfilerAdapter
33 *
34 */
35public interface ILocalSearchAdapter {
36
37 /**
38 *
39 * @since 1.2
40 */
41 default void adapterRegistered(ILocalSearchAdaptable adaptable) {};
42 /**
43 *
44 * @since 1.2
45 */
46 default void adapterUnregistered(ILocalSearchAdaptable adaptable) {};
47
48 /**
49 * Callback method to indicate the start of a matching process
50 *
51 * @param lsMatcher the local search matcher that starts the matching
52 */
53 default void patternMatchingStarted(LocalSearchMatcher lsMatcher) {};
54
55 /**
56 * Callback method to indicate the end of a matching process
57 * </p>
58 * <strong>WARNING</strong>: It is not guaranteed that this method will be called;
59 * it is possible that a match process will end after a match is found and no other matches are accessed.
60 *
61 * @param lsMatcher the local search matcher that finished
62 * @since 2.0
63 */
64 default void noMoreMatchesAvailable(LocalSearchMatcher lsMatcher) {};
65
66 /**
67 * Callback method to indicate switching to a new plan during the execution of a pattern matching
68 *
69 * @param oldPlan the plan that is finished. Value is null when the first plan is starting.
70 * @param newPlan the plan that will begin execution
71 * @since 2.0
72 */
73 default void planChanged(Optional<SearchPlan> oldPlan, Optional<SearchPlan> newPlan) {};
74
75 /**
76 * Callback method to indicate the selection of an operation to execute
77 *
78 * @param plan the current plan executor
79 * @param frame the current matching frame
80 * @param isBacktrack if true, the selected operation was reached via backtracking
81 * @since 2.0
82 */
83 default void operationSelected(SearchPlan plan, ISearchOperation operation, MatchingFrame frame, boolean isBacktrack) {};
84
85 /**
86 * Callback method to indicate that an operation is executed
87 *
88 * @param plan the current plan
89 * @param frame the current matching frame
90 * @param isSuccessful if true, the operation executed successfully, or false if the execution failed and backtracking will happen
91 * @since 2.0
92 */
93 default void operationExecuted(SearchPlan plan, ISearchOperation operation, MatchingFrame frame, boolean isSuccessful) {};
94
95 /**
96 * Callback that is used to indicate that a match has been found
97 *
98 * @param plan the search plan executor that found the match
99 * @param frame the frame that holds the substitutions of the variables that match
100 * @since 2.0
101 */
102 default void matchFound(SearchPlan plan, MatchingFrame frame) {};
103 /**
104 * Callback that is used to indicate that the previously reported match has been found as a duplicate, thus will be ignored from the match results.
105 *
106 * @param plan the search plan executor that found the match
107 * @param frame the frame that holds the substitutions of the variables that match
108 * @since 2.0
109 */
110 default void duplicateMatchFound(MatchingFrame frame) {};
111
112 /**
113 * Callback method to indicate that a search plan is initialized in an executor with the given frame and starting operation
114 *
115 * @param searchPlan
116 * @param frame
117 * @since 2.0
118 */
119 default void executorInitializing(SearchPlan searchPlan, MatchingFrame frame) {};
120}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/ISearchContext.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/ISearchContext.java
new file mode 100644
index 00000000..380774bb
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/ISearchContext.java
@@ -0,0 +1,143 @@
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.localsearch.matcher;
10
11import java.util.Collections;
12import java.util.Set;
13
14import org.apache.log4j.Logger;
15import org.eclipse.emf.ecore.EClass;
16import org.eclipse.emf.ecore.EDataType;
17import org.eclipse.emf.ecore.EStructuralFeature;
18import tools.refinery.viatra.runtime.base.api.IndexingLevel;
19import tools.refinery.viatra.runtime.base.api.NavigationHelper;
20import tools.refinery.viatra.runtime.localsearch.matcher.integration.IAdornmentProvider;
21import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
22import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
23import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
24import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
25import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
26import tools.refinery.viatra.runtime.matchers.util.ICache;
27import tools.refinery.viatra.runtime.matchers.util.IProvider;
28
29/**
30 * The {@link ISearchContext} interface allows search operations to reuse platform services such as the indexer.
31 *
32 * @author Zoltan Ujhelyi
33 * @noreference This interface is not intended to be referenced by clients.
34 * @noimplement This interface is not intended to be implemented by clients.
35 * @noextend This interface is not intended to be extended by clients.
36 *
37 */
38public interface ISearchContext {
39
40 /**
41 * Provides access to the generic query runtime context of the current engine
42 * @since 1.7
43 */
44 IQueryRuntimeContext getRuntimeContext();
45
46 /**
47 * @param classes
48 * @param dataTypes
49 * @param features
50 */
51 void registerObservedTypes(Set<EClass> classes, Set<EDataType> dataTypes, Set<EStructuralFeature> features);
52
53 /**
54 * Returns a matcher for a selected query specification.
55 *
56 * @throws ViatraQueryRuntimeException
57 * @since 1.5
58 */
59 IQueryResultProvider getMatcher(CallWithAdornment dependency);
60
61 /**
62 * Allows search operations to cache values through the entire lifecycle of the local search backend. The values are
63 * calculated if not cached before using the given provider, or returned from the cache accordingly.
64 *
65 * @since 1.7
66 */
67 <T> T accessBackendLevelCache(Object key, Class<? extends T> clazz, IProvider<T> valueProvider);
68
69 /**
70 * Returns the engine-specific logger
71 *
72 * @since 2.0
73 */
74 Logger getLogger();
75
76 /**
77 * @noreference This class is not intended to be referenced by clients.
78 * @noimplement This interface is not intended to be implemented by clients.
79 * @noextend This interface is not intended to be extended by clients.
80 */
81 public class SearchContext implements ISearchContext {
82
83 private final NavigationHelper navigationHelper;
84 private final IQueryRuntimeContext runtimeContext;
85
86 private final ICache backendLevelCache;
87 private final Logger logger;
88 private final ResultProviderRequestor resultProviderRequestor;
89
90 /**
91 * Initializes a search context using an arbitrary backend context
92 */
93 public SearchContext(IQueryBackendContext backendContext, ICache backendLevelCache,
94 ResultProviderRequestor resultProviderRequestor) {
95 this.resultProviderRequestor = resultProviderRequestor;
96 this.runtimeContext = backendContext.getRuntimeContext();
97 this.logger = backendContext.getLogger();
98 this.navigationHelper = null;
99
100 this.backendLevelCache = backendLevelCache;
101 }
102
103 public void registerObservedTypes(Set<EClass> classes, Set<EDataType> dataTypes, Set<EStructuralFeature> features) {
104 if (this.navigationHelper.isInWildcardMode()) {
105 // In wildcard mode, everything is registered (+ register throws an exception)
106 return;
107 }
108 this.navigationHelper.registerObservedTypes(classes, dataTypes, features, IndexingLevel.FULL);
109 }
110
111 /**
112 * @throws ViatraQueryRuntimeException
113 * @since 2.1
114 */
115 @Override
116 public IQueryResultProvider getMatcher(CallWithAdornment dependency) {
117 // Inject adornment for referenced pattern
118 IAdornmentProvider adornmentProvider = query -> {
119 if (query.equals(dependency.getReferredQuery())){
120 return Collections.singleton(dependency.getAdornment());
121 }
122 return Collections.emptySet();
123 };
124 return resultProviderRequestor.requestResultProvider(dependency.getCall(),
125 IAdornmentProvider.toHint(adornmentProvider));
126 }
127
128 @Override
129 public <T> T accessBackendLevelCache(Object key, Class<? extends T> clazz, IProvider<T> valueProvider) {
130 return backendLevelCache.getValue(key, clazz, valueProvider);
131 }
132
133 public IQueryRuntimeContext getRuntimeContext() {
134 return runtimeContext;
135 }
136
137 @Override
138 public Logger getLogger() {
139 return logger;
140 }
141
142 }
143}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java
new file mode 100644
index 00000000..e31d7b5c
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java
@@ -0,0 +1,301 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Marton Bur, 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.localsearch.matcher;
10
11import java.util.ArrayList;
12import java.util.HashSet;
13import java.util.Iterator;
14import java.util.LinkedList;
15import java.util.List;
16import java.util.NoSuchElementException;
17import java.util.Objects;
18import java.util.Optional;
19import java.util.Set;
20import java.util.Spliterator;
21import java.util.Spliterators;
22import java.util.stream.Collectors;
23import java.util.stream.Stream;
24import java.util.stream.StreamSupport;
25
26import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
27import tools.refinery.viatra.runtime.localsearch.plan.IPlanDescriptor;
28import tools.refinery.viatra.runtime.localsearch.plan.SearchPlan;
29import tools.refinery.viatra.runtime.localsearch.plan.SearchPlanExecutor;
30import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
31import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
32import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
33import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
34import tools.refinery.viatra.runtime.matchers.tuple.VolatileModifiableMaskedTuple;
35import tools.refinery.viatra.runtime.matchers.util.Preconditions;
36
37/**
38 * @author Zoltan Ujhelyi
39 * @noinstantiate This class is not intended to be instantiated by clients.
40 */
41public final class LocalSearchMatcher implements ILocalSearchAdaptable {
42
43 private final List<SearchPlanExecutor> plan;
44 private final IPlanDescriptor planDescriptor;
45 private final List<ILocalSearchAdapter> adapters;
46
47 /**
48 * @since 2.0
49 */
50 public List<SearchPlanExecutor> getPlan() {
51 return plan;
52 }
53
54 @Override
55 public List<ILocalSearchAdapter> getAdapters() {
56 return new ArrayList<>(adapters);
57 }
58
59 private abstract class PlanExecutionIterator implements Iterator<Tuple> {
60
61 protected final Iterator<SearchPlanExecutor> planIterator;
62
63 protected SearchPlanExecutor currentPlan;
64 protected MatchingFrame frame;
65 protected final Set<ITuple> matchSet;
66 protected VolatileModifiableMaskedTuple parametersOfFrameView;
67 private boolean isNextMatchCalculated;
68
69 public PlanExecutionIterator(final Iterator<SearchPlanExecutor> planIterator) {
70 this.planIterator = planIterator;
71 isNextMatchCalculated = false;
72 matchSet = new HashSet<>();
73 }
74
75 protected boolean selectNextPlan() {
76 if(currentPlan != null) {
77 currentPlan.removeAdapters(adapters);
78 }
79 boolean validPlanSelected = false;
80
81 SearchPlanExecutor nextPlan = null;
82
83 while (!validPlanSelected && planIterator.hasNext()) {
84 nextPlan = planIterator.next();
85 nextPlan.addAdapters(adapters);
86 nextPlan.resetPlan();
87
88 validPlanSelected = initializeMatchingFrame(nextPlan);
89 }
90
91 if (validPlanSelected) {
92 for (ILocalSearchAdapter adapter : adapters) {
93 adapter.planChanged(Optional.ofNullable(currentPlan).map(SearchPlanExecutor::getSearchPlan),
94 Optional.ofNullable(nextPlan).map(SearchPlanExecutor::getSearchPlan));
95 }
96 currentPlan = nextPlan;
97 return true;
98 } else {
99 currentPlan = null;
100 return false;
101 }
102 }
103
104 protected abstract boolean initializeMatchingFrame(SearchPlanExecutor nextPlan);
105
106 private boolean findNextNewMatchInCurrentPlan() {
107 boolean foundMatch = currentPlan.execute(frame);
108 while (foundMatch && matchSet.contains(parametersOfFrameView)) {
109 for (ILocalSearchAdapter adapter : adapters) {
110 adapter.duplicateMatchFound(frame);
111 }
112 foundMatch = currentPlan.execute(frame);
113 }
114 return foundMatch;
115 }
116
117 @Override
118 public boolean hasNext() {
119 if (isNextMatchCalculated) {
120 return true;
121 }
122 if (currentPlan == null) {
123 return false;
124 }
125 boolean foundMatch = findNextNewMatchInCurrentPlan();
126
127 while (!foundMatch && planIterator.hasNext()) {
128 foundMatch = selectNextPlan() && findNextNewMatchInCurrentPlan();
129 }
130 if (!foundMatch) {
131 for (ILocalSearchAdapter adapter : adapters) {
132 adapter.noMoreMatchesAvailable(LocalSearchMatcher.this);
133 }
134 }
135 isNextMatchCalculated = foundMatch;
136 return foundMatch;
137 }
138
139 @Override
140 public Tuple next() {
141 if (!hasNext()) {
142 throw new NoSuchElementException("No more matches available.");
143 }
144 isNextMatchCalculated = false;
145 final Tuple match = parametersOfFrameView.toImmutable();
146 matchSet.add(match);
147 return match;
148 }
149 }
150
151 private class PlanExecutionIteratorWithArrayParameters extends PlanExecutionIterator {
152
153 private final Object[] parameterValues;
154
155 public PlanExecutionIteratorWithArrayParameters(Iterator<SearchPlanExecutor> planIterator, final Object[] parameterValues) {
156 super(planIterator);
157 this.parameterValues = parameterValues;
158 selectNextPlan();
159 }
160
161 protected boolean initializeMatchingFrame(SearchPlanExecutor nextPlan) {
162 frame = new MatchingFrame(nextPlan.getVariableMapping().size());
163 parametersOfFrameView = new VolatileModifiableMaskedTuple(frame, nextPlan.getParameterMask());
164 for (int i = 0; i < parameterValues.length; i++) {
165 Object valueToSet = parameterValues[i];
166 if (valueToSet != null) {
167 Object oldValue = parametersOfFrameView.get(i);
168 if (oldValue == null) {
169 parametersOfFrameView.set(i, valueToSet);
170 } else if (!Objects.equals(valueToSet, oldValue)) {
171 // Initial value setting resulted in contradictory values. This can happen because two parameter
172 // variables have been unified but the call provides different values for the parameters.
173 return false;
174 }
175 // If oldValue is not null but equal to newValue, the setting can be ignored
176 }
177 }
178
179 return true;
180 }
181 }
182 private class PlanExecutionIteratorWithTupleParameters extends PlanExecutionIterator {
183
184 private final ITuple parameterValues;
185 private final TupleMask parameterSeedMask;
186
187 public PlanExecutionIteratorWithTupleParameters(Iterator<SearchPlanExecutor> planIterator, final TupleMask parameterSeedMask, final ITuple parameterValues) {
188 super(planIterator);
189 this.parameterSeedMask = parameterSeedMask;
190 this.parameterValues = parameterValues;
191 selectNextPlan();
192 }
193
194 protected boolean initializeMatchingFrame(SearchPlanExecutor nextPlan) {
195 frame = new MatchingFrame(nextPlan.getVariableMapping().size());
196 parametersOfFrameView = new VolatileModifiableMaskedTuple(frame, nextPlan.getParameterMask());
197 for (int i = 0; i < parameterSeedMask.getSize(); i++) {
198 int index = parameterSeedMask.indices[i];
199 Object valueToSet = parameterValues.get(i);
200 if (valueToSet != null) {
201 Object oldValue = parametersOfFrameView.get(index);
202 if (oldValue == null) {
203 parametersOfFrameView.set(index, valueToSet);
204 } else if (!Objects.equals(valueToSet, oldValue)) {
205 // Initial value setting resulted in contradictory values. This can happen because two parameter
206 // variables have been unified but the call provides different values for the parameters.
207 return false;
208 }
209 // If oldValue is not null but equal to newValue, the setting can be ignored
210 }
211 }
212
213 return true;
214 }
215 }
216
217 /**
218 * @since 2.0
219 */
220 public LocalSearchMatcher(ISearchContext searchContext, IPlanDescriptor planDescriptor, List<SearchPlan> plan) {
221 Preconditions.checkArgument(planDescriptor != null, "Cannot initialize matcher with null query.");
222 this.planDescriptor = planDescriptor;
223 this.plan = plan.stream().map(p -> new SearchPlanExecutor(p, searchContext)).collect(Collectors.toList());
224 this.adapters = new LinkedList<>();
225 }
226
227 @Override
228 public void addAdapter(ILocalSearchAdapter adapter) {
229 this.adapters.add(adapter);
230 adapter.adapterRegistered(this);
231 }
232
233 @Override
234 public void removeAdapter(ILocalSearchAdapter adapter) {
235 this.adapters.remove(adapter);
236 adapter.adapterUnregistered(this);
237 }
238
239 @Override
240 public void addAdapters(List<ILocalSearchAdapter> adapters) {
241 this.adapters.addAll(adapters);
242 for (ILocalSearchAdapter adapter : adapters) {
243 adapter.adapterRegistered(this);
244 }
245 }
246
247 @Override
248 public void removeAdapters(List<ILocalSearchAdapter> adapters) {
249 this.adapters.removeAll(adapters);
250 for (ILocalSearchAdapter adapter : adapters) {
251 adapter.adapterUnregistered(this);
252 }
253 }
254
255 public int getParameterCount() {
256 return planDescriptor.getQuery().getParameters().size();
257 }
258
259 private void matchingStarted() {
260 for (ILocalSearchAdapter adapter : adapters) {
261 adapter.patternMatchingStarted(this);
262 }
263 }
264
265 /**
266 * @since 2.0
267 */
268 public Stream<Tuple> streamMatches(final Object[] parameterValues) {
269 matchingStarted();
270 PlanExecutionIterator it = new PlanExecutionIteratorWithArrayParameters(plan.iterator(), parameterValues);
271 return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it,
272 Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.DISTINCT), false);
273 }
274
275 /**
276 * @since 2.0
277 */
278 public Stream<Tuple> streamMatches(TupleMask parameterSeedMask, final ITuple parameterValues) {
279 matchingStarted();
280 PlanExecutionIterator it = new PlanExecutionIteratorWithTupleParameters(
281 plan.iterator(), parameterSeedMask, parameterValues);
282 return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it,
283 Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.DISTINCT), false);
284 }
285
286 /**
287 * Returns the query specification this matcher used as source for the implementation
288 * @return never null
289 */
290 public PQuery getQuerySpecification() {
291 return planDescriptor.getQuery();
292 }
293
294
295 /**
296 * @since 1.5
297 */
298 public IPlanDescriptor getPlanDescriptor() {
299 return planDescriptor;
300 }
301}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/MatcherReference.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/MatcherReference.java
new file mode 100644
index 00000000..6bf25192
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/MatcherReference.java
@@ -0,0 +1,97 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Zoltan Ujhelyi, Marton Bur, 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.localsearch.matcher;
10
11import java.util.Set;
12
13import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
14import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
15import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
16
17public class MatcherReference {
18 final PQuery query;
19 final Set<PParameter> adornment;
20
21 /**
22 * Hints that can override the callee's own hints. This field is intentionally left out from hashCode and equals
23 */
24 final QueryEvaluationHint hints;
25
26 /**
27 * @since 1.4
28 */
29 public MatcherReference(PQuery query, Set<PParameter> adornment, QueryEvaluationHint hints) {
30 super();
31 this.query = query;
32 this.adornment = adornment;
33 this.hints = hints;
34 }
35
36 public MatcherReference(PQuery query, Set<PParameter> adornment){
37 this(query, adornment, null);
38 }
39
40 public PQuery getQuery() {
41 return query;
42 }
43 public Set<PParameter> getAdornment() {
44 return adornment;
45 }
46 @Override
47 public int hashCode() {
48 final int prime = 31;
49 int result = 1;
50
51 result = prime * result + ((adornment == null) ? 0 : adornment.hashCode());
52 result = prime * result + ((query == null) ? 0 : query.hashCode());
53 return result;
54 }
55 @Override
56 public boolean equals(Object obj) {
57 if (this == obj)
58 return true;
59 if (obj == null)
60 return false;
61 if (getClass() != obj.getClass())
62 return false;
63 MatcherReference other = (MatcherReference) obj;
64 if (adornment == null) {
65 if (other.adornment != null)
66 return false;
67 } else if (!adornment.equals(other.adornment))
68 return false;
69 if (query == null) {
70 if (other.query != null)
71 return false;
72 } else if (!query.equals(other.query))
73 return false;
74 return true;
75 }
76
77 /**
78 * @return the hints to override the called reference's own hints with. Can be null.
79 * @since 1.4
80 */
81 public QueryEvaluationHint getHints() {
82 return hints;
83 }
84
85 @Override
86 public String toString() {
87 StringBuilder sb = new StringBuilder();
88 sb.append(query.getFullyQualifiedName());
89 sb.append("(");
90 for(PParameter p : query.getParameters()){
91 sb.append(adornment.contains(p) ? "b" : "f");
92 }
93 sb.append(")");
94 return sb.toString();
95 }
96
97} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/AbstractLocalSearchResultProvider.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/AbstractLocalSearchResultProvider.java
new file mode 100644
index 00000000..1ae24d2d
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/AbstractLocalSearchResultProvider.java
@@ -0,0 +1,532 @@
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.localsearch.matcher.integration;
10
11import java.lang.reflect.InvocationTargetException;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.HashMap;
16import java.util.HashSet;
17import java.util.LinkedHashSet;
18import java.util.LinkedList;
19import java.util.List;
20import java.util.Map;
21import java.util.Objects;
22import java.util.Optional;
23import java.util.Queue;
24import java.util.Set;
25import java.util.concurrent.Callable;
26import java.util.stream.Collectors;
27import java.util.stream.IntStream;
28import java.util.stream.Stream;
29
30import tools.refinery.viatra.runtime.localsearch.exceptions.LocalSearchException;
31import tools.refinery.viatra.runtime.localsearch.matcher.CallWithAdornment;
32import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
33import tools.refinery.viatra.runtime.localsearch.matcher.LocalSearchMatcher;
34import tools.refinery.viatra.runtime.localsearch.matcher.MatcherReference;
35import tools.refinery.viatra.runtime.localsearch.plan.IPlanDescriptor;
36import tools.refinery.viatra.runtime.localsearch.plan.IPlanProvider;
37import tools.refinery.viatra.runtime.localsearch.plan.SearchPlan;
38import tools.refinery.viatra.runtime.localsearch.plan.SearchPlanForBody;
39import tools.refinery.viatra.runtime.localsearch.planner.compiler.IOperationCompiler;
40import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
41import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
42import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
43import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
44import tools.refinery.viatra.runtime.matchers.backend.IUpdateable;
45import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
46import tools.refinery.viatra.runtime.matchers.backend.QueryHintOption;
47import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
48import tools.refinery.viatra.runtime.matchers.context.IInputKey;
49import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
50import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
51import tools.refinery.viatra.runtime.matchers.context.IndexingService;
52import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
53import tools.refinery.viatra.runtime.matchers.planning.helpers.FunctionalDependencyHelper;
54import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference;
55import tools.refinery.viatra.runtime.matchers.psystem.PBody;
56import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
57import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
58import tools.refinery.viatra.runtime.matchers.psystem.queries.PQueries;
59import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
60import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IFlattenCallPredicate;
61import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
62import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
63import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
64import tools.refinery.viatra.runtime.matchers.util.Accuracy;
65
66/**
67 * @author Zoltan Ujhelyi
68 * @since 1.7
69 *
70 */
71public abstract class AbstractLocalSearchResultProvider implements IQueryResultProvider {
72
73 protected final LocalSearchBackend backend;
74 protected final IQueryBackendContext backendContext;
75 protected final IQueryRuntimeContext runtimeContext;
76 protected final PQuery query;
77 protected final QueryEvaluationHint userHints;
78 protected final Map<PQuery, LocalSearchHints> hintCache = new HashMap<>();
79 protected final IPlanProvider planProvider;
80 private static final String PLAN_CACHE_KEY = AbstractLocalSearchResultProvider.class.getName() + "#planCache";
81 private final Map<MatcherReference, IPlanDescriptor> planCache;
82 protected final ISearchContext searchContext;
83 /**
84 * @since 2.1
85 */
86 protected ResultProviderRequestor resultProviderRequestor;
87
88 /**
89 * @since 1.5
90 */
91 @SuppressWarnings({ "unchecked"})
92 public AbstractLocalSearchResultProvider(LocalSearchBackend backend, IQueryBackendContext context, PQuery query,
93 IPlanProvider planProvider, QueryEvaluationHint userHints) {
94 this.backend = backend;
95 this.backendContext = context;
96 this.query = query;
97
98 this.planProvider = planProvider;
99 this.userHints = userHints;
100 this.runtimeContext = context.getRuntimeContext();
101 this.resultProviderRequestor = backend.getResultProviderRequestor(query, userHints);
102 this.searchContext = new ISearchContext.SearchContext(backendContext, backend.getCache(), resultProviderRequestor);
103 this.planCache = backend.getCache().getValue(PLAN_CACHE_KEY, Map.class, HashMap::new);
104 }
105
106 protected abstract IOperationCompiler getOperationCompiler(IQueryBackendContext backendContext, LocalSearchHints configuration);
107
108 private IQueryRuntimeContext getRuntimeContext() {
109 return backend.getRuntimeContext();
110 }
111
112 private LocalSearchMatcher createMatcher(IPlanDescriptor plan, final ISearchContext searchContext) {
113 List<SearchPlan> executors = plan.getPlan().stream()
114 .map(input -> new SearchPlan(input.getBody(), input.getCompiledOperations(), input.calculateParameterMask(),
115 input.getVariableKeys()))
116 .collect(Collectors.toList());
117 return new LocalSearchMatcher(searchContext, plan, executors);
118 }
119
120 private IPlanDescriptor getOrCreatePlan(MatcherReference key, IQueryBackendContext backendContext, IOperationCompiler compiler, LocalSearchHints configuration, IPlanProvider planProvider) {
121 if (planCache.containsKey(key)){
122 return planCache.get(key);
123 } else {
124 IPlanDescriptor plan = planProvider.getPlan(backendContext, compiler,
125 resultProviderRequestor, configuration, key);
126 planCache.put(key, plan);
127 return plan;
128 }
129 }
130
131 private IPlanDescriptor getOrCreatePlan(MatcherReference key, IPlanProvider planProvider) {
132 if (planCache.containsKey(key)){
133 return planCache.get(key);
134 } else {
135 LocalSearchHints configuration = overrideDefaultHints(key.getQuery());
136 IOperationCompiler compiler = getOperationCompiler(backendContext, configuration);
137 IPlanDescriptor plan = planProvider.getPlan(backendContext, compiler,
138 resultProviderRequestor, configuration, key);
139 planCache.put(key, plan);
140 return plan;
141 }
142 }
143
144 private LocalSearchHints overrideDefaultHints(PQuery pQuery) {
145 if (hintCache.containsKey(pQuery)) {
146 return hintCache.get(pQuery);
147 } else {
148 LocalSearchHints hint = LocalSearchHints.getDefaultOverriddenBy(
149 computeOverridingHints(pQuery));
150 hintCache.put(pQuery, hint);
151 return hint;
152 }
153 }
154
155 /**
156 * Combine with {@link QueryHintOption#getValueOrDefault(QueryEvaluationHint)} to access
157 * hint settings not covered by {@link LocalSearchHints}
158 */
159 private QueryEvaluationHint computeOverridingHints(PQuery pQuery) {
160 return backendContext.getHintProvider().getQueryEvaluationHint(pQuery).overrideBy(userHints);
161 }
162
163 /**
164 * Prepare this result provider. This phase is separated from the constructor to allow the backend to cache its instance before
165 * requesting preparation for its dependencies.
166 * @since 1.5
167 */
168 public void prepare() {
169 try {
170 runtimeContext.coalesceTraversals(() -> {
171 LocalSearchHints configuration = overrideDefaultHints(query);
172 if (configuration.isUseBase()) {
173 indexInitializationBeforePlanning();
174 }
175 prepareDirectDependencies();
176 runtimeContext.executeAfterTraversal(AbstractLocalSearchResultProvider.this::preparePlansForExpectedAdornments);
177 return null;
178 });
179 } catch (InvocationTargetException e) {
180 throw new QueryProcessingException("Error while building required indexes: {1}", new String[]{e.getTargetException().getMessage()}, "Error while building required indexes.", query, e);
181 }
182 }
183
184 protected void preparePlansForExpectedAdornments() {
185 // Plan for possible adornments
186 for (Set<PParameter> adornment : overrideDefaultHints(query).getAdornmentProvider().getAdornments(query)) {
187 MatcherReference reference = new MatcherReference(query, adornment, userHints);
188 LocalSearchHints configuration = overrideDefaultHints(query);
189 IOperationCompiler compiler = getOperationCompiler(backendContext, configuration);
190 IPlanDescriptor plan = getOrCreatePlan(reference, backendContext, compiler, configuration, planProvider);
191 // Index keys
192 try {
193 if (configuration.isUseBase()) {
194 indexKeys(plan.getIteratedKeys());
195 }
196 } catch (InvocationTargetException e) {
197 throw new QueryProcessingException(e.getMessage(), null, e.getMessage(), query, e);
198 }
199 //Prepare dependencies
200 for(SearchPlanForBody body: plan.getPlan()){
201 for(CallWithAdornment dependency : body.getDependencies()){
202 searchContext.getMatcher(dependency);
203 }
204 }
205 }
206 }
207
208 protected void prepareDirectDependencies() {
209 // Do not prepare for any adornment at this point
210 IAdornmentProvider adornmentProvider = input -> Collections.emptySet();
211 QueryEvaluationHint adornmentHint = IAdornmentProvider.toHint(adornmentProvider);
212
213 for(IQueryReference call : getDirectDependencies()){
214 resultProviderRequestor.requestResultProvider(call, adornmentHint);
215 }
216 }
217
218 /**
219 * This method is called before planning start to allow indexing. It is important to note that this method is called
220 * inside a coalesceTraversals block, meaning (1) it is safe to add multiple registration requests as necessary, but
221 * (2) no value or statistics is available from the index.
222 *
223 * @throws ViatraQueryRuntimeException
224 */
225 protected void indexInitializationBeforePlanning() {
226 // By default, no indexing is necessary
227 }
228
229 /**
230 * Collects and indexes all types _directly_ referred by the PQuery {@link #query}. Types indirect
231 * @param requiredIndexingServices
232 */
233 protected void indexReferredTypesOfQuery(PQuery query, IndexingService requiredIndexingServices) {
234 PQueries.directlyRequiredTypesOfQuery(query, true /*only enumerables are considered for indexing */).forEach(
235 inputKey -> runtimeContext.ensureIndexed(inputKey, requiredIndexingServices)
236 );
237 }
238
239 private Set<IQueryReference> getDirectDependencies() {
240 IFlattenCallPredicate flattenPredicate = overrideDefaultHints(query).getFlattenCallPredicate();
241 Queue<PQuery> queue = new LinkedList<>();
242 Set<PQuery> visited = new HashSet<>();
243 Set<IQueryReference> result = new HashSet<>();
244 queue.add(query);
245
246 while(!queue.isEmpty()){
247 PQuery next = queue.poll();
248 visited.add(next);
249 for(PBody body : next.getDisjunctBodies().getBodies()){
250 for (IQueryReference call : body.getConstraintsOfType(IQueryReference.class)) {
251 if (call instanceof PositivePatternCall &&
252 flattenPredicate.shouldFlatten((PositivePatternCall) call))
253 {
254 PQuery dep = ((PositivePatternCall) call).getReferredQuery();
255 if (!visited.contains(dep)){
256 queue.add(dep);
257 }
258 } else {
259 result.add(call);
260 }
261 }
262 }
263 }
264 return result;
265 }
266
267 private LocalSearchMatcher initializeMatcher(Object[] parameters) {
268 return newLocalSearchMatcher(parameters);
269 }
270
271 private LocalSearchMatcher initializeMatcher(TupleMask parameterSeedMask) {
272 return newLocalSearchMatcher(parameterSeedMask.transformUnique(query.getParameters()));
273
274 }
275
276
277 /**
278 * @throws ViatraQueryRuntimeException
279 */
280 public LocalSearchMatcher newLocalSearchMatcher(ITuple parameters) {
281 final Set<PParameter> adornment = new HashSet<>();
282 for (int i = 0; i < parameters.getSize(); i++) {
283 if (parameters.get(i) != null) {
284 adornment.add(query.getParameters().get(i));
285 }
286 }
287
288 return newLocalSearchMatcher(adornment);
289 }
290
291 /**
292 * @throws ViatraQueryRuntimeException
293 */
294 public LocalSearchMatcher newLocalSearchMatcher(Object[] parameters) {
295 final Set<PParameter> adornment = new HashSet<>();
296 for (int i = 0; i < parameters.length; i++) {
297 if (parameters[i] != null) {
298 adornment.add(query.getParameters().get(i));
299 }
300 }
301
302 return newLocalSearchMatcher(adornment);
303 }
304
305 private LocalSearchMatcher newLocalSearchMatcher(final Set<PParameter> adornment) {
306 final MatcherReference reference = new MatcherReference(query, adornment, userHints);
307
308 IPlanDescriptor plan = getOrCreatePlan(reference, planProvider);
309 if (overrideDefaultHints(reference.getQuery()).isUseBase()){
310 try {
311 indexKeys(plan.getIteratedKeys());
312 } catch (InvocationTargetException e) {
313 throw new LocalSearchException("Could not index keys", e);
314 }
315 }
316
317 LocalSearchMatcher matcher = createMatcher(plan, searchContext);
318 matcher.addAdapters(backend.getAdapters());
319 return matcher;
320 }
321
322 private void indexKeys(final Iterable<IInputKey> keys) throws InvocationTargetException {
323 final IQueryRuntimeContext qrc = getRuntimeContext();
324 qrc.coalesceTraversals(new Callable<Void>() {
325
326 @Override
327 public Void call() throws Exception {
328 for(IInputKey key : keys){
329 if (key.isEnumerable()) {
330 qrc.ensureIndexed(key, IndexingService.INSTANCES);
331 }
332 }
333 return null;
334 }
335 });
336 }
337
338 @Override
339 public boolean hasMatch(Object[] parameters) {
340 final LocalSearchMatcher matcher = initializeMatcher(parameters);
341 return matcher.streamMatches(parameters).findAny().isPresent();
342 }
343
344 @Override
345 public boolean hasMatch(TupleMask parameterSeedMask, ITuple parameters) {
346 final LocalSearchMatcher matcher = initializeMatcher(parameterSeedMask);
347 return matcher.streamMatches(parameterSeedMask, parameters).findAny().isPresent();
348 }
349
350 @Override
351 public Optional<Tuple> getOneArbitraryMatch(Object[] parameters) {
352 final LocalSearchMatcher matcher = initializeMatcher(parameters);
353 return matcher.streamMatches(parameters).findAny();
354 }
355
356 @Override
357 public Optional<Tuple> getOneArbitraryMatch(TupleMask parameterSeedMask, ITuple parameters) {
358 final LocalSearchMatcher matcher = initializeMatcher(parameterSeedMask);
359 return matcher.streamMatches(parameterSeedMask, parameters).findAny();
360 }
361
362 @Override
363 public int countMatches(Object[] parameters) {
364 final LocalSearchMatcher matcher = initializeMatcher(parameters);
365 // Count returns long; casting to int - in case of integer overflow casting will throw the exception
366 return (int) matcher.streamMatches(parameters).count();
367 }
368
369 @Override
370 public int countMatches(TupleMask parameterSeedMask, ITuple parameters) {
371 final LocalSearchMatcher matcher = initializeMatcher(parameterSeedMask);
372 // Count returns long; casting to int - in case of integer overflow casting will throw the exception
373 return (int) matcher.streamMatches(parameterSeedMask, parameters).count();
374 }
375
376 private static final double ESTIMATE_CEILING = Long.MAX_VALUE / 16.0;
377
378 @Override
379 public Optional<Long> estimateCardinality(TupleMask groupMask, Accuracy requiredAccuracy) {
380 if (Accuracy.BEST_UPPER_BOUND.atLeastAsPreciseAs(requiredAccuracy)) { // approximate using parameter types
381 final List<PParameter> parameters = query.getParameters();
382 final Map<Set<Integer>, Set<Integer>> dependencies = backendContext.getQueryAnalyzer()
383 .getProjectedFunctionalDependencies(query, false);
384
385 List<Integer> projectionIndices = groupMask.getIndicesAsList();
386
387 return estimateParameterCombinations(requiredAccuracy, parameters, dependencies,
388 projectionIndices,
389 Collections.emptySet() /* No parameters with fixed value */).map(Double::longValue);
390 }
391 else return Optional.empty();
392 }
393
394 @Override
395 public Optional<Double> estimateAverageBucketSize(TupleMask groupMask, Accuracy requiredAccuracy) {
396 if (Accuracy.BEST_UPPER_BOUND.atLeastAsPreciseAs(requiredAccuracy)) { // approximate using parameter types
397 final List<PParameter> parameters = query.getParameters();
398 final Map<Set<Integer>, Set<Integer>> dependencies = backendContext.getQueryAnalyzer()
399 .getProjectedFunctionalDependencies(query, false);
400
401 // all parameters used for the estimation - determinized order
402 final List<Integer> allParameterIndices =
403 IntStream.range(0, parameters.size()).boxed().collect(Collectors.toList());
404
405 // some free parameters are functionally determined by bound parameters
406 final Set<Integer> boundOrImplied = FunctionalDependencyHelper.closureOf(groupMask.getIndicesAsList(),
407 dependencies);
408
409 return estimateParameterCombinations(requiredAccuracy, parameters, dependencies,
410 allParameterIndices,
411 boundOrImplied);
412 }
413 else return Optional.empty();
414 }
415
416 /**
417 * @since 2.1
418 * @noreference This method is not intended to be referenced by clients.
419 */
420 public double estimateCost(TupleMask inputBindingMask) {
421 // TODO this is currently an abstract cost, not really a branching factor
422
423 HashSet<PParameter> adornment = new HashSet<>(inputBindingMask.transform(query.getParameters()));
424 final MatcherReference reference = new MatcherReference(query, adornment, userHints);
425 IPlanDescriptor plan = getOrCreatePlan(reference, planProvider);
426
427 return plan.getPlan().stream().mapToDouble(SearchPlanForBody::getCost).sum();
428 }
429
430 /**
431 * Approximates using parameter types
432 */
433 private Optional<Double> estimateParameterCombinations(
434 Accuracy requiredAccuracy,
435 final List<PParameter> parameters,
436 final Map<Set<Integer>, Set<Integer>> functionalDependencies,
437 final Collection<Integer> parameterIndicesToEstimate,
438 final Set<Integer> otherDeterminingIndices)
439 {
440 // keep order deterministic
441 LinkedHashSet<Integer> freeParameterIndices = new LinkedHashSet<>(parameterIndicesToEstimate);
442
443 // determining indices are bound
444 freeParameterIndices.removeAll(otherDeterminingIndices);
445
446 // some free parameters are functionally determined by other free parameters
447 for (Integer candidateForRemoval : new ArrayList<>(freeParameterIndices)) {
448 List<Integer> others = Stream.concat(
449 otherDeterminingIndices.stream(),
450 freeParameterIndices.stream().filter(index -> !Objects.equals(index, candidateForRemoval))
451 ).collect(Collectors.toList());
452 Set<Integer> othersClosure = FunctionalDependencyHelper.closureOf(others, functionalDependencies);
453 if (othersClosure.contains(candidateForRemoval)) {
454 // other parameters functionally determine this mone, does not count towards estimate
455 freeParameterIndices.remove(candidateForRemoval);
456 }
457 }
458
459
460 Optional<Double> result = Optional.of(1.0);
461 // TODO this is currently works with declared types only. For better results, information from
462 // the Type inferrer should be included in the PSystem
463 for (int i = 0; (i < parameters.size()); i++) {
464 final IInputKey type = parameters.get(i).getDeclaredUnaryType();
465 if (freeParameterIndices.contains(i) && type != null) {
466 result = result.flatMap(accumulator ->
467 runtimeContext.estimateCardinality(type, TupleMask.identity(1), requiredAccuracy).map(multiplier ->
468 Math.min(accumulator * multiplier, ESTIMATE_CEILING /* avoid overflow */)
469 ));
470 }
471 }
472 // TODO better approximate cardinality based on plan, branching factors, etc.
473 return result;
474 }
475
476
477 @Override
478 public Stream<Tuple> getAllMatches(Object[] parameters) {
479 final LocalSearchMatcher matcher = initializeMatcher(parameters);
480 return matcher.streamMatches(parameters);
481 }
482
483 @Override
484 public Stream<Tuple> getAllMatches(TupleMask parameterSeedMask, ITuple parameters) {
485 final LocalSearchMatcher matcher = initializeMatcher(parameterSeedMask);
486 return matcher.streamMatches(parameterSeedMask, parameters);
487 }
488
489 @Override
490 public IQueryBackend getQueryBackend() {
491 return backend;
492 }
493
494 @Override
495 public void addUpdateListener(IUpdateable listener, Object listenerTag, boolean fireNow) {
496 // throw new UnsupportedOperationException(UPDATE_LISTENER_NOT_SUPPORTED);
497 }
498
499 @Override
500 public void removeUpdateListener(Object listenerTag) {
501 // throw new UnsupportedOperationException(UPDATE_LISTENER_NOT_SUPPORTED);
502 }
503
504 /**
505 * @since 1.4
506 */
507 public IMatcherCapability getCapabilites() {
508 LocalSearchHints configuration = overrideDefaultHints(query);
509 return configuration;
510 }
511
512 /**
513 * Forgets all stored plans in this result provider. If no plans are stored, nothing happens.
514 *
515 * @since 2.0
516 * @noreference This method is not intended to be referenced by clients; it should only used by {@link LocalSearchBackend}.
517 */
518 public void forgetAllPlans() {
519 planCache.clear();
520 }
521
522 /**
523 * Returns a search plan for a given adornment if exists
524 *
525 * @return a search plan for the pattern with the given adornment, or null if none exists
526 * @since 2.0
527 * @noreference This method is not intended to be referenced by clients; it should only used by {@link LocalSearchBackend}.
528 */
529 public IPlanDescriptor getSearchPlan(Set<PParameter> adornment) {
530 return planCache.get(new MatcherReference(query, adornment));
531 }
532} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/AllValidAdornments.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/AllValidAdornments.java
new file mode 100644
index 00000000..f801163e
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/AllValidAdornments.java
@@ -0,0 +1,37 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.matcher.integration;
10
11import java.util.Set;
12import java.util.stream.Collectors;
13
14import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
15import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameterDirection;
16import tools.refinery.viatra.runtime.matchers.psystem.queries.PQueries;
17import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
18import tools.refinery.viatra.runtime.matchers.util.Sets;
19
20
21/**
22 * This implementation calculates all valid adornments for the given query, respecting the parameter direction constraints.
23 *
24 * @author Grill Balázs
25 * @since 1.5
26 */
27public class AllValidAdornments implements IAdornmentProvider {
28
29 @Override
30 public Iterable<Set<PParameter>> getAdornments(PQuery query) {
31 final Set<PParameter> ins = query.getParameters().stream().filter(PQueries.parameterDirectionPredicate(PParameterDirection.IN)).collect(Collectors.toSet());
32 Set<PParameter> inouts = query.getParameters().stream().filter(PQueries.parameterDirectionPredicate(PParameterDirection.INOUT)).collect(Collectors.toSet());
33 Set<? extends Set<PParameter>> possibleInouts = Sets.powerSet(inouts);
34 return possibleInouts.stream().map(input -> Sets.union(ins, input)).collect(Collectors.toSet());
35 }
36
37}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/DontFlattenDisjunctive.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/DontFlattenDisjunctive.java
new file mode 100644
index 00000000..bf1b61b5
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/DontFlattenDisjunctive.java
@@ -0,0 +1,29 @@
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.localsearch.matcher.integration;
10
11import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
12import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IFlattenCallPredicate;
13
14/**
15 * Forbids flattening of patterns that have more than one body.
16 *
17 * @since 2.1
18
19 * @author Gabor Bergmann
20 *
21 */
22public class DontFlattenDisjunctive implements IFlattenCallPredicate {
23
24 @Override
25 public boolean shouldFlatten(PositivePatternCall positivePatternCall) {
26 return 1 >= positivePatternCall.getReferredQuery().getDisjunctBodies().getBodies().size();
27 }
28
29}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/DontFlattenIncrementalPredicate.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/DontFlattenIncrementalPredicate.java
new file mode 100644
index 00000000..1b918528
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/DontFlattenIncrementalPredicate.java
@@ -0,0 +1,44 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.matcher.integration;
10
11import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
12import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint.BackendRequirement;
13import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
14import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IFlattenCallPredicate;
15
16/**
17 * This implementation forbids flattening of patterns marked to be executed with a caching / incremental backend.
18 * This makes is possible for the user to configure hybrid matching via using
19 * the 'search' and 'incremental keywords in the pattern definition file.
20 *
21 * @since 1.5
22 *
23 */
24public class DontFlattenIncrementalPredicate implements IFlattenCallPredicate {
25
26 @Override
27 public boolean shouldFlatten(PositivePatternCall positivePatternCall) {
28 QueryEvaluationHint evaluationHints = positivePatternCall.getReferredQuery().getEvaluationHints();
29 if (evaluationHints == null) return true;
30
31 BackendRequirement backendRequirementType = evaluationHints.getQueryBackendRequirementType();
32 switch(backendRequirementType) {
33 case DEFAULT_CACHING:
34 return false;
35 case SPECIFIC:
36 return !evaluationHints.getQueryBackendFactory().isCaching();
37 case UNSPECIFIED:
38 case DEFAULT_SEARCH:
39 default:
40 return true;
41 }
42 }
43
44}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/GenericLocalSearchResultProvider.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/GenericLocalSearchResultProvider.java
new file mode 100644
index 00000000..ed6c1e5f
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/GenericLocalSearchResultProvider.java
@@ -0,0 +1,49 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, 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.localsearch.matcher.integration;
10
11import tools.refinery.viatra.runtime.localsearch.plan.IPlanProvider;
12import tools.refinery.viatra.runtime.localsearch.planner.compiler.GenericOperationCompiler;
13import tools.refinery.viatra.runtime.localsearch.planner.compiler.IOperationCompiler;
14import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
15import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
16import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
17import tools.refinery.viatra.runtime.matchers.context.IndexingService;
18import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
19
20/**
21 * @author Zoltan Ujhelyi
22 * @since 1.7
23 *
24 */
25public class GenericLocalSearchResultProvider extends AbstractLocalSearchResultProvider {
26
27 /**
28 * @throws ViatraQueryRuntimeException
29 */
30 public GenericLocalSearchResultProvider(LocalSearchBackend backend, IQueryBackendContext context, PQuery query,
31 IPlanProvider planProvider, QueryEvaluationHint userHints) {
32 super(backend, context, query, planProvider, userHints);
33 }
34
35 @Override
36 protected void indexInitializationBeforePlanning() {
37 super.indexInitializationBeforePlanning();
38
39 indexReferredTypesOfQuery(query, IndexingService.INSTANCES);
40 indexReferredTypesOfQuery(query, IndexingService.STATISTICS);
41 }
42
43 @Override
44 protected IOperationCompiler getOperationCompiler(IQueryBackendContext backendContext,
45 LocalSearchHints configuration) {
46 return new GenericOperationCompiler(runtimeContext);
47 }
48
49}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/IAdornmentProvider.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/IAdornmentProvider.java
new file mode 100644
index 00000000..86058be0
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/IAdornmentProvider.java
@@ -0,0 +1,72 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.matcher.integration;
10
11import java.util.Collections;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
15import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint.BackendRequirement;
16import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
17import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
18
19/**
20 * An adornment provider is used to define the adornments the pattern matcher should prepare for.
21 *
22 * <p>A default implementation is available in {@link AllValidAdornments} that describes all
23 * adornments fulfilling the parameter direction declarations;
24 * another default option (with better performance but restricted applicability) is {@link LazyPlanningAdornments}.
25 *
26 * <br><br>
27 *
28 * Users may implement this interface to limit the number of prepared plans based on some runtime information:
29 *
30 * <pre>
31 * class SomeAdornments{
32 *
33 * public Iterable&lt;Set&lt;{@link PParameter}>> getAdornments({@link PQuery} query){
34 * if (SomeGeneratedQuerySpecification.instance().getInternalQueryRepresentation().equals(query)){
35 * return Collections.singleton(Sets.filter(Sets.newHashSet(query.getParameters()), new Predicate<PParameter>() {
36 *
37 * &#64;Override
38 * public boolean apply(PParameter input) {
39 * // Decide whether this particular parameter will be bound
40 * return false;
41 * }
42 * }));
43 * }
44 * // Returning an empty iterable is safe for unknown queries
45 * return Collections.emptySet();
46 * }
47 *
48 * }
49 * </pre>
50 *
51 * @author Grill Balázs
52 * @since 1.5
53 *
54 */
55public interface IAdornmentProvider {
56
57 /**
58 * The bound parameter sets
59 */
60 public Iterable<Set<PParameter>> getAdornments(PQuery query);
61
62 /**
63 * @return a simple hint that only overrides the adornment provider
64 * @since 2.1
65 */
66 public static QueryEvaluationHint toHint(IAdornmentProvider adornmentProvider) {
67 return new QueryEvaluationHint(
68 Collections.singletonMap(LocalSearchHintOptions.ADORNMENT_PROVIDER, adornmentProvider),
69 BackendRequirement.UNSPECIFIED);
70 }
71
72}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LazyPlanningAdornments.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LazyPlanningAdornments.java
new file mode 100644
index 00000000..30b3689f
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LazyPlanningAdornments.java
@@ -0,0 +1,41 @@
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.localsearch.matcher.integration;
10
11import java.util.Collections;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
15import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
16
17/**
18 * This adornment provider does not trigger the preparation of any plans.
19 * Actual query plans will be computed on demand, when the first actual match request is made with a given adornment.
20 *
21 * <p> Caution: this is a safe default adornment provider for {@link GenericLocalSearchResultProvider} only;
22 * do not use for the EMF-specific LS backend.
23 *
24 * <p> The benefits is in execution time: query planning costs for adornments are postponed until first usage
25 * or even entirely avoided (when adornment is never used in practice).
26 * However, query evaluation time may become less predictable, as the first matcher call (with a given adornment)
27 * will include the planning cost.
28 * For benchmarking or other purposes where this is not desirable, use an adornment provider that demands plan precomputation for all necessary adornments.
29 *
30 * @author Gabor Bergmann
31 * @since 2.1
32 *
33 */
34public class LazyPlanningAdornments implements IAdornmentProvider {
35
36 @Override
37 public Iterable<Set<PParameter>> getAdornments(PQuery query) {
38 return Collections.emptySet();
39 }
40
41}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchBackend.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchBackend.java
new file mode 100644
index 00000000..ae51e2b0
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchBackend.java
@@ -0,0 +1,259 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, 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.localsearch.matcher.integration;
10
11import java.lang.reflect.InvocationTargetException;
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.Collection;
15import java.util.Collections;
16import java.util.HashSet;
17import java.util.List;
18import java.util.Map;
19import java.util.Set;
20import java.util.stream.Stream;
21
22import tools.refinery.viatra.runtime.localsearch.exceptions.LocalSearchException;
23import tools.refinery.viatra.runtime.localsearch.matcher.ILocalSearchAdapter;
24import tools.refinery.viatra.runtime.localsearch.plan.IPlanDescriptor;
25import tools.refinery.viatra.runtime.localsearch.plan.IPlanProvider;
26import tools.refinery.viatra.runtime.localsearch.plan.SimplePlanProvider;
27import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
28import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
29import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
30import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
31import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
32import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
33import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
34import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
35import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
36import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
37import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
38import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
39import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
40import tools.refinery.viatra.runtime.matchers.util.ICache;
41import tools.refinery.viatra.runtime.matchers.util.PurgableCache;
42
43/**
44 * @author Marton Bur, Zoltan Ujhelyi
45 * @noextend This class is not intended to be subclassed by clients.
46 */
47public abstract class LocalSearchBackend implements IQueryBackend {
48
49 IQueryBackendContext context;
50 IPlanProvider planProvider;
51 private final Set<ILocalSearchAdapter> adapters = new HashSet<>();
52
53 private final PurgableCache generalCache;
54
55 private final Map<PQuery, List<AbstractLocalSearchResultProvider>> resultProviderCache = CollectionsFactory.createMap();
56
57
58 /**
59 * @since 1.5
60 */
61 public LocalSearchBackend(IQueryBackendContext context) {
62 super();
63 this.context = context;
64 this.generalCache = new PurgableCache();
65 this.planProvider = new SimplePlanProvider(context.getLogger());
66 }
67
68 @Override
69 public void flushUpdates() {
70
71 }
72
73 @Override
74 public IQueryResultProvider getResultProvider(PQuery query) {
75 return getResultProvider(query, null);
76 }
77
78 /**
79 * @since 1.4
80 */
81 @Override
82 public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint hints) {
83
84 final QueryEvaluationHint callHints = getHintProvider().getQueryEvaluationHint(query).overrideBy(hints);
85 IMatcherCapability requestedCapability = context.getRequiredMatcherCapability(query, callHints);
86 for(AbstractLocalSearchResultProvider existingResultProvider : resultProviderCache.getOrDefault(query, Collections.emptyList())){
87 if (requestedCapability.canBeSubstitute(existingResultProvider.getCapabilites())){
88 return existingResultProvider;
89 }
90 }
91
92 AbstractLocalSearchResultProvider resultProvider = initializeResultProvider(query, hints);
93 resultProviderCache.computeIfAbsent(query, k->new ArrayList<>()).add(resultProvider);
94 resultProvider.prepare();
95 return resultProvider;
96 }
97
98 /**
99 * Returns a requestor that this backend uses while processing pattern calls <i>from</i> this query.
100 * @noreference This method is not intended to be referenced by clients.
101 * @since 2.1
102 */
103 public ResultProviderRequestor getResultProviderRequestor(PQuery query, QueryEvaluationHint userHints) {
104 QueryEvaluationHint hintOnQuery =
105 context.getHintProvider().getQueryEvaluationHint(query).overrideBy(userHints);
106 LocalSearchHints defaultsApplied = LocalSearchHints.getDefaultOverriddenBy(hintOnQuery);
107
108 return new ResultProviderRequestor(this,
109 context.getResultProviderAccess(),
110 context.getHintProvider(),
111 defaultsApplied.getCallDelegationStrategy(),
112 userHints,
113 /* no global overrides */ null);
114 }
115
116 /**
117 * @throws ViatraQueryRuntimeException
118 * @since 1.7
119 */
120 protected abstract AbstractLocalSearchResultProvider initializeResultProvider(PQuery query, QueryEvaluationHint hints);
121
122 @Override
123 public void dispose() {
124 resultProviderCache.clear();
125 generalCache.purge();
126 }
127
128 @Override
129 public boolean isCaching() {
130 return false;
131 }
132
133 /**
134 * @since 2.0
135 */
136 @Override
137 public AbstractLocalSearchResultProvider peekExistingResultProvider(PQuery query) {
138 return resultProviderCache.getOrDefault(query, Collections.emptyList()).stream().findAny().orElse(null);
139 }
140
141 /**
142 * @since 1.4
143 */
144 public IQueryRuntimeContext getRuntimeContext() {
145 return context.getRuntimeContext();
146 }
147
148
149 /**
150 * @since 1.5
151 */
152 public QueryAnalyzer getQueryAnalyzer() {
153 return context.getQueryAnalyzer();
154 }
155
156
157 /**
158 * @since 1.4
159 */
160 public IQueryBackendHintProvider getHintProvider() {
161 return context.getHintProvider();
162 }
163
164 /**
165 * @since 1.5
166 */
167 public void addAdapter(ILocalSearchAdapter adapter){
168 adapters.add(adapter);
169 }
170
171 /**
172 * @since 1.5
173 */
174 public void removeAdapter(ILocalSearchAdapter adapter){
175 adapters.remove(adapter);
176 }
177
178 /**
179 * Return a copy of the current adapters
180 * @since 1.7
181 */
182 public List<ILocalSearchAdapter> getAdapters() {
183 return new ArrayList<>(adapters);
184 }
185
186 /**
187 * @since 1.5
188 */
189 public IQueryBackendContext getBackendContext() {
190 return context;
191 }
192
193 /**
194 * Returns the internal cache of the backend
195 * @since 1.7
196 * @noreference This method is not intended to be referenced by clients.
197 */
198 public ICache getCache() {
199 return generalCache;
200 }
201
202 /**
203 * Updates the previously stored search plans for one or more given queries, computing a new set of plans if
204 * necessary. The new plans created are the same that would be created by executing prepare on the given query
205 * definitions.
206 *
207 * @since 2.0
208 */
209 public void recomputePlans(PQuery... queries) {
210 recomputePlans(Arrays.stream(queries).flatMap(query -> resultProviderCache.getOrDefault(query, Collections.emptyList()).stream()));
211 }
212
213 /**
214 * Updates the previously stored search plans for one or more given queries, computing a new set of plans if
215 * necessary The new plans created are the same that would be created by executing prepare on the given query
216 * definitions.
217 *
218 * @since 2.0
219 */
220 public void recomputePlans(Collection<PQuery> queries) {
221 recomputePlans(queries.stream().flatMap(query -> resultProviderCache.getOrDefault(query, Collections.emptyList()).stream()));
222 }
223
224 /**
225 * Updates the previously stored search plans for one or more given queries, computing a new set of plans if
226 * necessary The new plans created are the same that would be created by executing prepare on the given query
227 * definitions.
228 *
229 * @since 2.0
230 */
231 public void recomputePlans() {
232 recomputePlans(resultProviderCache.values().stream().flatMap(List::stream));
233 }
234
235 private void recomputePlans(Stream<AbstractLocalSearchResultProvider> resultProviders) {
236 try {
237 context.getRuntimeContext().coalesceTraversals(() -> {
238 resultProviders.forEach(resultProvider -> {
239 resultProvider.forgetAllPlans();
240 resultProvider.prepare();
241 });
242 return null;
243 });
244 } catch (InvocationTargetException e) {
245 throw new LocalSearchException("Error while rebuilding plans: " + e.getMessage(), e);
246 }
247 }
248
249 /**
250 * Returns a search plan for a given query and adornment if such plan is already calculated.
251 *
252 * @return a previously calculated search plan for the given query and adornment, or null if no such plan exists
253 * @since 2.0
254 */
255 public IPlanDescriptor getSearchPlan(PQuery query, Set<PParameter> adornment) {
256 final AbstractLocalSearchResultProvider resultProvider = peekExistingResultProvider(query);
257 return (resultProvider == null) ? null : resultProvider.getSearchPlan(adornment);
258 }
259}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchBackendFactoryProvider.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchBackendFactoryProvider.java
new file mode 100644
index 00000000..bba381d3
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchBackendFactoryProvider.java
@@ -0,0 +1,29 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.localsearch.matcher.integration;
10
11import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
12import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactoryProvider;
13
14/**
15 * @since 2.0
16 */
17public class LocalSearchBackendFactoryProvider implements IQueryBackendFactoryProvider {
18
19 @Override
20 public IQueryBackendFactory getFactory() {
21 return LocalSearchEMFBackendFactory.INSTANCE;
22 }
23
24 @Override
25 public boolean isSystemDefaultSearchBackend() {
26 return true;
27 }
28
29} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchEMFBackendFactory.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchEMFBackendFactory.java
new file mode 100644
index 00000000..5bffebac
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchEMFBackendFactory.java
@@ -0,0 +1,65 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, 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.localsearch.matcher.integration;
10
11import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
12import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
13import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
14import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
15import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
16import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
17
18/**
19 * @author Marton Bur, Zoltan Ujhelyi
20 * @since 2.0
21 *
22 */
23public enum LocalSearchEMFBackendFactory implements IQueryBackendFactory {
24
25
26 INSTANCE;
27
28 /**
29 * @since 1.5
30 */
31 @Override
32 public IQueryBackend create(IQueryBackendContext context) {
33 return new LocalSearchBackend(context) {
34
35 @Override
36 protected AbstractLocalSearchResultProvider initializeResultProvider(PQuery query, QueryEvaluationHint hints) {
37 return new LocalSearchResultProvider(this, context, query, planProvider, hints);
38 }
39
40 @Override
41 public IQueryBackendFactory getFactory() {
42 return INSTANCE;
43 }
44 };
45 }
46
47 @Override
48 public Class<? extends IQueryBackend> getBackendClass() {
49 return LocalSearchBackend.class;
50 }
51
52 /**
53 * @since 1.4
54 */
55 @Override
56 public IMatcherCapability calculateRequiredCapability(PQuery query, QueryEvaluationHint hint) {
57 return LocalSearchHints.parse(hint);
58 }
59
60 @Override
61 public boolean isCaching() {
62 return false;
63 }
64
65}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchGenericBackendFactory.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchGenericBackendFactory.java
new file mode 100644
index 00000000..1dd08f98
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchGenericBackendFactory.java
@@ -0,0 +1,65 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, 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.localsearch.matcher.integration;
10
11import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
12import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
13import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
14import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
15import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
16import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
17
18/**
19 * @author Marton Bur, Zoltan Ujhelyi
20 * @since 1.7
21 *
22 */
23public enum LocalSearchGenericBackendFactory implements IQueryBackendFactory {
24
25 INSTANCE;
26
27 /**
28 * @since 1.5
29 */
30 @Override
31 public IQueryBackend create(IQueryBackendContext context) {
32 return new LocalSearchBackend(context) {
33
34 @Override
35 protected AbstractLocalSearchResultProvider initializeResultProvider(PQuery query, QueryEvaluationHint hints) {
36 return new GenericLocalSearchResultProvider(this, context, query, planProvider, hints);
37 }
38
39 @Override
40 public IQueryBackendFactory getFactory() {
41 return INSTANCE;
42 }
43
44 };
45 }
46
47 @Override
48 public Class<? extends IQueryBackend> getBackendClass() {
49 return LocalSearchBackend.class;
50 }
51
52 /**
53 * @since 1.4
54 */
55 @Override
56 public IMatcherCapability calculateRequiredCapability(PQuery query, QueryEvaluationHint hint) {
57 return LocalSearchHints.parse(hint);
58 }
59
60 @Override
61 public boolean isCaching() {
62 return false;
63 }
64
65}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchGenericBackendFactoryProvider.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchGenericBackendFactoryProvider.java
new file mode 100644
index 00000000..ea422d91
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchGenericBackendFactoryProvider.java
@@ -0,0 +1,24 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.localsearch.matcher.integration;
10
11import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
12import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactoryProvider;
13
14/**
15 * @since 2.0
16 */
17public class LocalSearchGenericBackendFactoryProvider implements IQueryBackendFactoryProvider {
18
19 @Override
20 public IQueryBackendFactory getFactory() {
21 return LocalSearchGenericBackendFactory.INSTANCE;
22 }
23
24} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchHintOptions.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchHintOptions.java
new file mode 100644
index 00000000..43462204
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchHintOptions.java
@@ -0,0 +1,70 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.localsearch.matcher.integration;
10
11import tools.refinery.viatra.runtime.localsearch.planner.cost.ICostFunction;
12import tools.refinery.viatra.runtime.localsearch.planner.cost.impl.IndexerBasedConstraintCostFunction;
13import tools.refinery.viatra.runtime.matchers.backend.ICallDelegationStrategy;
14import tools.refinery.viatra.runtime.matchers.backend.QueryHintOption;
15import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IFlattenCallPredicate;
16
17/**
18 *
19 * @author Gabor Bergmann
20 * @since 1.5
21 * @noinstantiate This class is not intended to be instantiated by clients.
22 */
23public final class LocalSearchHintOptions {
24
25 private LocalSearchHintOptions() {
26 // Private constructor for utility class
27 }
28
29 public static final QueryHintOption<Boolean> USE_BASE_INDEX =
30 hintOption("USE_BASE_INDEX", true);
31
32 // This key can be used to influence the core planner algorithm
33 public static final QueryHintOption<Integer> PLANNER_TABLE_ROW_COUNT =
34 hintOption("PLANNER_TABLE_ROW_COUNT", 4);
35 /**
36 * Cost function to be used by the planner. Must implement {@link ICostFunction}
37 * @since 1.4
38 */
39 public static final QueryHintOption<ICostFunction> PLANNER_COST_FUNCTION =
40 hintOption("PLANNER_COST_FUNCTION", new IndexerBasedConstraintCostFunction());
41 /**
42 * Predicate to decide whether to flatten specific positive pattern calls {@link IFlattenCallPredicate}
43 * @since 1.4
44 */
45 public static final QueryHintOption<IFlattenCallPredicate> FLATTEN_CALL_PREDICATE =
46 hintOption("FLATTEN_CALL_PREDICATE", new DontFlattenDisjunctive());
47 /**
48 * Strategy to decide how hints (most importantly, backend selection) propagate across pattern calls.
49 * Must implement {@link ICallDelegationStrategy}.
50 * @since 2.1
51 */
52 public static final QueryHintOption<ICallDelegationStrategy> CALL_DELEGATION_STRATEGY =
53 hintOption("CALL_DELEGATION_STRATEGY", ICallDelegationStrategy.FULL_BACKEND_ADHESION);
54
55 /**
56 * A provider of expected adornments {@link IAdornmentProvider}.
57 *
58 * The safe default is {@link AllValidAdornments};
59 * however, the generic backend variant may safely use {@link LazyPlanningAdornments} instead.
60 *
61 * @since 1.5
62 */
63 public static final QueryHintOption<IAdornmentProvider> ADORNMENT_PROVIDER =
64 hintOption("ADORNMENT_PROVIDER", new AllValidAdornments());
65
66 // internal helper for conciseness
67 private static <T, V extends T> QueryHintOption<T> hintOption(String hintKeyLocalName, V defaultValue) {
68 return new QueryHintOption<>(LocalSearchHintOptions.class, hintKeyLocalName, defaultValue);
69 }
70}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchHints.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchHints.java
new file mode 100644
index 00000000..75f338b6
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchHints.java
@@ -0,0 +1,357 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.matcher.integration;
10
11import static tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHintOptions.ADORNMENT_PROVIDER;
12import static tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHintOptions.CALL_DELEGATION_STRATEGY;
13import static tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHintOptions.FLATTEN_CALL_PREDICATE;
14import static tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHintOptions.PLANNER_COST_FUNCTION;
15import static tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHintOptions.PLANNER_TABLE_ROW_COUNT;
16import static tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHintOptions.USE_BASE_INDEX;
17import static tools.refinery.viatra.runtime.matchers.backend.CommonQueryHintOptions.normalizationTraceCollector;
18
19import java.util.HashMap;
20import java.util.Map;
21import java.util.Objects;
22
23import tools.refinery.viatra.runtime.localsearch.planner.cost.ICostFunction;
24import tools.refinery.viatra.runtime.localsearch.planner.cost.impl.IndexerBasedConstraintCostFunction;
25import tools.refinery.viatra.runtime.localsearch.planner.cost.impl.StatisticsBasedConstraintCostFunction;
26import tools.refinery.viatra.runtime.localsearch.planner.cost.impl.VariableBindingBasedCostFunction;
27import tools.refinery.viatra.runtime.matchers.backend.ICallDelegationStrategy;
28import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
29import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
30import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
31import tools.refinery.viatra.runtime.matchers.backend.QueryHintOption;
32import tools.refinery.viatra.runtime.matchers.psystem.rewriters.DefaultFlattenCallPredicate;
33import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IFlattenCallPredicate;
34import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IRewriterTraceCollector;
35import tools.refinery.viatra.runtime.matchers.psystem.rewriters.NeverFlattenCallPredicate;
36import tools.refinery.viatra.runtime.matchers.psystem.rewriters.NopTraceCollector;
37
38/**
39 * Type safe builder and extractor for Local search specific hints
40 *
41 * @author Grill Balázs
42 * @since 1.4
43 *
44 */
45public final class LocalSearchHints implements IMatcherCapability {
46
47 private Boolean useBase = null;
48
49 private Integer rowCount = null;
50
51 private ICostFunction costFunction = null;
52
53 private IFlattenCallPredicate flattenCallPredicate = null;
54
55 private ICallDelegationStrategy callDelegationStrategy = null;
56
57 private IAdornmentProvider adornmentProvider = null;
58
59 private IRewriterTraceCollector traceCollector = NopTraceCollector.INSTANCE;
60
61 private IQueryBackendFactory backendFactory = null;
62
63 private LocalSearchHints() {}
64
65 /**
66 * Return the default settings overridden by the given hints
67 */
68 public static LocalSearchHints getDefaultOverriddenBy(QueryEvaluationHint overridingHint){
69 return parse(getDefault().build(overridingHint));
70 }
71
72 /**
73 * Default settings which are considered the most safe, providing a reasonable performance for most of the cases. Assumes the availability of the base indexer.
74 */
75 public static LocalSearchHints getDefault(){
76 LocalSearchHints result = new LocalSearchHints();
77 result.useBase = USE_BASE_INDEX.getDefaultValue();
78 result.rowCount = PLANNER_TABLE_ROW_COUNT.getDefaultValue();
79 result.costFunction = PLANNER_COST_FUNCTION.getDefaultValue();
80 result.flattenCallPredicate = FLATTEN_CALL_PREDICATE.getDefaultValue();
81 result.callDelegationStrategy = CALL_DELEGATION_STRATEGY.getDefaultValue();
82 result.adornmentProvider = ADORNMENT_PROVIDER.getDefaultValue();
83 result.backendFactory = LocalSearchEMFBackendFactory.INSTANCE;
84 return result;
85 }
86
87 /**
88 * With this setting, the patterns are flattened before planning. This may cause performance gain in some cases compared to the {@link #getDefault()} settings,
89 * However this should be used with care for patterns containing calls with several bodies.
90 */
91 public static LocalSearchHints getDefaultFlatten(){
92 LocalSearchHints result = new LocalSearchHints();
93 result.useBase = true;
94 result.rowCount = 4;
95 result.costFunction = new IndexerBasedConstraintCostFunction();
96 result.flattenCallPredicate = new DefaultFlattenCallPredicate();
97 result.callDelegationStrategy = CALL_DELEGATION_STRATEGY.getDefaultValue();
98 result.adornmentProvider = ADORNMENT_PROVIDER.getDefaultValue();
99 result.backendFactory = LocalSearchEMFBackendFactory.INSTANCE;
100 return result;
101 }
102
103 /**
104 * Settings to be used when the base index is not available.
105 */
106 public static LocalSearchHints getDefaultNoBase(){
107 LocalSearchHints result = new LocalSearchHints();
108 result.useBase = false;
109 result.rowCount = 4;
110 result.costFunction = new VariableBindingBasedCostFunction();
111 result.flattenCallPredicate = new NeverFlattenCallPredicate();
112 result.callDelegationStrategy = ICallDelegationStrategy.FULL_BACKEND_ADHESION;
113 result.adornmentProvider = ADORNMENT_PROVIDER.getDefaultValue();
114 result.backendFactory = LocalSearchEMFBackendFactory.INSTANCE;
115 return result;
116 }
117
118 /**
119 * Initializes the generic (not EMF specific) search backend with the default settings
120 * @since 1.7
121 */
122 public static LocalSearchHints getDefaultGeneric(){
123 LocalSearchHints result = new LocalSearchHints();
124 result.useBase = true; // Should be unused; but a false value might cause surprises as an engine-default hint
125 result.rowCount = 4;
126 result.costFunction = new IndexerBasedConstraintCostFunction(StatisticsBasedConstraintCostFunction.INVERSE_NAVIGATION_PENALTY_GENERIC);
127 result.flattenCallPredicate = FLATTEN_CALL_PREDICATE.getDefaultValue();
128 result.callDelegationStrategy = ICallDelegationStrategy.FULL_BACKEND_ADHESION;
129 result.adornmentProvider = new LazyPlanningAdornments();
130 result.backendFactory = LocalSearchGenericBackendFactory.INSTANCE;
131 return result;
132 }
133
134 /**
135 * Initializes the default search backend with hybrid-enabled settings
136 * @since 2.1
137 */
138 public static LocalSearchHints getDefaultHybrid(){
139 LocalSearchHints result = getDefault();
140 result.callDelegationStrategy = ICallDelegationStrategy.PARTIAL_BACKEND_ADHESION;
141 result.flattenCallPredicate = new IFlattenCallPredicate.And(
142 new DontFlattenIncrementalPredicate(), new DontFlattenDisjunctive());
143 return result;
144 }
145
146 /**
147 * Initializes the generic (not EMF specific) search backend with hybrid-enabled settings
148 * @since 2.1
149 */
150 public static LocalSearchHints getDefaultGenericHybrid(){
151 LocalSearchHints result = getDefaultGeneric();
152 result.callDelegationStrategy = ICallDelegationStrategy.PARTIAL_BACKEND_ADHESION;
153 result.flattenCallPredicate = new IFlattenCallPredicate.And(
154 new DontFlattenIncrementalPredicate(), new DontFlattenDisjunctive());
155 return result;
156 }
157
158 public static LocalSearchHints parse(QueryEvaluationHint hint){
159 LocalSearchHints result = new LocalSearchHints();
160
161 result.useBase = USE_BASE_INDEX.getValueOrNull(hint);
162 result.rowCount = PLANNER_TABLE_ROW_COUNT.getValueOrNull(hint);
163 result.flattenCallPredicate = FLATTEN_CALL_PREDICATE.getValueOrNull(hint);
164 result.callDelegationStrategy = CALL_DELEGATION_STRATEGY.getValueOrNull(hint);
165 result.costFunction = PLANNER_COST_FUNCTION.getValueOrNull(hint);
166 result.adornmentProvider = ADORNMENT_PROVIDER.getValueOrNull(hint);
167 result.traceCollector = normalizationTraceCollector.getValueOrDefault(hint);
168
169 return result;
170 }
171
172
173 private Map<QueryHintOption<?>, Object> calculateHintMap() {
174 Map<QueryHintOption<?>, Object> map = new HashMap<>();
175 if (useBase != null){
176 USE_BASE_INDEX.insertOverridingValue(map, useBase);
177 }
178 if (rowCount != null){
179 PLANNER_TABLE_ROW_COUNT.insertOverridingValue(map, rowCount);
180 }
181 if (costFunction != null){
182 PLANNER_COST_FUNCTION.insertOverridingValue(map, costFunction);
183 }
184 if (flattenCallPredicate != null){
185 FLATTEN_CALL_PREDICATE.insertOverridingValue(map, flattenCallPredicate);
186 }
187 if (callDelegationStrategy != null){
188 CALL_DELEGATION_STRATEGY.insertOverridingValue(map, callDelegationStrategy);
189 }
190 if (adornmentProvider != null){
191 ADORNMENT_PROVIDER.insertOverridingValue(map, adornmentProvider);
192 }
193 if (traceCollector != null){
194 normalizationTraceCollector.insertOverridingValue(map, traceCollector);
195 }
196 return map;
197 }
198
199 public QueryEvaluationHint build(){
200 Map<QueryHintOption<?>, Object> map = calculateHintMap();
201 return new QueryEvaluationHint(map, backendFactory);
202 }
203
204 /**
205 * @since 1.7
206 */
207 public QueryEvaluationHint build(QueryEvaluationHint overridingHint) {
208 if (overridingHint == null)
209 return build();
210
211 IQueryBackendFactory factory = (overridingHint.getQueryBackendFactory() == null)
212 ? this.backendFactory
213 : overridingHint.getQueryBackendFactory();
214
215 Map<QueryHintOption<?>, Object> hints = calculateHintMap();
216 if (overridingHint.getBackendHintSettings() != null) {
217 hints.putAll(overridingHint.getBackendHintSettings());
218 }
219
220 return new QueryEvaluationHint(hints, factory);
221 }
222
223 public boolean isUseBase() {
224 return useBase;
225 }
226
227 public ICostFunction getCostFunction() {
228 return costFunction;
229 }
230
231 public IFlattenCallPredicate getFlattenCallPredicate() {
232 return flattenCallPredicate;
233 }
234
235 /**
236 * @since 2.1
237 */
238 public ICallDelegationStrategy getCallDelegationStrategy() {
239 return callDelegationStrategy;
240 }
241
242 public Integer getRowCount() {
243 return rowCount;
244 }
245
246 /**
247 * @since 1.5
248 */
249 public IAdornmentProvider getAdornmentProvider() {
250 return adornmentProvider;
251 }
252
253 /**
254 * @since 1.6
255 */
256 public IRewriterTraceCollector getTraceCollector() {
257 return traceCollector == null ? normalizationTraceCollector.getDefaultValue() : traceCollector;
258 }
259
260 public LocalSearchHints setUseBase(boolean useBase) {
261 this.useBase = useBase;
262 return this;
263 }
264
265 public LocalSearchHints setRowCount(int rowCount) {
266 this.rowCount = rowCount;
267 return this;
268 }
269
270 public LocalSearchHints setCostFunction(ICostFunction costFunction) {
271 this.costFunction = costFunction;
272 return this;
273 }
274
275 public LocalSearchHints setFlattenCallPredicate(IFlattenCallPredicate flattenCallPredicate) {
276 this.flattenCallPredicate = flattenCallPredicate;
277 return this;
278 }
279
280
281 /**
282 * @since 2.1
283 */
284 public LocalSearchHints setCallDelegationStrategy(ICallDelegationStrategy callDelegationStrategy) {
285 this.callDelegationStrategy = callDelegationStrategy;
286 return this;
287 }
288
289 /**
290 * @since 1.6
291 */
292 public LocalSearchHints setTraceCollector(IRewriterTraceCollector traceCollector) {
293 this.traceCollector = traceCollector;
294 return this;
295 }
296
297 /**
298 * @since 1.5
299 */
300 public LocalSearchHints setAdornmentProvider(IAdornmentProvider adornmentProvider) {
301 this.adornmentProvider = adornmentProvider;
302 return this;
303 }
304
305 public static LocalSearchHints customizeUseBase(boolean useBase){
306 return new LocalSearchHints().setUseBase(useBase);
307 }
308
309 public static LocalSearchHints customizeRowCount(int rowCount){
310 return new LocalSearchHints().setRowCount(rowCount);
311 }
312
313 public static LocalSearchHints customizeCostFunction(ICostFunction costFunction){
314 return new LocalSearchHints().setCostFunction(costFunction);
315 }
316
317 public static LocalSearchHints customizeFlattenCallPredicate(IFlattenCallPredicate predicate){
318 return new LocalSearchHints().setFlattenCallPredicate(predicate);
319 }
320
321 /**
322 * @since 2.1
323 */
324 public static LocalSearchHints customizeCallDelegationStrategy(ICallDelegationStrategy strategy){
325 return new LocalSearchHints().setCallDelegationStrategy(strategy);
326 }
327
328 /**
329 * @since 1.5
330 */
331 public static LocalSearchHints customizeAdornmentProvider(IAdornmentProvider adornmentProvider){
332 return new LocalSearchHints().setAdornmentProvider(adornmentProvider);
333 }
334
335 /**
336 * @since 1.6
337 */
338 public static LocalSearchHints customizeTraceCollector(IRewriterTraceCollector traceCollector){
339 return new LocalSearchHints().setTraceCollector(traceCollector);
340 }
341
342 @Override
343 public boolean canBeSubstitute(IMatcherCapability capability) {
344 if (capability instanceof LocalSearchHints){
345 LocalSearchHints other = (LocalSearchHints)capability;
346 /*
347 * We allow substitution of matchers if their functionally relevant settings are equal.
348 */
349 return Objects.equals(other.useBase, useBase);
350 }
351 /*
352 * For any other cases (e.g. for Rete), we cannot assume
353 * that matchers created by LS are functionally equivalent.
354 */
355 return false;
356 }
357}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchResultProvider.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchResultProvider.java
new file mode 100644
index 00000000..a3017b18
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/integration/LocalSearchResultProvider.java
@@ -0,0 +1,56 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, 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.localsearch.matcher.integration;
10
11import tools.refinery.viatra.runtime.localsearch.plan.IPlanProvider;
12import tools.refinery.viatra.runtime.localsearch.planner.compiler.EMFOperationCompiler;
13import tools.refinery.viatra.runtime.localsearch.planner.compiler.IOperationCompiler;
14import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
15import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
16import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
17import tools.refinery.viatra.runtime.matchers.context.IndexingService;
18import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
19
20/**
21 * @author Marton Bur, Zoltan Ujhelyi
22 *
23 */
24public class LocalSearchResultProvider extends AbstractLocalSearchResultProvider {
25
26 /**
27 * @throws ViatraQueryRuntimeException
28 * @since 1.5
29 */
30 public LocalSearchResultProvider(LocalSearchBackend backend, IQueryBackendContext context, PQuery query,
31 IPlanProvider planProvider) {
32 this(backend, context, query, planProvider, null);
33 }
34
35 /**
36 * @throws ViatraQueryRuntimeException
37 * @since 1.5
38 */
39 public LocalSearchResultProvider(LocalSearchBackend backend, IQueryBackendContext context, PQuery query,
40 IPlanProvider planProvider, QueryEvaluationHint userHints) {
41 super(backend, context, query, planProvider, userHints);
42 }
43
44 @Override
45 protected void indexInitializationBeforePlanning() {
46 super.indexInitializationBeforePlanning();
47
48 indexReferredTypesOfQuery(query, IndexingService.STATISTICS);
49 }
50
51 @Override
52 protected IOperationCompiler getOperationCompiler(IQueryBackendContext backendContext,
53 LocalSearchHints configuration) {
54 return new EMFOperationCompiler(runtimeContext, configuration.isUseBase());
55 }
56}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/CheckOperationExecutor.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/CheckOperationExecutor.java
new file mode 100644
index 00000000..295ac110
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/CheckOperationExecutor.java
@@ -0,0 +1,50 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations;
10
11import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
12import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
13import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation.ISearchOperationExecutor;
14
15/**
16 * Abstract base class for search operations that check only the already set variables.
17 *
18 * @noextend This class is not intended to be subclassed by clients.
19 * @since 2.0
20 */
21public abstract class CheckOperationExecutor implements ISearchOperationExecutor {
22
23 /**
24 * The executed field ensures that the second call of the check always returns false, resulting in a quick
25 * backtracking.
26 */
27 private boolean executed;
28
29 @Override
30 public void onInitialize(MatchingFrame frame, ISearchContext context) {
31 executed = false;
32 }
33
34 @Override
35 public void onBacktrack(MatchingFrame frame, ISearchContext context) {
36 }
37
38 @Override
39 public boolean execute(MatchingFrame frame, ISearchContext context) {
40 executed = executed ? false : check(frame, context);
41 return executed;
42 }
43
44 /**
45 * Executes the checking operation
46 * @since 1.7
47 */
48 protected abstract boolean check(MatchingFrame frame, ISearchContext context) ;
49
50}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/ExtendOperationExecutor.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/ExtendOperationExecutor.java
new file mode 100644
index 00000000..01179118
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/ExtendOperationExecutor.java
@@ -0,0 +1,72 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.operations;
10
11import java.util.Iterator;
12
13import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
14import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
15import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation.ISearchOperationExecutor;
16
17/**
18 * An operation that can be used to enumerate all possible values for a single position based on a constraint
19 * @author Zoltan Ujhelyi, Akos Horvath
20 * @noextend This class is not intended to be subclassed by clients.
21 * @since 2.0
22 */
23public abstract class ExtendOperationExecutor<T> implements ISearchOperationExecutor {
24
25 private Iterator<? extends T> it;
26
27 /**
28 * Returns an iterator with the possible options from the current state
29 * @since 2.0
30 */
31 protected abstract Iterator<? extends T> getIterator(MatchingFrame frame, ISearchContext context);
32 /**
33 * Updates the frame with the next element of the iterator. Called during {@link #execute(MatchingFrame, ISearchContext)}.
34 *
35 * @return true if the update is successful or false otherwise; in case of false is returned, the next element should be taken from the iterator.
36 * @since 2.0
37 */
38 protected abstract boolean fillInValue(T newValue, MatchingFrame frame, ISearchContext context);
39
40 /**
41 * Restores the frame to the state before {@link #fillInValue(Object, MatchingFrame, ISearchContext)}. Called during
42 * {@link #onBacktrack(MatchingFrame, ISearchContext)}.
43 *
44 * @since 2.0
45 */
46 protected abstract void cleanup(MatchingFrame frame, ISearchContext context);
47
48 @Override
49 public void onInitialize(MatchingFrame frame, ISearchContext context) {
50 it = getIterator(frame, context);
51 }
52
53 @Override
54 public void onBacktrack(MatchingFrame frame, ISearchContext context) {
55 it = null;
56
57 }
58
59 @Override
60 public boolean execute(MatchingFrame frame, ISearchContext context) {
61 if (it.hasNext()){
62 T newValue = it.next();
63 while(!fillInValue(newValue, frame, context) && it.hasNext()){
64 newValue = it.next();
65 }
66 return true;
67 } else {
68 return false;
69 }
70 }
71
72}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/IIteratingSearchOperation.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/IIteratingSearchOperation.java
new file mode 100644
index 00000000..6fee0097
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/IIteratingSearchOperation.java
@@ -0,0 +1,27 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.localsearch.operations;
10
11import tools.refinery.viatra.runtime.matchers.context.IInputKey;
12
13/**
14 * Denotes a {@link ISearchOperation} which involves iterating over an instances of an {@link IInputKey}
15 *
16 * @author Grill Balázs
17 * @since 1.4
18 *
19 */
20public interface IIteratingSearchOperation extends ISearchOperation{
21
22 /**
23 * Get the {@link IInputKey} which instances this operation iterates upon.
24 */
25 public IInputKey getIteratedInputKey();
26
27}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/IPatternMatcherOperation.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/IPatternMatcherOperation.java
new file mode 100644
index 00000000..d8106329
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/IPatternMatcherOperation.java
@@ -0,0 +1,26 @@
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.localsearch.operations;
10
11import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
12
13/**
14 * Marker interface for pattern matcher call operations, such as positive and negative pattern calls or match aggregators.
15 *
16 * @author Zoltan Ujhelyi
17 * @since 1.7
18 */
19public interface IPatternMatcherOperation {
20
21 /**
22 * Returns the precomputed call information associated with the current operation
23 * @since 2.0
24 */
25 CallInformation getCallInformation();
26}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/ISearchOperation.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/ISearchOperation.java
new file mode 100644
index 00000000..eb6243ed
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/ISearchOperation.java
@@ -0,0 +1,88 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.operations;
10
11import java.util.List;
12import java.util.function.Function;
13
14import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
15import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
16import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
17
18/**
19 * Represents a search operation executable by the LS engine. It is expected that an operation can be shared among
20 * multiple LS matchers, but the created executors are not.
21 *
22 * @author Zoltan Ujhelyi
23 *
24 */
25public interface ISearchOperation {
26
27 /**
28 * Initializes a new operation executor for the given operation. Repeated calls must return different executor
29 * instances.
30 *
31 * @since 2.0
32 */
33 public ISearchOperationExecutor createExecutor();
34
35 /**
36 *
37 * @since 2.0
38 *
39 */
40 public interface ISearchOperationExecutor {
41
42 /**
43 * Returns the stateless operation this executor was initialized from
44 */
45 ISearchOperation getOperation();
46
47 /**
48 * During the execution of the corresponding plan, the onInitialize callback is evaluated before the execution of
49 * the operation may begin. Operations may use this method to initialize its internal data structures.
50 *
51 * @throws ViatraQueryRuntimeException
52 */
53 void onInitialize(MatchingFrame frame, ISearchContext context);
54
55 /**
56 * After the execution of the operation failed and {@link #execute(MatchingFrame, ISearchContext)} returns false, the onBacktrack
57 * callback is evaluated. Operations may use this method to clean up any temporary structures, and make the
58 * operation ready for a new execution.
59 *
60 * @throws ViatraQueryRuntimeException
61 */
62 void onBacktrack(MatchingFrame frame, ISearchContext context);
63
64 /**
65 *
66 * @param frame
67 * @param context
68 * @return true if successful, or false if backtracking needed
69 * @throws ViatraQueryRuntimeException
70 */
71 boolean execute(MatchingFrame frame, ISearchContext context);
72 }
73 /**
74 *
75 * @return the ordered list of the variable numbers that are affected by the search operation
76 */
77 List<Integer> getVariablePositions();
78
79 /**
80 * Creates a string representation of the search operation by replacing the variable numbers according to the
81 * parameter function. It is expected that the provided function does return a non-null value for each variable
82 * index that is returned by {@link #getVariablePositions()}; otherwise a {@link NullPointerException} will be
83 * thrown during the calculation of the string.
84 *
85 * @since 2.0
86 */
87 String toString(Function<Integer, String> variableMapping);
88}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/MatchingFrameValueProvider.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/MatchingFrameValueProvider.java
new file mode 100644
index 00000000..38f62129
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/MatchingFrameValueProvider.java
@@ -0,0 +1,41 @@
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.localsearch.operations;
10
11import java.util.Map;
12
13import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
14import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider;
15import tools.refinery.viatra.runtime.matchers.util.Preconditions;
16
17/**
18 *
19 *
20 * @author Zoltan Ujhelyi
21 *
22 */
23public class MatchingFrameValueProvider implements IValueProvider {
24
25 final Map<String, Integer> nameMap;
26 final MatchingFrame frame;
27
28 public MatchingFrameValueProvider(MatchingFrame frame, Map<String, Integer> nameMap) {
29 super();
30 this.frame = frame;
31 this.nameMap = nameMap;
32 }
33
34 @Override
35 public Object getValue(String variableName) {
36 Integer index = nameMap.get(variableName);
37 Preconditions.checkArgument(index != null, "Unknown parameter variable name");
38 return frame.get(index);
39 }
40
41}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/AggregatorCheck.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/AggregatorCheck.java
new file mode 100644
index 00000000..4d631635
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/AggregatorCheck.java
@@ -0,0 +1,116 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.operations.check;
10
11import java.util.Collections;
12import java.util.List;
13import java.util.Objects;
14import java.util.function.Function;
15import java.util.stream.Stream;
16
17import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
18import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
19import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
20import tools.refinery.viatra.runtime.localsearch.operations.IPatternMatcherOperation;
21import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
22import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
23import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
24import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
25import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint;
26import tools.refinery.viatra.runtime.matchers.tuple.VolatileModifiableMaskedTuple;
27
28/**
29 * Calculates the aggregated value of a column based on the given {@link AggregatorConstraint}
30 *
31 * @author Balázs Grill
32 * @since 1.4
33 * @noextend This class is not intended to be subclassed by clients.
34 */
35public class AggregatorCheck implements ISearchOperation, IPatternMatcherOperation {
36
37 private class Executor extends CheckOperationExecutor {
38
39 private final VolatileModifiableMaskedTuple maskedTuple;
40 private IQueryResultProvider matcher;
41
42 public Executor() {
43 super();
44 this.maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask());
45 }
46
47 @Override
48 public void onInitialize(MatchingFrame frame, ISearchContext context) {
49 super.onInitialize(frame, context);
50 maskedTuple.updateTuple(frame);
51 matcher = context.getMatcher(information.getCallWithAdornment());
52 }
53
54 @Override
55 protected boolean check(MatchingFrame frame, ISearchContext context) {
56 IMultisetAggregationOperator<?, ?, ?> operator = aggregator.getAggregator().getOperator();
57 Object result = aggregate(operator, aggregator.getAggregatedColumn(), frame);
58 return result == null ? false : Objects.equals(frame.getValue(position), result);
59 }
60
61 @SuppressWarnings("unchecked")
62 private <Domain, Accumulator, AggregateResult> AggregateResult aggregate(
63 IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, int aggregatedColumn,
64 MatchingFrame initialFrame) {
65 maskedTuple.updateTuple(initialFrame);
66 final Stream<Domain> valueStream = matcher.getAllMatches(information.getParameterMask(), maskedTuple)
67 .map(match -> (Domain) match.get(aggregatedColumn));
68 return operator.aggregateStream(valueStream);
69 }
70
71 @Override
72 public ISearchOperation getOperation() {
73 return AggregatorCheck.this;
74 }
75 }
76
77 private final int position;
78 private final AggregatorConstraint aggregator;
79 private final CallInformation information;
80
81 /**
82 * @since 1.7
83 */
84 public AggregatorCheck(CallInformation information, AggregatorConstraint aggregator, int position) {
85 super();
86 this.information = information;
87 this.position = position;
88 this.aggregator = aggregator;
89 }
90
91 @Override
92 public ISearchOperationExecutor createExecutor() {
93 return new Executor();
94 }
95
96 @Override
97 public List<Integer> getVariablePositions() {
98 return Collections.singletonList(position);
99 }
100
101 @Override
102 public String toString() {
103 return toString(Object::toString);
104 }
105
106 @Override
107 public String toString(Function<Integer, String> variableMapping) {
108 return "check "+variableMapping.apply(position)+" = " + aggregator.getAggregator().getOperator().getName() + " find " + information.toString(variableMapping);
109 }
110
111 @Override
112 public CallInformation getCallInformation() {
113 return information;
114 }
115
116}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/BinaryTransitiveClosureCheck.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/BinaryTransitiveClosureCheck.java
new file mode 100644
index 00000000..8f818542
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/BinaryTransitiveClosureCheck.java
@@ -0,0 +1,135 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations.check;
10
11import java.util.Arrays;
12import java.util.HashSet;
13import java.util.LinkedList;
14import java.util.List;
15import java.util.Objects;
16import java.util.Queue;
17import java.util.Set;
18import java.util.function.Function;
19
20import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
21import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
22import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
23import tools.refinery.viatra.runtime.localsearch.operations.IPatternMatcherOperation;
24import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
25import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
26import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
27import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
28
29/**
30 * Checking for a transitive closure expressed as a local search pattern matcher. The matched pattern must have two
31 * parameters of the same model type.
32 *
33 * @author Zoltan Ujhelyi
34 * @noextend This class is not intended to be subclassed by clients.
35 * @noinstantiate This class is not intended to be instantiated by clients.
36 *
37 */
38public class BinaryTransitiveClosureCheck implements ISearchOperation, IPatternMatcherOperation {
39
40 private class Executor extends CheckOperationExecutor {
41
42 @Override
43 public void onInitialize(MatchingFrame frame, ISearchContext context) {
44 super.onInitialize(frame, context);
45 matcher = context.getMatcher(information.getCallWithAdornment());
46 // Note: second parameter is NOT bound during execution, but the first is
47 }
48
49 @Override
50 protected boolean check(MatchingFrame frame, ISearchContext context) {
51 if (checkReflexive(frame)) {
52 return true;
53 }
54
55 Object targetValue = frame.get(targetPosition);
56 Queue<Object> sourcesToEvaluate = new LinkedList<>();
57 sourcesToEvaluate.add(frame.get(sourcePosition));
58 Set<Object> sourceEvaluated = new HashSet<>();
59 final Object[] mappedFrame = new Object[] {null, null};
60 while (!sourcesToEvaluate.isEmpty()) {
61 Object currentValue = sourcesToEvaluate.poll();
62 sourceEvaluated.add(currentValue);
63 mappedFrame[0] = currentValue;
64 for (Tuple match : (Iterable<Tuple>) () -> matcher.getAllMatches(mappedFrame).iterator()) {
65 Object foundTarget = match.get(1);
66 if (targetValue.equals(foundTarget)) {
67 return true;
68 } else if (!sourceEvaluated.contains(foundTarget)) {
69 sourcesToEvaluate.add(foundTarget);
70 }
71 }
72 }
73 return false;
74 }
75
76 protected boolean checkReflexive(MatchingFrame frame) {
77 return reflexive && Objects.equals(frame.get(sourcePosition), frame.get(targetPosition));
78 }
79
80 @Override
81 public ISearchOperation getOperation() {
82 return BinaryTransitiveClosureCheck.this;
83 }
84 }
85
86 private final CallInformation information;
87 private IQueryResultProvider matcher;
88 private final int sourcePosition;
89 private final int targetPosition;
90 private final boolean reflexive;
91
92 /**
93 * The source position will be matched in the called pattern to the first parameter; while target to the second.
94 * </p>
95 * <strong>NOTE</strong>: the reflexive check call does not include the parameter type checks; appropriate type checks should be
96 * added as necessary by the operation compiler.
97 *
98 * @since 2.0
99 */
100 public BinaryTransitiveClosureCheck(CallInformation information, int sourcePosition, int targetPosition, boolean reflexive) {
101 super();
102 this.sourcePosition = sourcePosition;
103 this.targetPosition = targetPosition;
104 this.information = information;
105 this.reflexive = reflexive;
106 }
107
108 @Override
109 public ISearchOperationExecutor createExecutor() {
110 return new Executor();
111 }
112
113 @Override
114 public String toString() {
115 return toString(Object::toString);
116 }
117
118 @Override
119 public String toString(Function<Integer, String> variableMapping) {
120 String c = information.toString(variableMapping);
121 int p = c.indexOf('(');
122 String modifier = reflexive ? "*" : "+";
123 return "check find "+c.substring(0, p)+ modifier +c.substring(p);
124 }
125
126 @Override
127 public List<Integer> getVariablePositions() {
128 return Arrays.asList(sourcePosition, targetPosition);
129 }
130
131 @Override
132 public CallInformation getCallInformation() {
133 return information;
134 }
135}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/CheckConstant.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/CheckConstant.java
new file mode 100644
index 00000000..4ce48af0
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/CheckConstant.java
@@ -0,0 +1,70 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, 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.localsearch.operations.check;
10
11import java.util.Collections;
12import java.util.List;
13import java.util.function.Function;
14
15import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
16import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
17import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
18import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
19
20/**
21 * This operation handles constants in search plans by checking if a variable is bound to a certain constant value. Such
22 * operations should be executed as early as possible during plan execution.
23 *
24 * @author Marton Bur
25 * @noextend This class is not intended to be subclassed by clients.
26 */
27public class CheckConstant implements ISearchOperation {
28
29 private class Executor extends CheckOperationExecutor {
30
31 @Override
32 protected boolean check(MatchingFrame frame, ISearchContext context) {
33 return frame.get(position).equals(value);
34 }
35
36 @Override
37 public ISearchOperation getOperation() {
38 return CheckConstant.this;
39 }
40 }
41
42 private int position;
43 private Object value;
44
45 public CheckConstant(int position, Object value) {
46 this.position = position;
47 this.value = value;
48 }
49
50 @Override
51 public ISearchOperationExecutor createExecutor() {
52 return new Executor();
53 }
54
55 @Override
56 public List<Integer> getVariablePositions() {
57 return Collections.singletonList(position);
58 }
59
60 @Override
61 public String toString() {
62 return toString(Object::toString);
63 }
64
65 @Override
66 public String toString(Function<Integer, String> variableMapping) {
67 return "check constant "+variableMapping.apply(position)+"='"+value+"'";
68 }
69
70}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/CheckPositivePatternCall.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/CheckPositivePatternCall.java
new file mode 100644
index 00000000..5f9b688a
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/CheckPositivePatternCall.java
@@ -0,0 +1,96 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.localsearch.operations.check;
10
11import java.util.List;
12import java.util.function.Function;
13
14import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
15import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
16import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
17import tools.refinery.viatra.runtime.localsearch.operations.IPatternMatcherOperation;
18import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
19import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
20import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
21import tools.refinery.viatra.runtime.matchers.tuple.VolatileModifiableMaskedTuple;
22
23/**
24 * @author Grill Balázs
25 * @since 1.4
26 * @noextend This class is not intended to be subclassed by clients.
27 */
28public class CheckPositivePatternCall implements ISearchOperation, IPatternMatcherOperation {
29
30 private class Executor extends CheckOperationExecutor {
31
32 private final VolatileModifiableMaskedTuple maskedTuple;
33 private IQueryResultProvider matcher;
34
35 public Executor() {
36 super();
37 this.maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask());
38 }
39
40 @Override
41 public void onInitialize(MatchingFrame frame, ISearchContext context) {
42 super.onInitialize(frame, context);
43 maskedTuple.updateTuple(frame);
44 matcher = context.getMatcher(information.getCallWithAdornment());
45 }
46
47 /**
48 * @since 1.5
49 */
50 protected boolean check(MatchingFrame frame, ISearchContext context) {
51 return matcher.hasMatch(information.getParameterMask(), maskedTuple);
52 }
53
54 @Override
55 public ISearchOperation getOperation() {
56 return CheckPositivePatternCall.this;
57 }
58 }
59
60 private final CallInformation information;
61
62
63 /**
64 * @since 1.7
65 */
66 public CheckPositivePatternCall(CallInformation information) {
67 super();
68 this.information = information;
69
70 }
71
72 @Override
73 public ISearchOperationExecutor createExecutor() {
74 return new Executor();
75 }
76
77 @Override
78 public List<Integer> getVariablePositions() {
79 return information.getVariablePositions();
80 }
81
82 @Override
83 public String toString() {
84 return toString(Object::toString);
85 }
86
87 @Override
88 public String toString(Function<Integer, String> variableMapping) {
89 return "check find "+information.toString(variableMapping);
90 }
91
92 @Override
93 public CallInformation getCallInformation() {
94 return information;
95 }
96}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/ContainmentCheck.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/ContainmentCheck.java
new file mode 100644
index 00000000..9dfb16f5
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/ContainmentCheck.java
@@ -0,0 +1,85 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations.check;
10
11import java.util.Arrays;
12import java.util.List;
13import java.util.function.Function;
14
15import org.eclipse.emf.ecore.EObject;
16import org.eclipse.emf.ecore.EStructuralFeature;
17import org.eclipse.emf.ecore.util.EcoreUtil;
18import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
19import tools.refinery.viatra.runtime.localsearch.exceptions.LocalSearchException;
20import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
21import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
22import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
23
24/**
25 * A simple operation that checks whether a {@link EStructuralFeature} connects two selected variables.
26 * @noextend This class is not intended to be subclassed by clients.
27 */
28public class ContainmentCheck implements ISearchOperation {
29
30 private class Executor extends CheckOperationExecutor {
31
32 @Override
33 protected boolean check(MatchingFrame frame, ISearchContext context) {
34 try {
35 EObject child = (EObject) frame.getValue(childPosition);
36 EObject container = (EObject)frame.getValue(containerPosition);
37
38 if (transitive) {
39 return EcoreUtil.isAncestor(container, child);
40 } else {
41 return child.eContainer().equals(container);
42 }
43 } catch (ClassCastException e) {
44 throw new LocalSearchException(LocalSearchException.TYPE_ERROR, e);
45 }
46 }
47
48 @Override
49 public ISearchOperation getOperation() {
50 return ContainmentCheck.this;
51 }
52 }
53
54 int childPosition;
55 int containerPosition;
56 private boolean transitive;
57
58 public ContainmentCheck(int childPosition, int containerPosition, boolean transitive) {
59 super();
60 this.childPosition = childPosition;
61 this.containerPosition = containerPosition;
62 this.transitive = transitive;
63 }
64
65 @Override
66 public ISearchOperationExecutor createExecutor() {
67 return new Executor();
68 }
69
70 @Override
71 public String toString() {
72 return toString(Object::toString);
73 }
74
75 @Override
76 public String toString(Function<Integer, String> variableMapping) {
77 return "check containment +"+variableMapping.apply(containerPosition)+" <>--> +"+childPosition+(transitive ? " transitively" : " directly");
78 }
79
80 @Override
81 public List<Integer> getVariablePositions() {
82 return Arrays.asList(childPosition, containerPosition);
83 }
84
85}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/CountCheck.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/CountCheck.java
new file mode 100644
index 00000000..d74c1c6f
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/CountCheck.java
@@ -0,0 +1,92 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations.check;
10
11import java.util.Collections;
12import java.util.List;
13import java.util.function.Function;
14
15import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
16import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
17import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
18import tools.refinery.viatra.runtime.localsearch.operations.IPatternMatcherOperation;
19import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
20import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
21import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
22import tools.refinery.viatra.runtime.matchers.tuple.VolatileModifiableMaskedTuple;
23
24/**
25 * Calculates the count of matches for a called matcher
26 *
27 * @author Zoltan Ujhelyi
28 * @noextend This class is not intended to be subclassed by clients.
29 */
30public class CountCheck implements ISearchOperation, IPatternMatcherOperation {
31
32 private class Executor extends CheckOperationExecutor {
33
34 private final VolatileModifiableMaskedTuple maskedTuple;
35 private IQueryResultProvider matcher;
36
37 public Executor() {
38 maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask());
39 }
40
41 @Override
42 public void onInitialize(MatchingFrame frame, ISearchContext context) {
43 super.onInitialize(frame, context);
44 maskedTuple.updateTuple(frame);
45 matcher = context.getMatcher(information.getCallWithAdornment());
46 }
47
48 @Override
49 protected boolean check(MatchingFrame frame, ISearchContext context) {
50 int count = matcher.countMatches(information.getParameterMask(), maskedTuple);
51 return ((Integer)frame.getValue(position)) == count;
52 }
53
54 @Override
55 public ISearchOperation getOperation() {
56 return CountCheck.this;
57 }
58 }
59
60 private final int position;
61 private final CallInformation information;
62
63 /**
64 * @since 1.7
65 */
66 public CountCheck(CallInformation information, int position) {
67 super();
68 this.information = information;
69 this.position = position;
70 }
71
72
73 @Override
74 public ISearchOperationExecutor createExecutor() {
75 return new Executor();
76 }
77
78 @Override
79 public List<Integer> getVariablePositions() {
80 return Collections.singletonList(position);
81 }
82
83 @Override
84 public String toString(Function<Integer, String> variableMapping) {
85 return "check "+variableMapping.apply(position)+" = count find "+ information.toString(variableMapping);
86 }
87
88 @Override
89 public CallInformation getCallInformation() {
90 return information;
91 }
92}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/ExpressionCheck.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/ExpressionCheck.java
new file mode 100644
index 00000000..fc9efdca
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/ExpressionCheck.java
@@ -0,0 +1,78 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations.check;
10
11import java.util.ArrayList;
12import java.util.List;
13import java.util.Map;
14import java.util.function.Function;
15
16import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
17import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
18import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
19import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
20import tools.refinery.viatra.runtime.localsearch.operations.MatchingFrameValueProvider;
21import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator;
22
23/**
24 * @author Zoltan Ujhelyi
25 * @noextend This class is not intended to be subclassed by clients.
26 */
27public class ExpressionCheck implements ISearchOperation {
28
29 private class Executor extends CheckOperationExecutor {
30
31 @Override
32 protected boolean check(MatchingFrame frame, ISearchContext context) {
33 try {
34 boolean result = (Boolean) evaluator.evaluateExpression(new MatchingFrameValueProvider(frame, nameMap));
35 return result;
36 } catch (Exception e) {
37 context.getLogger().warn("Error while evaluating expression", e);
38 return false;
39 }
40 }
41
42 @Override
43 public ISearchOperation getOperation() {
44 return ExpressionCheck.this;
45 }
46 }
47
48 IExpressionEvaluator evaluator;
49 Map<String, Integer> nameMap;
50
51 public ExpressionCheck(IExpressionEvaluator evaluator, Map<String, Integer> nameMap) {
52 super();
53 this.evaluator = evaluator;
54 this.nameMap = nameMap;
55 }
56
57 @Override
58 public ISearchOperationExecutor createExecutor() {
59 return new Executor();
60 }
61
62 @Override
63 public List<Integer> getVariablePositions() {
64 // XXX not sure if this is the correct implementation to get the affected variable indicies
65 return new ArrayList<>(nameMap.values());
66 }
67
68 @Override
69 public String toString() {
70 return toString(Object::toString);
71 }
72
73 @Override
74 public String toString(Function<Integer, String> variableMapping) {
75 return "check expression "+evaluator.getShortDescription();
76 }
77
78}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/ExpressionEvalCheck.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/ExpressionEvalCheck.java
new file mode 100644
index 00000000..5b1bca10
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/ExpressionEvalCheck.java
@@ -0,0 +1,95 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.localsearch.operations.check;
10
11import java.util.ArrayList;
12import java.util.List;
13import java.util.Map;
14import java.util.Set;
15import java.util.function.Function;
16
17import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
18import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
19import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
20import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
21import tools.refinery.viatra.runtime.localsearch.operations.MatchingFrameValueProvider;
22import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator;
23
24/**
25 * @author Grill Balázs
26 * @since 1.3
27 * @noextend This class is not intended to be subclassed by clients.
28 */
29public class ExpressionEvalCheck implements ISearchOperation {
30
31 private class Executor extends CheckOperationExecutor {
32
33 @Override
34 protected boolean check(MatchingFrame frame, ISearchContext context) {
35 try {
36 Object result = evaluator.evaluateExpression(new MatchingFrameValueProvider(frame, nameMap));
37 if (!unwind && result != null) {
38 Object currentValue = frame.get(outputPosition);
39 return result.equals(currentValue);
40 } else if (unwind && result instanceof Set<?>) {
41 Object currentValue = frame.get(outputPosition);
42 return ((Set<?>)result).contains(currentValue);
43 }
44 } catch (Exception e) {
45 context.getLogger().warn("Error while evaluating expression", e);
46 }
47 return false;
48 }
49
50 @Override
51 public ISearchOperation getOperation() {
52 return ExpressionEvalCheck.this;
53 }
54 }
55
56 private final int outputPosition;
57 private final IExpressionEvaluator evaluator;
58 private final Map<String, Integer> nameMap;
59 private final boolean unwind;
60
61 public ExpressionEvalCheck(IExpressionEvaluator evaluator, Map<String, Integer> nameMap, int position) {
62 this(evaluator, nameMap, false, position);
63 }
64
65 /**
66 * @since 2.7
67 */
68 public ExpressionEvalCheck(IExpressionEvaluator evaluator, Map<String, Integer> nameMap, boolean unwind, int position) {
69 this.evaluator = evaluator;
70 this.nameMap = nameMap;
71 this.unwind = unwind;
72 this.outputPosition = position;
73 }
74
75 @Override
76 public ISearchOperationExecutor createExecutor() {
77 return new Executor();
78 }
79
80 @Override
81 public List<Integer> getVariablePositions() {
82 // XXX not sure if this is the correct implementation to get the affected variable indicies
83 return new ArrayList<>(nameMap.values());
84 }
85
86 @Override
87 public String toString() {
88 return toString(Object::toString);
89 }
90
91 @Override
92 public String toString(Function<Integer, String> variableMapping) {
93 return "check "+variableMapping.apply(outputPosition)+" = expression "+evaluator.getShortDescription();
94 }
95}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InequalityCheck.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InequalityCheck.java
new file mode 100644
index 00000000..3f30c3c4
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InequalityCheck.java
@@ -0,0 +1,77 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations.check;
10
11import java.util.Arrays;
12import java.util.List;
13import java.util.function.Function;
14
15import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
16import tools.refinery.viatra.runtime.localsearch.exceptions.LocalSearchException;
17import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
18import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
19import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
20
21/**
22 * @author Zoltan Ujhelyi
23 * @noextend This class is not intended to be subclassed by clients.
24 */
25public class InequalityCheck implements ISearchOperation {
26
27 private class Executor extends CheckOperationExecutor {
28
29 @Override
30 protected boolean check(MatchingFrame frame, ISearchContext context) {
31 Object source = frame.getValue(sourceLocation);
32 Object target = frame.getValue(targetLocation);
33 if (source == null) {
34 throw new LocalSearchException("Source not bound.");
35 }
36 if (target == null) {
37 throw new LocalSearchException("Target not bound");
38 }
39 return !source.equals(target);
40 }
41
42 @Override
43 public ISearchOperation getOperation() {
44 return InequalityCheck.this;
45 }
46 }
47
48 int sourceLocation;
49 int targetLocation;
50
51 public InequalityCheck(int sourceLocation, int targetLocation) {
52 super();
53 this.sourceLocation = sourceLocation;
54 this.targetLocation = targetLocation;
55 }
56
57 @Override
58 public ISearchOperationExecutor createExecutor() {
59 return new Executor();
60 }
61
62 @Override
63 public String toString() {
64 return toString(Object::toString);
65 }
66
67 @Override
68 public String toString(Function<Integer, String> variableMapping) {
69 return "check "+variableMapping.apply(sourceLocation)+" != "+variableMapping.apply(targetLocation);
70 }
71
72 @Override
73 public List<Integer> getVariablePositions() {
74 return Arrays.asList(sourceLocation, targetLocation);
75 }
76
77}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InstanceOfClassCheck.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InstanceOfClassCheck.java
new file mode 100644
index 00000000..68d92040
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InstanceOfClassCheck.java
@@ -0,0 +1,75 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations.check;
10
11import java.util.Arrays;
12import java.util.List;
13import java.util.Objects;
14import java.util.function.Function;
15
16import org.eclipse.emf.ecore.EClass;
17import org.eclipse.emf.ecore.EObject;
18import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
19import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
20import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
21import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
22
23/**
24 * @author Zoltan Ujhelyi
25 * @noextend This class is not intended to be subclassed by clients.
26 */
27public class InstanceOfClassCheck implements ISearchOperation {
28
29 private class Executor extends CheckOperationExecutor {
30
31 @Override
32 protected boolean check(MatchingFrame frame, ISearchContext context) {
33 Objects.requireNonNull(frame.getValue(position), () -> String.format("Invalid plan, variable %s unbound", position));
34 if (frame.getValue(position) instanceof EObject) {
35 return clazz.isSuperTypeOf(((EObject) frame.getValue(position)).eClass());
36 }
37 return false;
38 }
39
40 @Override
41 public ISearchOperation getOperation() {
42 return InstanceOfClassCheck.this;
43 }
44 }
45
46 private int position;
47 private EClass clazz;
48
49 public InstanceOfClassCheck(int position, EClass clazz) {
50 this.position = position;
51 this.clazz = clazz;
52
53 }
54
55 @Override
56 public ISearchOperationExecutor createExecutor() {
57 return new Executor();
58 }
59
60 @Override
61 public String toString() {
62 return toString(Object::toString);
63 }
64
65 @Override
66 public String toString(Function<Integer, String> variableMapping) {
67 return "check "+clazz.getName()+"(+"+ variableMapping.apply(position)+")";
68 }
69
70 @Override
71 public List<Integer> getVariablePositions() {
72 return Arrays.asList(position);
73 }
74
75}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InstanceOfDataTypeCheck.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InstanceOfDataTypeCheck.java
new file mode 100644
index 00000000..940104a2
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InstanceOfDataTypeCheck.java
@@ -0,0 +1,71 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations.check;
10
11import java.util.Arrays;
12import java.util.List;
13import java.util.Objects;
14import java.util.function.Function;
15
16import org.eclipse.emf.ecore.EDataType;
17import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
18import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
19import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
20import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
21
22/**
23 * @author Zoltan Ujhelyi
24 * @noextend This class is not intended to be subclassed by clients.
25 */
26public class InstanceOfDataTypeCheck implements ISearchOperation {
27
28 private class Executor extends CheckOperationExecutor {
29
30 @Override
31 protected boolean check(MatchingFrame frame, ISearchContext context) {
32 Objects.requireNonNull(frame.getValue(position), () -> String.format("Invalid plan, variable %s unbound", position));
33 return dataType.isInstance(frame.getValue(position));
34 }
35
36 @Override
37 public ISearchOperation getOperation() {
38 return InstanceOfDataTypeCheck.this;
39 }
40 }
41
42 private int position;
43 private EDataType dataType;
44
45 public InstanceOfDataTypeCheck(int position, EDataType dataType) {
46 this.position = position;
47 this.dataType = dataType;
48
49 }
50
51 @Override
52 public ISearchOperationExecutor createExecutor() {
53 return new Executor();
54 }
55
56 @Override
57 public String toString() {
58 return toString(Object::toString);
59 }
60
61 @Override
62 public String toString(Function<Integer, String> variableMapping) {
63 return "check "+dataType.getName()+"(+"+variableMapping.apply(position)+")";
64 }
65
66 @Override
67 public List<Integer> getVariablePositions() {
68 return Arrays.asList(position);
69 }
70
71}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InstanceOfJavaClassCheck.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InstanceOfJavaClassCheck.java
new file mode 100644
index 00000000..1da312a0
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/InstanceOfJavaClassCheck.java
@@ -0,0 +1,71 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations.check;
10
11import java.util.Arrays;
12import java.util.List;
13import java.util.Objects;
14import java.util.function.Function;
15
16import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
17import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
18import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
19import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
20
21/**
22 * @author Zoltan Ujhelyi
23 * @since 1.4
24 * @noextend This class is not intended to be subclassed by clients.
25 */
26public class InstanceOfJavaClassCheck implements ISearchOperation {
27
28 private class Executor extends CheckOperationExecutor {
29
30 @Override
31 protected boolean check(MatchingFrame frame, ISearchContext context) {
32 Objects.requireNonNull(frame.getValue(position), () -> String.format("Invalid plan, variable %s unbound", position));
33 return clazz.isInstance(frame.getValue(position));
34 }
35
36 @Override
37 public ISearchOperation getOperation() {
38 return InstanceOfJavaClassCheck.this;
39 }
40 }
41
42 private int position;
43 private Class<?> clazz;
44
45 public InstanceOfJavaClassCheck(int position, Class<?> clazz) {
46 this.position = position;
47 this.clazz = clazz;
48
49 }
50
51 @Override
52 public ISearchOperationExecutor createExecutor() {
53 return new Executor();
54 }
55
56 @Override
57 public String toString() {
58 return toString(Object::toString);
59 }
60
61 @Override
62 public String toString(Function<Integer, String> variableMapping) {
63 return "check java "+clazz.getName()+"(+"+variableMapping.apply(position)+")";
64 }
65
66 @Override
67 public List<Integer> getVariablePositions() {
68 return Arrays.asList(position);
69 }
70
71}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/NACOperation.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/NACOperation.java
new file mode 100644
index 00000000..3759e1d1
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/NACOperation.java
@@ -0,0 +1,89 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations.check;
10
11import java.util.List;
12import java.util.function.Function;
13
14import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
15import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
16import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
17import tools.refinery.viatra.runtime.localsearch.operations.IPatternMatcherOperation;
18import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
19import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
20import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
21import tools.refinery.viatra.runtime.matchers.tuple.VolatileModifiableMaskedTuple;
22
23/**
24 * @author Zoltan Ujhelyi
25 * @noextend This class is not intended to be subclassed by clients.
26 */
27public class NACOperation implements ISearchOperation, IPatternMatcherOperation {
28
29 private class Executor extends CheckOperationExecutor {
30 private final VolatileModifiableMaskedTuple maskedTuple;
31 private IQueryResultProvider matcher;
32
33 private Executor() {
34 this.maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask());
35 }
36
37 @Override
38 public void onInitialize(MatchingFrame frame, ISearchContext context) {
39 super.onInitialize(frame, context);
40 maskedTuple.updateTuple(frame);
41 matcher = context.getMatcher(information.getCallWithAdornment());
42 }
43
44 @Override
45 protected boolean check(MatchingFrame frame, ISearchContext context) {
46 return !matcher.hasMatch(information.getParameterMask(), maskedTuple);
47 }
48
49 @Override
50 public ISearchOperation getOperation() {
51 return NACOperation.this;
52 }
53 }
54
55 private final CallInformation information;
56
57 /**
58 * @since 1.7
59 */
60 public NACOperation(CallInformation information) {
61 super();
62 this.information = information;
63 }
64
65 @Override
66 public ISearchOperationExecutor createExecutor() {
67 return new Executor();
68 }
69
70 @Override
71 public String toString() {
72 return toString(Object::toString);
73 }
74
75 @Override
76 public String toString(Function<Integer, String> variableMapping) {
77 return "check neg find "+information.toString(variableMapping);
78 }
79
80 @Override
81 public List<Integer> getVariablePositions() {
82 return information.getVariablePositions();
83 }
84
85 @Override
86 public CallInformation getCallInformation() {
87 return information;
88 }
89}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/StructuralFeatureCheck.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/StructuralFeatureCheck.java
new file mode 100644
index 00000000..a3e5bc40
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/StructuralFeatureCheck.java
@@ -0,0 +1,91 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations.check;
10
11import java.util.Arrays;
12import java.util.Collection;
13import java.util.List;
14import java.util.Objects;
15import java.util.function.Function;
16
17import org.eclipse.emf.ecore.EObject;
18import org.eclipse.emf.ecore.EStructuralFeature;
19import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
20import tools.refinery.viatra.runtime.localsearch.exceptions.LocalSearchException;
21import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
22import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
23import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
24
25/**
26 * A simple operation that checks whether a {@link EStructuralFeature} connects two selected variables.
27 * @noextend This class is not intended to be subclassed by clients.
28 */
29public class StructuralFeatureCheck implements ISearchOperation {
30
31 private class Executor extends CheckOperationExecutor {
32
33 @Override
34 protected boolean check(MatchingFrame frame, ISearchContext context) {
35 Objects.requireNonNull(frame.getValue(sourcePosition), () -> String.format("Invalid plan, variable %s unbound", sourcePosition));
36 Objects.requireNonNull(frame.getValue(targetPosition), () -> String.format("Invalid plan, variable %s unbound", targetPosition));
37 try {
38 EObject source = (EObject) frame.getValue(sourcePosition);
39 if(! feature.getEContainingClass().isSuperTypeOf(source.eClass()) ){
40 // TODO planner should ensure the proper supertype relation, see bug 500968
41 return false;
42 }
43 Object target = frame.getValue(targetPosition);
44 if (feature.isMany()) {
45 return ((Collection<?>) source.eGet(feature)).contains(target);
46 } else {
47 return target.equals(source.eGet(feature));
48 }
49 } catch (ClassCastException e) {
50 throw new LocalSearchException(LocalSearchException.TYPE_ERROR, e);
51 }
52 }
53
54 @Override
55 public ISearchOperation getOperation() {
56 return StructuralFeatureCheck.this;
57 }
58 }
59
60 int sourcePosition;
61 int targetPosition;
62 EStructuralFeature feature;
63
64 public StructuralFeatureCheck(int sourcePosition, int targetPosition, EStructuralFeature feature) {
65 super();
66 this.sourcePosition = sourcePosition;
67 this.targetPosition = targetPosition;
68 this.feature = feature;
69 }
70
71 @Override
72 public ISearchOperationExecutor createExecutor() {
73 return new Executor();
74 }
75
76 @Override
77 public String toString() {
78 return toString(Object::toString);
79 }
80
81 @Override
82 public String toString(Function<Integer, String> variableMapping) {
83 return "check "+feature.getContainerClass().getSimpleName()+"."+feature.getName()+"(+"+variableMapping.apply(sourcePosition)+", +"+variableMapping.apply(targetPosition)+")";
84 }
85
86 @Override
87 public List<Integer> getVariablePositions() {
88 return Arrays.asList(sourcePosition, targetPosition);
89 }
90
91}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/nobase/ScopeCheck.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/nobase/ScopeCheck.java
new file mode 100644
index 00000000..06989fdc
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/check/nobase/ScopeCheck.java
@@ -0,0 +1,91 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.operations.check.nobase;
10
11import java.util.Arrays;
12import java.util.List;
13import java.util.Objects;
14import java.util.function.Function;
15
16import org.eclipse.emf.ecore.EObject;
17import org.eclipse.emf.ecore.util.EcoreUtil;
18import tools.refinery.viatra.runtime.base.api.filters.IBaseIndexObjectFilter;
19import tools.refinery.viatra.runtime.emf.EMFScope;
20import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
21import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
22import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
23import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
24
25/**
26 * This operation simply checks if a model element is part of the Query Scope
27 *
28 * @author Marton Bur
29 *
30 */
31public class ScopeCheck implements ISearchOperation {
32 private class Executor extends CheckOperationExecutor {
33
34 @Override
35 protected boolean check(MatchingFrame frame, ISearchContext context) {
36 Objects.requireNonNull(frame.getValue(position), () -> String.format("Invalid plan, variable %d unbound", position));
37 Object value = frame.getValue(position);
38 if(value instanceof EObject){
39 EObject eObject = (EObject) value;
40 IBaseIndexObjectFilter filterConfiguration = scope.getOptions().getObjectFilterConfiguration();
41 boolean filtered = false;
42 if(filterConfiguration != null){
43 filtered = filterConfiguration.isFiltered(eObject);
44 }
45 if(filtered){
46 return false;
47 } else {
48 return EcoreUtil.isAncestor(scope.getScopeRoots(), eObject);
49 }
50 } else {
51 return true;
52 }
53 }
54
55 @Override
56 public ISearchOperation getOperation() {
57 return ScopeCheck.this;
58 }
59
60 }
61
62 private int position;
63 private EMFScope scope;
64
65 public ScopeCheck(int position, EMFScope scope) {
66 this.position = position;
67 this.scope = scope;
68
69 }
70
71 @Override
72 public ISearchOperationExecutor createExecutor() {
73 return new Executor();
74 }
75
76 @Override
77 public String toString() {
78 return toString(Object::toString);
79 }
80
81
82 @Override
83 public String toString(Function<Integer, String> variableMapping) {
84 return "check +"+variableMapping.apply(position) +" in scope "+scope;
85 }
86
87 @Override
88 public List<Integer> getVariablePositions() {
89 return Arrays.asList(position);
90 }
91}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/AggregatorExtend.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/AggregatorExtend.java
new file mode 100644
index 00000000..c2e75b7a
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/AggregatorExtend.java
@@ -0,0 +1,106 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.operations.extend;
10
11import java.util.Arrays;
12import java.util.Collections;
13import java.util.Iterator;
14import java.util.List;
15import java.util.function.Function;
16import java.util.stream.Stream;
17
18import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
19import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
20import tools.refinery.viatra.runtime.localsearch.operations.IPatternMatcherOperation;
21import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
22import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
23import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
24import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
25import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint;
26import tools.refinery.viatra.runtime.matchers.tuple.VolatileModifiableMaskedTuple;
27
28/**
29 * Calculates the aggregated value of a column based on the given {@link AggregatorConstraint}
30 *
31 * @author Balázs Grill
32 * @since 1.4
33 */
34public class AggregatorExtend implements ISearchOperation, IPatternMatcherOperation{
35
36 private class Executor extends SingleValueExtendOperationExecutor<Object> {
37
38 private final VolatileModifiableMaskedTuple maskedTuple;
39 private IQueryResultProvider matcher;
40
41 public Executor(int position) {
42 super(position);
43 this.maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask());
44 }
45
46 @Override
47 public Iterator<?> getIterator(MatchingFrame frame, ISearchContext context) {
48 maskedTuple.updateTuple(frame);
49 matcher = context.getMatcher(information.getCallWithAdornment());
50 Object aggregate = aggregate(aggregator.getAggregator().getOperator(), aggregator.getAggregatedColumn());
51 return aggregate == null ? Collections.emptyIterator() : Collections.singletonList(aggregate).iterator();
52
53 }
54
55 @SuppressWarnings("unchecked")
56 private <Domain, Accumulator, AggregateResult> AggregateResult aggregate(
57 IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, int aggregatedColumn) {
58 final Stream<Domain> valueStream = matcher.getAllMatches(information.getParameterMask(), maskedTuple)
59 .map(match -> (Domain) match.get(aggregatedColumn));
60 return operator.aggregateStream(valueStream);
61 }
62
63 @Override
64 public ISearchOperation getOperation() {
65 return AggregatorExtend.this;
66 }
67 }
68
69 private final AggregatorConstraint aggregator;
70 private final CallInformation information;
71 private final int position;
72
73 /**
74 * @since 1.7
75 */
76 public AggregatorExtend(CallInformation information, AggregatorConstraint aggregator, int position) {
77 this.aggregator = aggregator;
78 this.information = information;
79 this.position = position;
80 }
81
82 @Override
83 public ISearchOperationExecutor createExecutor() {
84 return new Executor(position);
85 }
86
87 @Override
88 public List<Integer> getVariablePositions() {
89 return Arrays.asList(position);
90 }
91
92 @Override
93 public String toString() {
94 return toString(Object::toString);
95 }
96
97 @Override
98 public String toString(Function<Integer, String> variableMapping) {
99 return "extend -"+variableMapping.apply(position)+" = " + aggregator.getAggregator().getOperator().getName()+" find " + information.toString(variableMapping);
100 }
101
102 @Override
103 public CallInformation getCallInformation() {
104 return information;
105 }
106}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/CountOperation.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/CountOperation.java
new file mode 100644
index 00000000..08ecc8d6
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/CountOperation.java
@@ -0,0 +1,88 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations.extend;
10
11import java.util.Collections;
12import java.util.Iterator;
13import java.util.List;
14import java.util.function.Function;
15
16import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
17import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
18import tools.refinery.viatra.runtime.localsearch.operations.IPatternMatcherOperation;
19import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
20import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
21import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
22import tools.refinery.viatra.runtime.matchers.tuple.VolatileModifiableMaskedTuple;
23
24/**
25 * Calculates the count of matches for a called matcher
26 *
27 * @author Zoltan Ujhelyi
28 */
29public class CountOperation implements ISearchOperation, IPatternMatcherOperation{
30
31 private class Executor extends SingleValueExtendOperationExecutor<Integer> {
32 private final VolatileModifiableMaskedTuple maskedTuple;
33 private IQueryResultProvider matcher;
34
35 public Executor(int position) {
36 super(position);
37 maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask());
38 }
39
40 @Override
41 public Iterator<Integer> getIterator(MatchingFrame frame, ISearchContext context) {
42 matcher = context.getMatcher(information.getCallWithAdornment());
43 maskedTuple.updateTuple(frame);
44 return Collections.singletonList(matcher.countMatches(information.getParameterMask(), maskedTuple)).iterator();
45 }
46
47 @Override
48 public ISearchOperation getOperation() {
49 return CountOperation.this;
50 }
51 }
52
53 private final CallInformation information;
54 private final int position;
55
56 /**
57 * @since 1.7
58 */
59 public CountOperation(CallInformation information, int position) {
60 this.information = information;
61 this.position = position;
62 }
63
64 @Override
65 public ISearchOperationExecutor createExecutor() {
66 return new Executor(position);
67 }
68
69 @Override
70 public List<Integer> getVariablePositions() {
71 return information.getVariablePositions();
72 }
73
74 @Override
75 public String toString() {
76 return toString(Object::toString);
77 }
78
79 @Override
80 public String toString(Function<Integer, String> variableMapping) {
81 return "extend -"+variableMapping.apply(position)+" = count find " + information.toString(variableMapping);
82 }
83
84 @Override
85 public CallInformation getCallInformation() {
86 return information;
87 }
88}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExpressionEval.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExpressionEval.java
new file mode 100644
index 00000000..7186f4ac
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExpressionEval.java
@@ -0,0 +1,104 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations.extend;
10
11import java.util.ArrayList;
12import java.util.Collections;
13import java.util.Iterator;
14import java.util.List;
15import java.util.Map;
16import java.util.Set;
17import java.util.function.Function;
18
19import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
20import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
21import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
22import tools.refinery.viatra.runtime.localsearch.operations.MatchingFrameValueProvider;
23import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator;
24
25/**
26 * Calculates the result of an expression and stores it inside a variable for future reference.
27 *
28 * @author Zoltan Ujhelyi
29 *
30 */
31public class ExpressionEval implements ISearchOperation {
32
33 private class Executor extends SingleValueExtendOperationExecutor<Object> {
34
35 public Executor(int position) {
36 super(position);
37 }
38
39 @Override
40 public Iterator<?> getIterator(MatchingFrame frame, ISearchContext context) {
41 try {
42 Object result = evaluator.evaluateExpression(new MatchingFrameValueProvider(frame, nameMap));
43 if (!unwind && result != null){
44 return Collections.singletonList(result).iterator();
45 } else if (unwind && result instanceof Set<?>) {
46 return ((Set<?>)result).iterator();
47 } else {
48 return Collections.emptyIterator();
49 }
50 } catch (Exception e) {
51 context.getLogger().warn("Error while evaluating expression", e);
52 return Collections.emptyIterator();
53 }
54 }
55
56 @Override
57 public ISearchOperation getOperation() {
58 return ExpressionEval.this;
59 }
60 }
61
62 private final IExpressionEvaluator evaluator;
63 private final boolean unwind;
64 private final Map<String, Integer> nameMap;
65 private final int position;
66
67 public ExpressionEval(IExpressionEvaluator evaluator, Map<String, Integer> nameMap, int position) {
68 this(evaluator, nameMap, false, position);
69 }
70
71 /**
72 * @since 2.7
73 */
74 public ExpressionEval(IExpressionEvaluator evaluator, Map<String, Integer> nameMap, boolean unwind, int position) {
75 this.evaluator = evaluator;
76 this.nameMap = nameMap;
77 this.unwind = unwind;
78 this.position = position;
79 }
80
81 @Override
82 public String toString() {
83 return toString(Object::toString);
84 }
85
86 @Override
87 public String toString(Function<Integer, String> variableMapping) {
88 return "extend -"+variableMapping.apply(position)+" = expression "+evaluator.getShortDescription();
89 }
90
91 @Override
92 public ISearchOperationExecutor createExecutor() {
93 return new Executor(position);
94 }
95
96 @Override
97 public List<Integer> getVariablePositions() {
98 // XXX not sure if this is the correct implementation to get the affected variable indicies
99 List<Integer> variables = new ArrayList<>();
100 variables.addAll(nameMap.values());
101 return variables;
102 }
103
104}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendBinaryTransitiveClosure.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendBinaryTransitiveClosure.java
new file mode 100644
index 00000000..1250e84e
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendBinaryTransitiveClosure.java
@@ -0,0 +1,185 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.localsearch.operations.extend;
10
11import java.util.Arrays;
12import java.util.HashSet;
13import java.util.Iterator;
14import java.util.LinkedList;
15import java.util.List;
16import java.util.Queue;
17import java.util.Set;
18import java.util.function.Function;
19
20import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
21import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
22import tools.refinery.viatra.runtime.localsearch.operations.IPatternMatcherOperation;
23import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
24import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
25import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
26import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
27
28/**
29 * Checking for a transitive closure expressed as a local search pattern matcher. The matched pattern must have two
30 * parameters of the same model type.
31 *
32 * @author Zoltan Ujhelyi
33 * @since 1.7
34 *
35 */
36public abstract class ExtendBinaryTransitiveClosure implements ISearchOperation, IPatternMatcherOperation {
37
38 private class Executor extends SingleValueExtendOperationExecutor<Object> {
39
40 public Executor(int position) {
41 super(position);
42 }
43
44 @Override
45 public Iterator<?> getIterator(MatchingFrame frame, ISearchContext context) {
46 // Note: second parameter is NOT bound during execution, but the first is
47 IQueryResultProvider matcher = context.getMatcher(information.getCallWithAdornment());
48
49 Queue<Object> seedsToEvaluate = new LinkedList<>();
50 final Object seedValue = frame.get(seedPosition);
51 seedsToEvaluate.add(seedValue);
52 Set<Object> seedsEvaluated = new HashSet<>();
53 Set<Object> targetsFound = new HashSet<>();
54 if (reflexive) {
55 targetsFound.add(seedValue);
56 }
57
58 while(!seedsToEvaluate.isEmpty()) {
59 Object currentValue = seedsToEvaluate.poll();
60 seedsEvaluated.add(currentValue);
61 final Object[] mappedFrame = calculateCallFrame(currentValue);
62 matcher.getAllMatches(mappedFrame).forEach(match -> {
63 Object foundTarget = getTarget(match);
64 targetsFound.add(foundTarget);
65 if (!seedsEvaluated.contains(foundTarget)) {
66 seedsToEvaluate.add(foundTarget);
67 }
68 });
69 }
70
71 return targetsFound.iterator();
72 }
73
74 @Override
75 public ISearchOperation getOperation() {
76 return ExtendBinaryTransitiveClosure.this;
77 }
78 }
79
80 /**
81 * Calculates the transitive closure of a pattern match in a forward direction (first parameter bound, second
82 * unbound).
83 * </p>
84 * <strong>Note</strong>: In case the call is reflexive, it is expected that the bound parameter already matches the universe type of the call.
85 *
86 * @since 1.7
87 */
88 public static class Forward extends ExtendBinaryTransitiveClosure {
89
90 private Object[] seedFrame = new Object[2];
91
92 /**
93 * @since 2.0
94 */
95 public Forward(CallInformation information, int sourcePosition, int targetPosition, boolean reflexive) {
96 super(information, sourcePosition, targetPosition, reflexive);
97 }
98
99 protected Object[] calculateCallFrame(Object seed) {
100 seedFrame[0] = seed;
101 seedFrame[1] = null;
102 return seedFrame;
103 }
104
105 protected Object getTarget(Tuple frame) {
106 return frame.get(1);
107 }
108 }
109
110 /**
111 * Calculates the transitive closure of a pattern match in a backward direction (first parameter unbound, second
112 * bound)
113 * </p>
114 * <strong>Note</strong>: In case the call is reflexive, it is expected that the bound parameter already matches the universe type of the call.
115 *
116 * @since 2.0
117 */
118 public static class Backward extends ExtendBinaryTransitiveClosure {
119 private Object[] seedFrame = new Object[2];
120
121 /**
122 * @since 2.0
123 */
124 public Backward(CallInformation information, int sourcePosition, int targetPosition, boolean reflexive) {
125 super(information, targetPosition, sourcePosition, reflexive);
126 }
127
128 protected Object[] calculateCallFrame(Object seed) {
129 seedFrame[0] = null;
130 seedFrame[1] = seed;
131 return seedFrame;
132 }
133
134 protected Object getTarget(Tuple frame) {
135 return frame.get(0);
136 }
137 }
138
139 private final int seedPosition;
140 private final int targetPosition;
141 private final CallInformation information;
142 private final boolean reflexive;
143
144 /**
145 * The source position will be matched in the called pattern to the first parameter; while target to the second.
146 * @since 2.0
147 */
148 protected ExtendBinaryTransitiveClosure(CallInformation information, int seedPosition, int targetPosition, boolean reflexive) {
149 this.information = information;
150 this.seedPosition = seedPosition;
151 this.targetPosition = targetPosition;
152 this.reflexive = reflexive;
153 }
154
155 protected abstract Object[] calculateCallFrame(Object seed);
156
157 protected abstract Object getTarget(Tuple frame);
158
159 @Override
160 public ISearchOperationExecutor createExecutor() {
161 return new Executor(targetPosition);
162 }
163
164 @Override
165 public String toString() {
166 return toString(Object::toString);
167 }
168
169 @Override
170 public String toString(Function<Integer, String> variableMapping) {
171 String c = information.toString(variableMapping);
172 int p = c.indexOf('(');
173 return "extend find " + c.substring(0, p) + "+" + c.substring(p);
174 }
175
176 @Override
177 public List<Integer> getVariablePositions() {
178 return Arrays.asList(seedPosition, targetPosition);
179 }
180
181 @Override
182 public CallInformation getCallInformation() {
183 return information;
184 }
185}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendConstant.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendConstant.java
new file mode 100644
index 00000000..455236f3
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendConstant.java
@@ -0,0 +1,75 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, 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.localsearch.operations.extend;
10
11import java.util.Arrays;
12import java.util.Collections;
13import java.util.Iterator;
14import java.util.List;
15import java.util.function.Function;
16
17import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
18import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
19import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
20
21/**
22 * This operation handles constants in search plans by binding a variable to a constant value. Such operations should be
23 * executed as early as possible during plan execution.
24 *
25 * @author Marton Bur
26 *
27 */
28public class ExtendConstant implements ISearchOperation {
29
30 private class Executor extends SingleValueExtendOperationExecutor<Object> {
31
32 public Executor(int position) {
33 super(position);
34 }
35
36 @Override
37 public Iterator<?> getIterator(MatchingFrame frame, ISearchContext context) {
38 return Collections.singletonList(value).iterator();
39 }
40
41 @Override
42 public ISearchOperation getOperation() {
43 return ExtendConstant.this;
44 }
45 }
46
47 private final Object value;
48 private final int position;
49
50 public ExtendConstant(int position, Object value) {
51 this.position = position;
52 this.value = value;
53 }
54
55 @Override
56 public ISearchOperationExecutor createExecutor() {
57 return new Executor(position);
58 }
59
60 @Override
61 public List<Integer> getVariablePositions() {
62 return Arrays.asList(position);
63 }
64
65 @Override
66 public String toString() {
67 return toString(Object::toString);
68 }
69
70 @Override
71 public String toString(Function<Integer, String> variableMapping) {
72 return "extend constant -"+variableMapping.apply(position)+"='"+value+"'";
73 }
74
75}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendPositivePatternCall.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendPositivePatternCall.java
new file mode 100644
index 00000000..690a3241
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendPositivePatternCall.java
@@ -0,0 +1,118 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.localsearch.operations.extend;
10
11import java.util.Iterator;
12import java.util.List;
13import java.util.function.Function;
14
15import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
16import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
17import tools.refinery.viatra.runtime.localsearch.operations.ExtendOperationExecutor;
18import tools.refinery.viatra.runtime.localsearch.operations.IPatternMatcherOperation;
19import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
20import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
21import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
22import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
23import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
24import tools.refinery.viatra.runtime.matchers.tuple.VolatileModifiableMaskedTuple;
25
26/**
27 * @author Grill Balázs
28 * @since 1.4
29 *
30 */
31public class ExtendPositivePatternCall implements ISearchOperation, IPatternMatcherOperation {
32
33 private class Executor extends ExtendOperationExecutor<Tuple> {
34 private final VolatileModifiableMaskedTuple maskedTuple;
35
36 public Executor() {
37 maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask());
38 }
39
40 @Override
41 protected Iterator<? extends Tuple> getIterator(MatchingFrame frame, ISearchContext context) {
42 maskedTuple.updateTuple(frame);
43 IQueryResultProvider matcher = context.getMatcher(information.getCallWithAdornment());
44 return matcher.getAllMatches(information.getParameterMask(), maskedTuple).iterator();
45 }
46
47 /**
48 * @since 2.0
49 */
50 @Override
51 protected boolean fillInValue(Tuple result, MatchingFrame frame, ISearchContext context) {
52 TupleMask mask = information.getFullFrameMask();
53 // The first loop clears out the elements from a possible previous iteration
54 for(int i : information.getFreeParameterIndices()) {
55 mask.set(frame, i, null);
56 }
57 for(int i : information.getFreeParameterIndices()) {
58 Object oldValue = mask.getValue(frame, i);
59 Object valueToFill = result.get(i);
60 if (oldValue != null && !oldValue.equals(valueToFill)){
61 // If the inverse map contains more than one values for the same key, it means that these arguments are unified by the caller.
62 // In this case if the callee assigns different values the frame shall be dropped
63 return false;
64 }
65 mask.set(frame, i, valueToFill);
66 }
67 return true;
68 }
69
70 @Override
71 protected void cleanup(MatchingFrame frame, ISearchContext context) {
72 TupleMask mask = information.getFullFrameMask();
73 for(int i : information.getFreeParameterIndices()){
74 mask.set(frame, i, null);
75 }
76
77 }
78
79 @Override
80 public ISearchOperation getOperation() {
81 return ExtendPositivePatternCall.this;
82 }
83 }
84
85 private final CallInformation information;
86
87 /**
88 * @since 1.7
89 */
90 public ExtendPositivePatternCall(CallInformation information) {
91 this.information = information;
92 }
93
94 @Override
95 public ISearchOperationExecutor createExecutor() {
96 return new Executor();
97 }
98
99 @Override
100 public List<Integer> getVariablePositions() {
101 return information.getVariablePositions();
102 }
103
104 @Override
105 public String toString() {
106 return toString(Object::toString);
107 }
108
109 @Override
110 public String toString(Function<Integer, String> variableMapping) {
111 return "extend find " + information.toString(variableMapping);
112 }
113
114 @Override
115 public CallInformation getCallInformation() {
116 return information;
117 }
118}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendToEStructuralFeatureSource.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendToEStructuralFeatureSource.java
new file mode 100644
index 00000000..04f0a8de
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendToEStructuralFeatureSource.java
@@ -0,0 +1,112 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.operations.extend;
10
11import java.util.Arrays;
12import java.util.Iterator;
13import java.util.List;
14import java.util.function.Function;
15import java.util.stream.StreamSupport;
16
17import org.eclipse.emf.ecore.EObject;
18import org.eclipse.emf.ecore.EStructuralFeature;
19import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey;
20import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
21import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
22import tools.refinery.viatra.runtime.localsearch.operations.IIteratingSearchOperation;
23import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
24import tools.refinery.viatra.runtime.matchers.context.IInputKey;
25import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
26import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
27import tools.refinery.viatra.runtime.matchers.tuple.VolatileMaskedTuple;
28
29/**
30 * Iterates over all sources of {@link EStructuralFeature} using an {@link IQueryRuntimeContext VIATRA Base indexer}.
31 * It is assumed that the indexer is initialized for the selected {@link EStructuralFeature}.
32 *
33 */
34public class ExtendToEStructuralFeatureSource implements IIteratingSearchOperation {
35
36 private class Executor extends SingleValueExtendOperationExecutor<EObject> {
37
38 private VolatileMaskedTuple maskedTuple;
39
40 public Executor(int position) {
41 super(position);
42 this.maskedTuple = new VolatileMaskedTuple(mask);
43 }
44
45
46 @Override
47 public Iterator<EObject> getIterator(MatchingFrame frame, ISearchContext context) {
48 maskedTuple.updateTuple(frame);
49 Iterable<? extends Object> values = context.getRuntimeContext().enumerateValues(type, indexerMask, maskedTuple);
50 return StreamSupport.stream(values.spliterator(), false)
51 .filter(EObject.class::isInstance)
52 .map(EObject.class::cast)
53 .iterator();
54 }
55
56 @Override
57 public ISearchOperation getOperation() {
58 return ExtendToEStructuralFeatureSource.this;
59 }
60 }
61
62 private final int sourcePosition;
63 private final int targetPosition;
64 private final EStructuralFeature feature;
65 private final IInputKey type;
66 private static final TupleMask indexerMask = TupleMask.fromSelectedIndices(2, new int[] {1});
67 private final TupleMask mask;
68
69 /**
70 * @since 1.7
71 */
72 public ExtendToEStructuralFeatureSource(int sourcePosition, int targetPosition, EStructuralFeature feature, TupleMask mask) {
73 this.sourcePosition = sourcePosition;
74 this.targetPosition = targetPosition;
75 this.feature = feature;
76 this.mask = mask;
77 this.type = new EStructuralFeatureInstancesKey(feature);
78 }
79
80 public EStructuralFeature getFeature() {
81 return feature;
82 }
83
84 @Override
85 public ISearchOperationExecutor createExecutor() {
86 return new Executor(sourcePosition);
87 }
88
89 @Override
90 public String toString() {
91 return toString(Object::toString);
92 }
93
94 @Override
95 public String toString(Function<Integer, String> variableMapping) {
96 return "extend "+feature.getContainerClass().getSimpleName()+"."+feature.getName()+"(-"+variableMapping.apply(sourcePosition)+", +"+variableMapping.apply(targetPosition)+") indexed";
97 }
98
99 @Override
100 public List<Integer> getVariablePositions() {
101 return Arrays.asList(sourcePosition, targetPosition);
102 }
103
104 /**
105 * @since 1.4
106 */
107 @Override
108 public IInputKey getIteratedInputKey() {
109 return type;
110 }
111
112}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendToEStructuralFeatureTarget.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendToEStructuralFeatureTarget.java
new file mode 100644
index 00000000..4304fc8d
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/ExtendToEStructuralFeatureTarget.java
@@ -0,0 +1,102 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.operations.extend;
10
11import java.util.Arrays;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Iterator;
15import java.util.List;
16import java.util.function.Function;
17
18import org.eclipse.emf.ecore.EObject;
19import org.eclipse.emf.ecore.EStructuralFeature;
20import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
21import tools.refinery.viatra.runtime.localsearch.exceptions.LocalSearchException;
22import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
23import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
24
25/**
26 * Iterates over all sources of {@link EStructuralFeature}
27 */
28public class ExtendToEStructuralFeatureTarget implements ISearchOperation {
29
30 private class Executor extends SingleValueExtendOperationExecutor<Object> {
31
32 public Executor(int position) {
33 super(position);
34 }
35
36 @SuppressWarnings("unchecked")
37 @Override
38 public Iterator<?> getIterator(MatchingFrame frame, ISearchContext context) {
39 try {
40 final EObject value = (EObject) frame.getValue(sourcePosition);
41 if(! feature.getEContainingClass().isSuperTypeOf(value.eClass()) ){
42 // TODO planner should ensure the proper supertype relation
43 return Collections.emptyIterator();
44 }
45 final Object featureValue = value.eGet(feature);
46 if (feature.isMany()) {
47 if (featureValue != null) {
48 final Collection<Object> objectCollection = (Collection<Object>) featureValue;
49 return objectCollection.iterator();
50 } else {
51 return Collections.emptyIterator();
52 }
53 } else {
54 if (featureValue != null) {
55 return Collections.singletonList(featureValue).iterator();
56 } else {
57 return Collections.emptyIterator();
58 }
59 }
60 } catch (ClassCastException e) {
61 throw new LocalSearchException("Invalid feature source in parameter" + Integer.toString(sourcePosition), e);
62 }
63 }
64
65 @Override
66 public ISearchOperation getOperation() {
67 return ExtendToEStructuralFeatureTarget.this;
68 }
69 }
70
71 private final int sourcePosition;
72 private final int targetPosition;
73 private final EStructuralFeature feature;
74
75 public ExtendToEStructuralFeatureTarget(int sourcePosition, int targetPosition, EStructuralFeature feature) {
76 this.sourcePosition = sourcePosition;
77 this.targetPosition = targetPosition;
78 this.feature = feature;
79 }
80
81 @Override
82 public String toString() {
83 return toString(Object::toString);
84 }
85
86 @Override
87 public String toString(Function<Integer, String> variableMapping) {
88 return "extend "+feature.getEContainingClass().getName()+"."+feature.getName()+"(+"+variableMapping.apply(sourcePosition)+", -"+ variableMapping.apply(targetPosition) +")";
89 }
90
91 @Override
92 public ISearchOperationExecutor createExecutor() {
93 return new Executor(targetPosition);
94 }
95
96
97 @Override
98 public List<Integer> getVariablePositions() {
99 return Arrays.asList(sourcePosition, targetPosition);
100 }
101
102}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverChildren.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverChildren.java
new file mode 100644
index 00000000..10764aea
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverChildren.java
@@ -0,0 +1,90 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.operations.extend;
10
11import java.util.Arrays;
12import java.util.Iterator;
13import java.util.List;
14import java.util.function.Function;
15
16import org.eclipse.emf.ecore.EObject;
17import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
18import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
19import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
20import tools.refinery.viatra.runtime.matchers.util.Preconditions;
21
22/**
23 * Iterates all child elements of a selected EObjects.
24 *
25 * @author Zoltan Ujhelyi
26 *
27 */
28public class IterateOverChildren implements ISearchOperation {
29
30 private class Executor extends SingleValueExtendOperationExecutor<EObject> {
31
32 public Executor(int position) {
33 super(position);
34 }
35
36 @Override
37 public Iterator<EObject> getIterator(MatchingFrame frame, ISearchContext context) {
38 Preconditions.checkState(frame.get(sourcePosition) instanceof EObject, "Only children of EObject elements are supported.");
39 EObject source = (EObject) frame.get(sourcePosition);
40 if(transitive) {
41 return source.eAllContents();
42 } else {
43 return source.eContents().iterator();
44 }
45 }
46
47 @Override
48 public ISearchOperation getOperation() {
49 return IterateOverChildren.this;
50 }
51 }
52
53 private final int position;
54 private int sourcePosition;
55 private final boolean transitive;
56
57 /**
58 *
59 * @param position the position of the variable storing the child elements
60 * @param sourcePosition the position of the variable storing the parent root; must be bound
61 * @param transitive if true, child elements are iterated over transitively
62 */
63 public IterateOverChildren(int position, int sourcePosition, boolean transitive) {
64 this.position = position;
65 this.sourcePosition = sourcePosition;
66 this.transitive = transitive;
67 }
68
69 @Override
70 public String toString() {
71 return toString(Object::toString);
72 }
73
74 @Override
75 public String toString(Function<Integer, String> variableMapping) {
76 return "extend containment +"+variableMapping.apply(sourcePosition)+" <>--> -"+variableMapping.apply(position)+(transitive ? " transitively" : " directly");
77 }
78
79 @Override
80 public ISearchOperationExecutor createExecutor() {
81 return new Executor(position);
82 }
83
84
85 @Override
86 public List<Integer> getVariablePositions() {
87 return Arrays.asList(position, sourcePosition);
88 }
89
90} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverContainers.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverContainers.java
new file mode 100644
index 00000000..df7e18c9
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverContainers.java
@@ -0,0 +1,126 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.operations.extend;
10
11import java.util.Arrays;
12import java.util.Collections;
13import java.util.Iterator;
14import java.util.List;
15import java.util.NoSuchElementException;
16import java.util.function.Function;
17
18import org.eclipse.emf.ecore.EObject;
19import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
20import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
21import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
22import tools.refinery.viatra.runtime.matchers.util.Preconditions;
23
24/**
25 * Iterates all child elements of a selected EObjects.
26 *
27 * @author Zoltan Ujhelyi
28 *
29 */
30public class IterateOverContainers implements ISearchOperation {
31
32 /**
33 * A helper iterator for transitively traversing a parent of an object
34 */
35 private static final class ParentIterator implements Iterator<EObject> {
36 private EObject current;
37
38 public ParentIterator(EObject source) {
39 this.current = source;
40 }
41
42 @Override
43 public void remove() {
44 throw new UnsupportedOperationException();
45 }
46
47 @Override
48 public EObject next() {
49 EObject newObject = current.eContainer();
50 if (newObject == null) {
51 throw new NoSuchElementException(String.format("No more parents available for EObject %s", current));
52 }
53 current = newObject;
54 return current;
55 }
56
57 @Override
58 public boolean hasNext() {
59 return current.eContainer() != null;
60 }
61 }
62
63 private class Executor extends SingleValueExtendOperationExecutor<EObject> {
64
65 public Executor(int position) {
66 super(position);
67 }
68
69 @Override
70 public Iterator<EObject> getIterator(MatchingFrame frame, ISearchContext context) {
71 Preconditions.checkState(frame.get(sourcePosition) instanceof EObject, "Only children of EObject elements are supported.");
72 EObject source = (EObject) frame.get(sourcePosition);
73 EObject container = source.eContainer();
74 if (container == null) {
75 return Collections.emptyIterator();
76 } else if (transitive) {
77 return new ParentIterator(source);
78 } else {
79 return Collections.singleton(container).iterator();
80 }
81 }
82
83 @Override
84 public ISearchOperation getOperation() {
85 return IterateOverContainers.this;
86 }
87 }
88
89 private final int sourcePosition;
90 private final int containerPosition;
91 private final boolean transitive;
92
93 /**
94 *
95 * @param containerPosition the position of the variable storing the found parent elements
96 * @param sourcePosition the position of the variable storing the selected element; must be bound
97 * @param transitive if false, only the direct container is returned; otherwise all containers
98 */
99 public IterateOverContainers(int containerPosition, int sourcePosition, boolean transitive) {
100 this.containerPosition = containerPosition;
101 this.sourcePosition = sourcePosition;
102 this.transitive = transitive;
103 }
104
105
106 @Override
107 public ISearchOperationExecutor createExecutor() {
108 return new Executor(containerPosition);
109 }
110
111 @Override
112 public String toString() {
113 return toString(Object::toString);
114 }
115
116 @Override
117 public String toString(Function<Integer, String> variableMapping) {
118 return "extend containment -"+variableMapping.apply(sourcePosition)+" <>--> +"+variableMapping.apply(containerPosition)+(transitive ? " transitively" : " directly");
119 }
120
121 @Override
122 public List<Integer> getVariablePositions() {
123 return Arrays.asList(containerPosition, sourcePosition);
124 }
125
126}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverEClassInstances.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverEClassInstances.java
new file mode 100644
index 00000000..333ed1db
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverEClassInstances.java
@@ -0,0 +1,97 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.operations.extend;
10
11import java.util.Collections;
12import java.util.Iterator;
13import java.util.List;
14import java.util.function.Function;
15
16import org.eclipse.emf.ecore.EClass;
17import tools.refinery.viatra.runtime.emf.types.EClassTransitiveInstancesKey;
18import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
19import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
20import tools.refinery.viatra.runtime.localsearch.operations.IIteratingSearchOperation;
21import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
22import tools.refinery.viatra.runtime.matchers.context.IInputKey;
23import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
24import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
25import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
26
27/**
28 * Iterates all available {@link EClass} instances using an {@link IQueryRuntimeContext VIATRA Base indexer}. It is
29 * assumed that the base indexer has been registered for the selected type.
30 *
31 * @author Zoltan Ujhelyi
32 *
33 */
34public class IterateOverEClassInstances implements IIteratingSearchOperation {
35
36 private class Executor extends SingleValueExtendOperationExecutor<Object> {
37
38 public Executor(int position) {
39 super(position);
40 }
41
42 @Override
43 public Iterator<? extends Object> getIterator(MatchingFrame frame, ISearchContext context) {
44 return context.getRuntimeContext().enumerateValues(type, indexerMask, Tuples.staticArityFlatTupleOf()).iterator();
45 }
46
47 @Override
48 public ISearchOperation getOperation() {
49 return IterateOverEClassInstances.this;
50 }
51 }
52
53 private final EClass clazz;
54 private final EClassTransitiveInstancesKey type;
55 private static final TupleMask indexerMask = TupleMask.empty(1);
56 private final int position;
57
58 public IterateOverEClassInstances(int position, EClass clazz) {
59 this.position = position;
60 this.clazz = clazz;
61 type = new EClassTransitiveInstancesKey(clazz);
62 }
63
64 public EClass getClazz() {
65 return clazz;
66 }
67
68
69 @Override
70 public ISearchOperationExecutor createExecutor() {
71 return new Executor(position);
72 }
73
74 @Override
75 public String toString() {
76 return toString(Object::toString);
77 }
78
79 @Override
80 public String toString(Function<Integer, String> variableMapping) {
81 return "extend "+clazz.getName()+"(-"+ variableMapping.apply(position)+") indexed";
82 }
83
84 @Override
85 public List<Integer> getVariablePositions() {
86 return Collections.singletonList(position);
87 }
88
89 /**
90 * @since 1.4
91 */
92 @Override
93 public IInputKey getIteratedInputKey() {
94 return type;
95 }
96
97}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverEDatatypeInstances.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverEDatatypeInstances.java
new file mode 100644
index 00000000..248a3d27
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverEDatatypeInstances.java
@@ -0,0 +1,96 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.operations.extend;
10
11import java.util.Collections;
12import java.util.Iterator;
13import java.util.List;
14import java.util.function.Function;
15
16import org.eclipse.emf.ecore.EDataType;
17import tools.refinery.viatra.runtime.emf.types.EDataTypeInSlotsKey;
18import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
19import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
20import tools.refinery.viatra.runtime.localsearch.operations.IIteratingSearchOperation;
21import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
22import tools.refinery.viatra.runtime.matchers.context.IInputKey;
23import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
24import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
25import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
26
27
28/**
29 * Iterates over all {@link EDataType} instances using an {@link IQueryRuntimeContext VIATRA Base indexer}. It is
30 * assumed that the indexer is initialized for the selected {@link EDataType}.
31 *
32 */
33public class IterateOverEDatatypeInstances implements IIteratingSearchOperation {
34
35 private class Executor extends SingleValueExtendOperationExecutor<Object> {
36
37 public Executor(int position) {
38 super(position);
39 }
40
41 @Override
42 public Iterator<? extends Object> getIterator(MatchingFrame frame, ISearchContext context) {
43 return context.getRuntimeContext().enumerateValues(type, indexerMask, Tuples.staticArityFlatTupleOf()).iterator();
44 }
45
46 @Override
47 public ISearchOperation getOperation() {
48 return IterateOverEDatatypeInstances.this;
49 }
50 }
51
52 private final EDataType dataType;
53 private final EDataTypeInSlotsKey type;
54 private static final TupleMask indexerMask = TupleMask.empty(1);
55 private final int position;
56
57 public IterateOverEDatatypeInstances(int position, EDataType dataType) {
58 this.position = position;
59 this.dataType = dataType;
60 type = new EDataTypeInSlotsKey(dataType);
61 }
62
63 public EDataType getDataType() {
64 return dataType;
65 }
66
67 @Override
68 public ISearchOperationExecutor createExecutor() {
69 return new Executor(position);
70 }
71
72 @Override
73 public String toString() {
74 return toString(Object::toString);
75 }
76
77 @Override
78 public String toString(Function<Integer, String> variableMapping) {
79 return "extend "+dataType.getName()+"(-"+variableMapping.apply(position)+") indexed";
80 }
81
82 @Override
83 public List<Integer> getVariablePositions() {
84 return Collections.singletonList(position);
85 }
86
87 /**
88 * @since 1.4
89 */
90 @Override
91 public IInputKey getIteratedInputKey() {
92 return type;
93 }
94
95
96}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverEStructuralFeatureInstances.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverEStructuralFeatureInstances.java
new file mode 100644
index 00000000..961939fa
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/IterateOverEStructuralFeatureInstances.java
@@ -0,0 +1,115 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.operations.extend;
10
11import java.util.Arrays;
12import java.util.Iterator;
13import java.util.List;
14import java.util.function.Function;
15
16import org.eclipse.emf.ecore.EStructuralFeature;
17import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey;
18import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
19import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
20import tools.refinery.viatra.runtime.localsearch.operations.IIteratingSearchOperation;
21import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
22import tools.refinery.viatra.runtime.matchers.context.IInputKey;
23import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
24import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
25import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
26import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
27
28/**
29 * Iterates all available {@link EStructuralFeature} elements using an {@link IQueryRuntimeContext VIATRA Base
30 * indexer}. It is assumed that the base indexer has been registered for the selected reference type.
31 *
32 */
33public class IterateOverEStructuralFeatureInstances implements IIteratingSearchOperation{
34
35 private class Executor implements ISearchOperationExecutor {
36 private Iterator<Tuple> it;
37
38 @Override
39 public void onBacktrack(MatchingFrame frame, ISearchContext context) {
40 frame.setValue(sourcePosition, null);
41 frame.setValue(targetPosition, null);
42 it = null;
43 }
44
45 @Override
46 public void onInitialize(MatchingFrame frame, ISearchContext context) {
47 Iterable<Tuple> tuples = context.getRuntimeContext().enumerateTuples(type, indexerMask, Tuples.staticArityFlatTupleOf());
48
49 it = tuples.iterator();
50 }
51
52 @Override
53 public boolean execute(MatchingFrame frame, ISearchContext context) {
54 if (it.hasNext()) {
55 final Tuple next = it.next();
56 frame.setValue(sourcePosition, next.get(0));
57 frame.setValue(targetPosition, next.get(1));
58 return true;
59 } else {
60 return false;
61 }
62 }
63
64 @Override
65 public ISearchOperation getOperation() {
66 return IterateOverEStructuralFeatureInstances.this;
67 }
68 }
69
70 private final EStructuralFeature feature;
71 private final int sourcePosition;
72 private final int targetPosition;
73 private final EStructuralFeatureInstancesKey type;
74 private static final TupleMask indexerMask = TupleMask.empty(2);
75
76 public IterateOverEStructuralFeatureInstances(int sourcePosition, int targetPosition, EStructuralFeature feature) {
77 this.sourcePosition = sourcePosition;
78 this.targetPosition = targetPosition;
79 this.feature = feature;
80 type = new EStructuralFeatureInstancesKey(feature);
81 }
82
83 public EStructuralFeature getFeature() {
84 return feature;
85 }
86
87 @Override
88 public ISearchOperationExecutor createExecutor() {
89 return new Executor();
90 }
91
92 @Override
93 public String toString() {
94 return toString(Object::toString);
95 }
96
97 @Override
98 public String toString(Function<Integer, String> variableMapping) {
99 return "extend "+feature.getContainerClass().getSimpleName()+"."+feature.getName()+"(-"+variableMapping.apply(sourcePosition)+", -"+variableMapping.apply(targetPosition)+") indexed";
100 }
101
102 @Override
103 public List<Integer> getVariablePositions() {
104 return Arrays.asList(sourcePosition, targetPosition);
105 }
106
107 /**
108 * @since 1.4
109 */
110 @Override
111 public IInputKey getIteratedInputKey() {
112 return type;
113 }
114
115}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/SingleValueExtendOperationExecutor.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/SingleValueExtendOperationExecutor.java
new file mode 100644
index 00000000..a04ffcca
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/SingleValueExtendOperationExecutor.java
@@ -0,0 +1,40 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.localsearch.operations.extend;
10
11import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
12import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
13import tools.refinery.viatra.runtime.localsearch.operations.ExtendOperationExecutor;
14
15/**
16 * @since 2.0
17 * @noextend This class is not intended to be subclassed by clients.
18 */
19public abstract class SingleValueExtendOperationExecutor<T> extends ExtendOperationExecutor<T> {
20 protected int position;
21
22 /**
23 * @param position the frame position all values are to be added
24 */
25 public SingleValueExtendOperationExecutor(int position) {
26 super();
27 this.position = position;
28 }
29
30 @Override
31 protected final boolean fillInValue(T newValue, MatchingFrame frame, ISearchContext context) {
32 frame.setValue(position, newValue);
33 return true;
34 }
35
36 @Override
37 protected final void cleanup(MatchingFrame frame, ISearchContext context) {
38 frame.setValue(position, null);
39 }
40} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/AbstractIteratingExtendOperationExecutor.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/AbstractIteratingExtendOperationExecutor.java
new file mode 100644
index 00000000..954d4c88
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/AbstractIteratingExtendOperationExecutor.java
@@ -0,0 +1,54 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.operations.extend.nobase;
10
11import java.util.Collections;
12import java.util.Spliterator;
13import java.util.Spliterators;
14import java.util.stream.Stream;
15import java.util.stream.StreamSupport;
16
17import org.eclipse.emf.common.notify.Notifier;
18import org.eclipse.emf.ecore.EObject;
19import org.eclipse.emf.ecore.resource.Resource;
20import org.eclipse.emf.ecore.resource.ResourceSet;
21import tools.refinery.viatra.runtime.emf.EMFScope;
22import tools.refinery.viatra.runtime.localsearch.operations.extend.SingleValueExtendOperationExecutor;
23
24/**
25 * This abstract class provides a utility method for extenders to iterate over the given scope.
26 *
27 * @author Grill Balázs
28 * @noextend This class is not intended to be subclassed by clients.
29 *
30 */
31abstract class AbstractIteratingExtendOperationExecutor<T> extends SingleValueExtendOperationExecutor<T> {
32
33 private final EMFScope scope;
34
35 public AbstractIteratingExtendOperationExecutor(int position, EMFScope scope) {
36 super(position);
37 this.scope = scope;
38 }
39
40 protected Stream<Notifier> getModelContents() {
41 return scope.getScopeRoots().stream().map(input -> {
42 if (input instanceof ResourceSet) {
43 return ((ResourceSet) input).getAllContents();
44 } else if (input instanceof Resource) {
45 return ((Resource) input).getAllContents();
46 } else if (input instanceof EObject) {
47 return ((EObject) input).eAllContents();
48 }
49 return Collections.<Notifier> emptyIterator();
50 }).map(i -> StreamSupport.stream(Spliterators.spliteratorUnknownSize(i, Spliterator.ORDERED), false))
51 .flatMap(i -> i);
52 }
53
54}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/ExtendToEStructuralFeatureSource.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/ExtendToEStructuralFeatureSource.java
new file mode 100644
index 00000000..fc79640b
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/ExtendToEStructuralFeatureSource.java
@@ -0,0 +1,118 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.operations.extend.nobase;
10
11import java.util.Arrays;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Iterator;
15import java.util.List;
16import java.util.function.Function;
17
18import org.eclipse.emf.ecore.EObject;
19import org.eclipse.emf.ecore.EReference;
20import org.eclipse.emf.ecore.EStructuralFeature;
21import tools.refinery.viatra.runtime.base.api.NavigationHelper;
22import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
23import tools.refinery.viatra.runtime.localsearch.exceptions.LocalSearchException;
24import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
25import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
26import tools.refinery.viatra.runtime.localsearch.operations.extend.SingleValueExtendOperationExecutor;
27
28/**
29 * Iterates over all sources of {@link EStructuralFeature} using an {@link NavigationHelper VIATRA Base indexer}.
30 * It is assumed that the indexer is initialized for the selected {@link EStructuralFeature}.
31 *
32 */
33public class ExtendToEStructuralFeatureSource implements ISearchOperation {
34
35 private class Executor extends SingleValueExtendOperationExecutor<Object> {
36
37 private Executor() {
38 super(sourcePosition);
39 }
40
41 @SuppressWarnings("unchecked")
42 @Override
43 public Iterator<?> getIterator(MatchingFrame frame, ISearchContext context) {
44 if(!(feature instanceof EReference)){
45 throw new LocalSearchException("Without base index, inverse navigation only possible along "
46 + "EReferences with defined EOpposite.");
47 }
48 EReference oppositeFeature = ((EReference)feature).getEOpposite();
49 if(oppositeFeature == null){
50 throw new LocalSearchException("Feature has no EOpposite, so cannot do inverse navigation " + feature.toString());
51 }
52 try {
53 final EObject value = (EObject) frame.getValue(targetPosition);
54 if(! oppositeFeature.getEContainingClass().isSuperTypeOf(value.eClass()) ){
55 // TODO planner should ensure the proper supertype relation
56 return Collections.emptyIterator();
57 }
58 final Object featureValue = value.eGet(oppositeFeature);
59 if (oppositeFeature.isMany()) {
60 if (featureValue != null) {
61 final Collection<Object> objectCollection = (Collection<Object>) featureValue;
62 return objectCollection.iterator();
63 } else {
64 return Collections.emptyIterator();
65 }
66 } else {
67 if (featureValue != null) {
68 return Collections.singleton(featureValue).iterator();
69 } else {
70 return Collections.emptyIterator();
71 }
72 }
73 } catch (ClassCastException e) {
74 throw new LocalSearchException("Invalid feature target in parameter" + Integer.toString(targetPosition), e);
75 }
76 }
77
78 @Override
79 public ISearchOperation getOperation() {
80 return ExtendToEStructuralFeatureSource.this;
81 }
82 }
83
84 private int targetPosition;
85 private EStructuralFeature feature;
86 private int sourcePosition;
87
88 public ExtendToEStructuralFeatureSource(int sourcePosition, int targetPosition, EStructuralFeature feature) {
89 this.sourcePosition = sourcePosition;
90 this.targetPosition = targetPosition;
91 this.feature = feature;
92 }
93
94 public EStructuralFeature getFeature() {
95 return feature;
96 }
97
98 @Override
99 public ISearchOperationExecutor createExecutor() {
100 return new Executor();
101 }
102
103 @Override
104 public String toString() {
105 return toString(Object::toString);
106 }
107
108 @Override
109 public String toString(Function<Integer, String> variableMapping) {
110 return "extend "+feature.getContainerClass().getSimpleName()+"."+feature.getName()+"(-"+variableMapping.apply(sourcePosition)+", +"+variableMapping.apply(targetPosition)+") iterating";
111 }
112
113 @Override
114 public List<Integer> getVariablePositions() {
115 return Arrays.asList(sourcePosition, targetPosition);
116 }
117
118}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/IterateOverEClassInstances.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/IterateOverEClassInstances.java
new file mode 100644
index 00000000..0a4c46ec
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/IterateOverEClassInstances.java
@@ -0,0 +1,93 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.operations.extend.nobase;
10
11import java.util.Collections;
12import java.util.Iterator;
13import java.util.List;
14import java.util.function.Function;
15
16import org.eclipse.emf.common.notify.Notifier;
17import org.eclipse.emf.ecore.EClass;
18import tools.refinery.viatra.runtime.base.api.NavigationHelper;
19import tools.refinery.viatra.runtime.emf.EMFScope;
20import tools.refinery.viatra.runtime.emf.types.EClassTransitiveInstancesKey;
21import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
22import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
23import tools.refinery.viatra.runtime.localsearch.operations.IIteratingSearchOperation;
24import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
25import tools.refinery.viatra.runtime.matchers.context.IInputKey;
26
27/**
28 * Iterates all available {@link EClass} instances without using an {@link NavigationHelper VIATRA Base indexer}.
29 *
30 * @author Zoltan Ujhelyi
31 */
32public class IterateOverEClassInstances implements IIteratingSearchOperation {
33
34 private class Executor extends AbstractIteratingExtendOperationExecutor<Notifier> {
35
36 public Executor(int position, EMFScope scope) {
37 super(position, scope);
38 }
39
40 @Override
41 public Iterator<? extends Notifier> getIterator(MatchingFrame frame, ISearchContext context) {
42 return getModelContents().filter(clazz::isInstance).iterator();
43 }
44
45 @Override
46 public ISearchOperation getOperation() {
47 return IterateOverEClassInstances.this;
48 }
49 }
50
51 private final int position;
52 private final EClass clazz;
53 private final EMFScope scope;
54
55 public IterateOverEClassInstances(int position, EClass clazz, EMFScope scope) {
56 this.position = position;
57 this.clazz = clazz;
58 this.scope = scope;
59 }
60
61 public EClass getClazz() {
62 return clazz;
63 }
64
65 @Override
66 public ISearchOperationExecutor createExecutor() {
67 return new Executor(position, scope);
68 }
69
70 @Override
71 public String toString() {
72 return toString(Object::toString);
73 }
74
75 @Override
76 public String toString(Function<Integer, String> variableMapping) {
77 return "extend "+clazz.getName()+"(-"+ variableMapping.apply(position)+") iterating";
78 }
79
80 @Override
81 public List<Integer> getVariablePositions() {
82 return Collections.singletonList(position);
83 }
84
85 /**
86 * @since 1.4
87 */
88 @Override
89 public IInputKey getIteratedInputKey() {
90 return new EClassTransitiveInstancesKey(clazz);
91 }
92
93}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/IterateOverEDatatypeInstances.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/IterateOverEDatatypeInstances.java
new file mode 100644
index 00000000..999e0a48
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/extend/nobase/IterateOverEDatatypeInstances.java
@@ -0,0 +1,123 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.operations.extend.nobase;
10
11import java.util.Collections;
12import java.util.Iterator;
13import java.util.List;
14import java.util.Map;
15import java.util.Objects;
16import java.util.Set;
17import java.util.function.Function;
18import java.util.stream.Collectors;
19import java.util.stream.Stream;
20
21import org.eclipse.emf.ecore.EAttribute;
22import org.eclipse.emf.ecore.EClass;
23import org.eclipse.emf.ecore.EDataType;
24import org.eclipse.emf.ecore.EObject;
25import tools.refinery.viatra.runtime.base.api.NavigationHelper;
26import tools.refinery.viatra.runtime.emf.EMFScope;
27import tools.refinery.viatra.runtime.emf.types.EDataTypeInSlotsKey;
28import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
29import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
30import tools.refinery.viatra.runtime.localsearch.operations.IIteratingSearchOperation;
31import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
32import tools.refinery.viatra.runtime.matchers.context.IInputKey;
33import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
34import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
35import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
36
37/**
38 * Iterates over all {@link EDataType} instances without using an {@link NavigationHelper VIATRA Base indexer}.
39 *
40 */
41public class IterateOverEDatatypeInstances implements IIteratingSearchOperation {
42
43 private class Executor extends AbstractIteratingExtendOperationExecutor<Object> {
44
45 public Executor(int position, EMFScope scope) {
46 super(position, scope);
47 }
48
49 @Override
50 public Iterator<? extends Object> getIterator(MatchingFrame frame, final ISearchContext context) {
51 return getModelContents().filter(EObject.class::isInstance).map(EObject.class::cast)
52 .map(input -> doGetEAttributes(input.eClass(), context)
53 .map(attribute -> {
54 if (attribute.isMany()) {
55 return ((List<?>) input.eGet(attribute)).stream();
56 } else {
57 Object o = input.eGet(attribute);
58 return o == null ? Stream.empty() : Stream.of(o);
59 }
60 }))
61 .flatMap(i -> i)
62 .<Object>flatMap(i -> i)
63 .iterator();
64 }
65
66 @Override
67 public ISearchOperation getOperation() {
68 return IterateOverEDatatypeInstances.this;
69 }
70 }
71
72 private final EDataType dataType;
73 private final int position;
74 private final EMFScope scope;
75
76 public IterateOverEDatatypeInstances(int position, EDataType dataType, EMFScope scope) {
77 this.position = position;
78 this.dataType = dataType;
79 this.scope = scope;
80 }
81
82 protected Stream<EAttribute> doGetEAttributes(EClass eclass, ISearchContext context){
83 @SuppressWarnings({ "unchecked"})
84 Map<Tuple, Set<EAttribute>> cache = context.accessBackendLevelCache(getClass(), Map.class, CollectionsFactory::createMap);
85 Tuple compositeKey = Tuples.staticArityFlatTupleOf(dataType, eclass);
86 return cache.computeIfAbsent(compositeKey, k ->
87 eclass.getEAllAttributes().stream().filter(input -> Objects.equals(input.getEType(), dataType)).collect(Collectors.toSet())
88 ).stream();
89 }
90
91 public EDataType getDataType() {
92 return dataType;
93 }
94
95 @Override
96 public ISearchOperationExecutor createExecutor() {
97 return new Executor(position, scope);
98 }
99
100 @Override
101 public String toString() {
102 return toString(Object::toString);
103 }
104
105 @Override
106 public String toString(Function<Integer, String> variableMapping) {
107 return "extend "+dataType.getName()+"(-"+variableMapping.apply(position)+") iterating";
108 }
109
110 @Override
111 public List<Integer> getVariablePositions() {
112 return Collections.singletonList(position);
113 }
114
115 /**
116 * @since 1.4
117 */
118 @Override
119 public IInputKey getIteratedInputKey() {
120 return new EDataTypeInSlotsKey(dataType);
121 }
122
123}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/generic/GenericTypeCheck.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/generic/GenericTypeCheck.java
new file mode 100644
index 00000000..2b189c57
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/generic/GenericTypeCheck.java
@@ -0,0 +1,96 @@
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.localsearch.operations.generic;
10
11import java.util.ArrayList;
12import java.util.Collections;
13import java.util.List;
14import java.util.function.Function;
15import java.util.stream.Collectors;
16
17import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
18import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
19import tools.refinery.viatra.runtime.localsearch.operations.CheckOperationExecutor;
20import tools.refinery.viatra.runtime.localsearch.operations.IIteratingSearchOperation;
21import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
22import tools.refinery.viatra.runtime.matchers.context.IInputKey;
23import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
24import tools.refinery.viatra.runtime.matchers.tuple.VolatileMaskedTuple;
25import tools.refinery.viatra.runtime.matchers.util.Preconditions;
26
27/**
28 * @author Zoltan Ujhelyi
29 * @since 1.7
30 * @noextend This class is not intended to be subclassed by clients.
31 */
32public class GenericTypeCheck implements ISearchOperation, IIteratingSearchOperation {
33
34 private class Executor extends CheckOperationExecutor {
35 private VolatileMaskedTuple maskedTuple;
36
37 private Executor() {
38 this.maskedTuple = new VolatileMaskedTuple(callMask);
39 }
40
41 @Override
42 protected boolean check(MatchingFrame frame, ISearchContext context) {
43 maskedTuple.updateTuple(frame);
44 return context.getRuntimeContext().containsTuple(type, maskedTuple);
45 }
46
47 @Override
48 public ISearchOperation getOperation() {
49 return GenericTypeCheck.this;
50 }
51 }
52
53 private final IInputKey type;
54 private final List<Integer> positionList;
55 private final TupleMask callMask;
56
57 public GenericTypeCheck(IInputKey type, int[] positions, TupleMask callMask) {
58 this.callMask = callMask;
59 Preconditions.checkArgument(positions.length == type.getArity(),
60 "The type %s requires %d parameters, but %d positions are provided", type.getPrettyPrintableName(),
61 type.getArity(), positions.length);
62 List<Integer> modifiablePositionList = new ArrayList<>();
63 for (int position : positions) {
64 modifiablePositionList.add(position);
65 }
66 this.positionList = Collections.unmodifiableList(modifiablePositionList);
67 this.type = type;
68 }
69
70 @Override
71 public List<Integer> getVariablePositions() {
72 return positionList;
73 }
74
75 @Override
76 public ISearchOperationExecutor createExecutor() {
77 return new Executor();
78 }
79
80 @Override
81 public String toString() {
82 return toString(Object::toString);
83 }
84
85 @Override
86 public String toString(Function<Integer, String> variableMapping) {
87 return "check " + type.getPrettyPrintableName() + "("
88 + positionList.stream().map(input -> String.format("+%s", variableMapping.apply(input))).collect(Collectors.joining(", "))
89 + ")";
90 }
91
92 @Override
93 public IInputKey getIteratedInputKey() {
94 return type;
95 }
96}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/generic/GenericTypeExtend.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/generic/GenericTypeExtend.java
new file mode 100644
index 00000000..dfc3e9ad
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/generic/GenericTypeExtend.java
@@ -0,0 +1,145 @@
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.localsearch.operations.generic;
10
11import java.util.ArrayList;
12import java.util.Collections;
13import java.util.Iterator;
14import java.util.List;
15import java.util.Objects;
16import java.util.Set;
17import java.util.function.Function;
18import java.util.stream.Collectors;
19
20import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
21import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
22import tools.refinery.viatra.runtime.localsearch.operations.ExtendOperationExecutor;
23import tools.refinery.viatra.runtime.localsearch.operations.IIteratingSearchOperation;
24import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
25import tools.refinery.viatra.runtime.matchers.context.IInputKey;
26import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
27import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
28import tools.refinery.viatra.runtime.matchers.tuple.VolatileMaskedTuple;
29import tools.refinery.viatra.runtime.matchers.util.Preconditions;
30
31/**
32 * @author Zoltan Ujhelyi
33 * @since 1.7
34 * @noextend This class is not intended to be subclassed by clients.
35 */
36public class GenericTypeExtend implements IIteratingSearchOperation {
37
38 private class Executor extends ExtendOperationExecutor<Tuple> {
39 private final VolatileMaskedTuple maskedTuple;
40
41 public Executor() {
42 this.maskedTuple = new VolatileMaskedTuple(callMask);
43 }
44
45 @Override
46 protected Iterator<? extends Tuple> getIterator(MatchingFrame frame, ISearchContext context) {
47 maskedTuple.updateTuple(frame);
48 return context.getRuntimeContext().enumerateTuples(type, indexerMask, maskedTuple).iterator();
49 }
50
51 @Override
52 protected boolean fillInValue(Tuple newTuple, MatchingFrame frame, ISearchContext context) {
53 for (Integer position : unboundVariableIndices) {
54 frame.setValue(position, null);
55 }
56 for (int i = 0; i < positions.length; i++) {
57 Object newValue = newTuple.get(i);
58 Object oldValue = frame.getValue(positions[i]);
59 if (oldValue != null && !Objects.equals(oldValue, newValue)) {
60 // If positions tuple maps more than one values for the same element (e.g. loop), it means that
61 // these arguments are to unified by the caller. In this case if the callee assigns different values
62 // the frame shall be considered a failed match
63 return false;
64 }
65 frame.setValue(positions[i], newValue);
66 }
67 return true;
68 }
69
70 @Override
71 protected void cleanup(MatchingFrame frame, ISearchContext context) {
72 for (Integer position : unboundVariableIndices) {
73 frame.setValue(position, null);
74 }
75 }
76
77 @Override
78 public ISearchOperation getOperation() {
79 return GenericTypeExtend.this;
80 }
81 }
82
83 private final IInputKey type;
84 private final int[] positions;
85 private final List<Integer> positionList;
86 private final Set<Integer> unboundVariableIndices;
87 private final TupleMask indexerMask;
88 private final TupleMask callMask;
89
90 /**
91 *
92 * @param type
93 * the type to execute the extend operation on
94 * @param positions
95 * the parameter positions that represent the variables of the input key
96 * @param unboundVariableIndices
97 * the set of positions that are bound at the start of the operation
98 */
99 public GenericTypeExtend(IInputKey type, int[] positions, TupleMask callMask, TupleMask indexerMask, Set<Integer> unboundVariableIndices) {
100 Preconditions.checkArgument(positions.length == type.getArity(),
101 "The type %s requires %d parameters, but %d positions are provided", type.getPrettyPrintableName(),
102 type.getArity(), positions.length);
103 List<Integer> modifiablePositionList = new ArrayList<>();
104 for (int position : positions) {
105 modifiablePositionList.add(position);
106 }
107 this.positionList = Collections.unmodifiableList(modifiablePositionList);
108 this.positions = positions;
109 this.type = type;
110
111 this.unboundVariableIndices = unboundVariableIndices;
112 this.indexerMask = indexerMask;
113 this.callMask = callMask;
114 }
115
116 @Override
117 public IInputKey getIteratedInputKey() {
118 return type;
119 }
120
121 @Override
122 public ISearchOperationExecutor createExecutor() {
123 return new Executor();
124 }
125
126 @Override
127 public List<Integer> getVariablePositions() {
128 return positionList;
129 }
130
131 @Override
132 public String toString() {
133 return toString(Object::toString);
134 }
135
136 @Override
137 public String toString(Function<Integer, String> variableMapping) {
138 return "extend " + type.getPrettyPrintableName() + "("
139 + positionList.stream()
140 .map(input -> String.format("%s%s", unboundVariableIndices.contains(input) ? "-" : "+", variableMapping.apply(input)))
141 .collect(Collectors.joining(", "))
142 + ")";
143 }
144
145}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/generic/GenericTypeExtendSingleValue.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/generic/GenericTypeExtendSingleValue.java
new file mode 100644
index 00000000..45e4fd0e
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/generic/GenericTypeExtendSingleValue.java
@@ -0,0 +1,114 @@
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.localsearch.operations.generic;
10
11import java.util.ArrayList;
12import java.util.Collections;
13import java.util.Iterator;
14import java.util.List;
15import java.util.Objects;
16import java.util.function.Function;
17import java.util.stream.Collectors;
18
19import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
20import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
21import tools.refinery.viatra.runtime.localsearch.operations.IIteratingSearchOperation;
22import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
23import tools.refinery.viatra.runtime.localsearch.operations.extend.SingleValueExtendOperationExecutor;
24import tools.refinery.viatra.runtime.matchers.context.IInputKey;
25import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
26import tools.refinery.viatra.runtime.matchers.tuple.VolatileMaskedTuple;
27import tools.refinery.viatra.runtime.matchers.util.Preconditions;
28
29/**
30 * @author Zoltan Ujhelyi
31 * @since 1.7
32 * @noextend This class is not intended to be subclassed by clients.
33 */
34public class GenericTypeExtendSingleValue implements IIteratingSearchOperation {
35
36 private class Executor extends SingleValueExtendOperationExecutor<Object> {
37
38 private final VolatileMaskedTuple maskedTuple;
39
40 public Executor(int position) {
41 super(position);
42 this.maskedTuple = new VolatileMaskedTuple(callMask);
43 }
44
45 @Override
46 protected Iterator<? extends Object> getIterator(MatchingFrame frame, ISearchContext context) {
47 maskedTuple.updateTuple(frame);
48 return context.getRuntimeContext().enumerateValues(type, indexerMask, maskedTuple).iterator();
49 }
50
51 @Override
52 public ISearchOperation getOperation() {
53 return GenericTypeExtendSingleValue.this;
54 }
55 }
56
57 private final IInputKey type;
58 private final List<Integer> positionList;
59 private final TupleMask indexerMask;
60 private final TupleMask callMask;
61 private final int unboundVariableIndex;
62
63 /**
64 *
65 * @param type
66 * the type to execute the extend operation on
67 * @param positions
68 * the parameter positions that represent the variables of the input key
69 */
70 public GenericTypeExtendSingleValue(IInputKey type, int[] positions, TupleMask callMask, TupleMask indexerMask, int unboundVariableIndex) {
71 Preconditions.checkArgument(positions.length == type.getArity(),
72 "The type %s requires %d parameters, but %d positions are provided", type.getPrettyPrintableName(),
73 type.getArity(), positions.length);
74 List<Integer> modifiablePositionList = new ArrayList<>();
75 for (int position : positions) {
76 modifiablePositionList.add(position);
77 }
78 this.unboundVariableIndex = unboundVariableIndex;
79 this.positionList = Collections.unmodifiableList(modifiablePositionList);
80 this.type = type;
81
82 this.callMask = callMask;
83 this.indexerMask = indexerMask;
84 }
85
86 @Override
87 public IInputKey getIteratedInputKey() {
88 return type;
89 }
90
91 @Override
92 public ISearchOperationExecutor createExecutor() {
93 return new Executor(unboundVariableIndex);
94 }
95
96 @Override
97 public List<Integer> getVariablePositions() {
98 return positionList;
99 }
100
101 @Override
102 public String toString() {
103 return toString(Object::toString);
104 }
105
106 @Override
107 public String toString(Function<Integer, String> variableMapping) {
108 return "extend " + type.getPrettyPrintableName() + "("
109 + positionList.stream().map(
110 input -> String.format("%s%s", Objects.equals(input, unboundVariableIndex) ? "-" : "+", variableMapping.apply(input)))
111 .collect(Collectors.joining(", "))
112 + ")";
113 }
114}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/util/CallInformation.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/util/CallInformation.java
new file mode 100644
index 00000000..b141a7b0
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/operations/util/CallInformation.java
@@ -0,0 +1,186 @@
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.localsearch.operations.util;
10
11import java.util.ArrayList;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Map;
16import java.util.Set;
17import java.util.function.Function;
18import java.util.stream.Collectors;
19
20import tools.refinery.viatra.runtime.localsearch.matcher.CallWithAdornment;
21import tools.refinery.viatra.runtime.localsearch.matcher.MatcherReference;
22import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference;
23import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
24import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.PatternCallBasedDeferred;
25import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryReflexiveTransitiveClosure;
26import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
27import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
28import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
29import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
30import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
31import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
32
33/**
34 * This class stores a precompiled version of call-related metadata and masks for local search operations
35 *
36 * @author Zoltan Ujhelyi
37 * @since 1.7
38 */
39public final class CallInformation {
40
41 private final TupleMask fullFrameMask;
42 private final TupleMask thinFrameMask;
43 private final TupleMask parameterMask;
44 private final int[] freeParameterIndices;
45
46 private final Map<PParameter, Integer> mapping = new HashMap<>();
47 private final Set<PParameter> adornment = new HashSet<>();
48 private final PQuery referredQuery;
49 private final MatcherReference matcherReference;
50 private final IQueryReference call;
51 private CallWithAdornment callWithAdornment;
52
53 public static CallInformation create(PatternCallBasedDeferred constraint, Map<PVariable, Integer> variableMapping, Set<Integer> bindings) {
54 return new CallInformation(constraint.getActualParametersTuple(), constraint, bindings, variableMapping);
55 }
56
57 public static CallInformation create(PositivePatternCall pCall, Map<PVariable, Integer> variableMapping, Set<Integer> bindings) {
58 return new CallInformation(pCall.getVariablesTuple(), pCall, bindings, variableMapping);
59 }
60
61 public static CallInformation create(BinaryTransitiveClosure constraint, Map<PVariable, Integer> variableMapping, Set<Integer> bindings) {
62 return new CallInformation(constraint.getVariablesTuple(), constraint, bindings, variableMapping);
63 }
64
65 /**
66 * @since 2.0
67 */
68 public static CallInformation create(BinaryReflexiveTransitiveClosure constraint, Map<PVariable, Integer> variableMapping, Set<Integer> bindings) {
69 return new CallInformation(constraint.getVariablesTuple(), constraint, bindings, variableMapping);
70 }
71
72 private CallInformation(Tuple actualParameters, IQueryReference call, final Set<Integer> bindings,
73 Map<PVariable, Integer> variableMapping) {
74 this.call = call;
75 this.referredQuery = call.getReferredQuery();
76 int keySize = actualParameters.getSize();
77 List<Integer> parameterMaskIndices = new ArrayList<>();
78 int[] fullParameterMaskIndices = new int[keySize];
79 for (int i = 0; i < keySize; i++) {
80 PParameter symbolicParameter = referredQuery.getParameters().get(i);
81 PVariable parameter = (PVariable) actualParameters.get(i);
82 Integer originalFrameIndex = variableMapping.get(parameter);
83 mapping.put(symbolicParameter, originalFrameIndex);
84 fullParameterMaskIndices[i] = originalFrameIndex;
85 if (bindings.contains(originalFrameIndex)) {
86 parameterMaskIndices.add(originalFrameIndex);
87 adornment.add(symbolicParameter);
88 }
89 }
90
91 thinFrameMask = TupleMask.fromSelectedIndices(variableMapping.size(), parameterMaskIndices);
92 fullFrameMask = TupleMask.fromSelectedIndices(variableMapping.size(), fullParameterMaskIndices);
93
94 // This second iteration is necessary as we don't know beforehand the number of bound parameters
95 int[] boundParameterIndices = new int[adornment.size()];
96 int boundIndex = 0;
97 freeParameterIndices = new int[keySize - adornment.size()];
98 int freeIndex = 0;
99 for (int i = 0; i < keySize; i++) {
100 if (bindings.contains(variableMapping.get(actualParameters.get(i)))) {
101 boundParameterIndices[boundIndex] = i;
102 boundIndex++;
103 } else {
104 freeParameterIndices[freeIndex] = i;
105 freeIndex++;
106 }
107 }
108 parameterMask = TupleMask.fromSelectedIndices(keySize, boundParameterIndices);
109 callWithAdornment = new CallWithAdornment(call, adornment);
110 matcherReference = callWithAdornment.getMatcherReference();
111 }
112
113 /**
114 * Returns a mask describing how the bound variables of a Matching Frame are mapped to parameter indexes
115 */
116 public TupleMask getThinFrameMask() {
117 return thinFrameMask;
118 }
119
120 /**
121 * Returns a mask describing how all variables of a Matching Frame are mapped to parameter indexes
122 */
123 public TupleMask getFullFrameMask() {
124 return fullFrameMask;
125 }
126
127 /**
128 * Returns a mask describing the adornment the called pattern uses
129 */
130 public TupleMask getParameterMask() {
131 return parameterMask;
132 }
133
134 public MatcherReference getReference() {
135 return matcherReference;
136 }
137
138 /**
139 * @since 2.1
140 */
141 public IQueryReference getCall() {
142 return call;
143 }
144
145 /**
146 * @since 2.1
147 */
148 public CallWithAdornment getCallWithAdornment() {
149 return callWithAdornment;
150 }
151
152 /**
153 * Returns the parameter indices that are unbound before the call
154 */
155 public int[] getFreeParameterIndices() {
156 return freeParameterIndices;
157 }
158
159 public List<Integer> getVariablePositions() {
160 List<Integer> variables = new ArrayList<>(mapping.size());
161 for(PParameter p : referredQuery.getParameters()){
162 variables.add(mapping.get(p));
163 }
164 return variables;
165 }
166
167
168
169 @Override
170 public String toString() {
171 return toString(Object::toString);
172 }
173
174 /**
175 * @since 2.0
176 */
177 public String toString(Function<Integer, String> variableMapping) {
178 return referredQuery.getFullyQualifiedName() + "("
179 + referredQuery.getParameters().stream().map(
180 input -> (adornment.contains(input) ? "+" : "-") + variableMapping.apply(mapping.get(input)))
181 .collect(Collectors.joining(","))
182 + ")";
183 }
184
185
186}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/IPlanDescriptor.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/IPlanDescriptor.java
new file mode 100644
index 00000000..987996c9
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/IPlanDescriptor.java
@@ -0,0 +1,49 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.plan;
10
11import java.util.Collection;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.context.IInputKey;
15import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
16import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
17
18/**
19 * Denotes an executable plan
20 *
21 * @author Grill Balázs
22 * @since 1.4
23 *
24 */
25public interface IPlanDescriptor {
26
27 /**
28 * The query which this plan implements
29 */
30 public PQuery getQuery();
31
32 /**
33 * The executable search plans for each body in the query
34 * @since 2.0
35 */
36 public Collection<SearchPlanForBody> getPlan();
37
38 /**
39 * The set of parameters this plan assumes to be bound
40 */
41 public Set<PParameter> getAdornment();
42
43 /**
44 * The collection of {@link IInputKey}s which needs to be iterated during the execution of this plan. For optimal
45 * performance, instances of these keys might be indexed.
46 */
47 public Set<IInputKey> getIteratedKeys();
48
49}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/IPlanProvider.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/IPlanProvider.java
new file mode 100644
index 00000000..b74282ca
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/IPlanProvider.java
@@ -0,0 +1,33 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.plan;
10
11import tools.refinery.viatra.runtime.localsearch.matcher.MatcherReference;
12import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHints;
13import tools.refinery.viatra.runtime.localsearch.planner.compiler.IOperationCompiler;
14import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
15import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
16import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
17
18/**
19 * @author Grill Balázs
20 * @since 1.4
21 * @noreference This interface is not intended to be referenced by clients.
22 */
23public interface IPlanProvider {
24
25 /**
26 * @throws ViatraQueryRuntimeException
27 * @since 2.1
28 */
29 public IPlanDescriptor getPlan(IQueryBackendContext backend, IOperationCompiler compiler,
30 ResultProviderRequestor resultProviderRequestor,
31 LocalSearchHints configuration, MatcherReference key);
32
33}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/PlanDescriptor.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/PlanDescriptor.java
new file mode 100644
index 00000000..a5565546
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/PlanDescriptor.java
@@ -0,0 +1,84 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.plan;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Set;
17import java.util.stream.Collectors;
18
19import tools.refinery.viatra.runtime.localsearch.operations.IIteratingSearchOperation;
20import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
21import tools.refinery.viatra.runtime.matchers.context.IInputKey;
22import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
23import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
24
25/**
26 * @author Grill Balázs
27 * @since 1.4
28 *
29 */
30public class PlanDescriptor implements IPlanDescriptor {
31
32 private final PQuery pquery;
33 private final List<SearchPlanForBody> plan;
34 private final Set<PParameter> adornment;
35 private Set<IInputKey> iteratedKeys = null;
36
37 public PlanDescriptor(PQuery pquery, Collection<SearchPlanForBody> plan, Set<PParameter> adornment) {
38 this.pquery = pquery;
39 this.plan = new ArrayList<>(plan);
40 this.adornment = adornment;
41 }
42
43 @Override
44 public PQuery getQuery() {
45 return pquery;
46 }
47
48 @Override
49 public Collection<SearchPlanForBody> getPlan() {
50 return plan;
51 }
52
53 @Override
54 public Set<PParameter> getAdornment() {
55 return adornment;
56 }
57
58 @Override
59 public Set<IInputKey> getIteratedKeys() {
60 if (iteratedKeys == null){
61 Set<IInputKey> keys = new HashSet<>();
62 for(SearchPlanForBody bodyPlan : plan){
63 for(ISearchOperation operation : bodyPlan.getCompiledOperations()){
64 if (operation instanceof IIteratingSearchOperation){
65 keys.add(((IIteratingSearchOperation) operation).getIteratedInputKey());
66 }
67 }
68 }
69 iteratedKeys = Collections.unmodifiableSet(keys);
70 }
71 return iteratedKeys;
72 }
73
74 @Override
75 public String toString() {
76 return new StringBuilder().append("Plan for ").append(pquery.getFullyQualifiedName()).append("(")
77 .append(adornment.stream().map(PParameter::getName).collect(Collectors.joining(",")))
78 .append("{")
79 .append(plan.stream().map(Object::toString).collect(Collectors.joining("}\n{")))
80 .append("}")
81 .toString();
82 }
83
84}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SearchPlan.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SearchPlan.java
new file mode 100644
index 00000000..3159f707
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SearchPlan.java
@@ -0,0 +1,99 @@
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.localsearch.plan;
10
11import java.util.ArrayList;
12import java.util.Collections;
13import java.util.List;
14import java.util.Map;
15import java.util.Map.Entry;
16import java.util.stream.Collectors;
17
18import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
19import tools.refinery.viatra.runtime.matchers.psystem.PBody;
20import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
21import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
22
23/**
24 * A SearchPlan stores a collection of SearchPlanOperations for a fixed order of variables.
25 *
26 * @author Zoltan Ujhelyi
27 *
28 */
29public class SearchPlan {
30
31 private final List<ISearchOperation> operations;
32 private final Map<Integer, PVariable> variableMapping;
33 private final TupleMask parameterMask;
34 private final PBody body;
35
36 /**
37 * @since 2.0
38 */
39 public SearchPlan(PBody body, List<ISearchOperation> operations, TupleMask parameterMask, Map<PVariable, Integer> variableMapping) {
40 this.body = body;
41 this.operations = Collections.unmodifiableList(new ArrayList<>(operations));
42 this.parameterMask = parameterMask;
43 this.variableMapping = Collections.unmodifiableMap(variableMapping.entrySet().stream()
44 .collect(Collectors.toMap(Entry::getValue, Entry::getKey)));
45 }
46
47
48 /**
49 * Returns an immutable list of operations stored in the plan.
50 * @return the operations
51 */
52 public List<ISearchOperation> getOperations() {
53 return operations;
54 }
55
56 /**
57 * Returns an immutable map of variable mappings for the plan
58 * @since 2.0
59 */
60 public Map<Integer, PVariable> getVariableMapping() {
61 return variableMapping;
62 }
63
64 /**
65 * Returns the index of a given operation in the plan
66 * @since 2.0
67 */
68 public int getOperationIndex(ISearchOperation operation) {
69 return operations.indexOf(operation);
70 }
71
72 /**
73 * @since 2.0
74 */
75 public TupleMask getParameterMask() {
76 return parameterMask;
77 }
78
79 /**
80 * @since 2.0
81 */
82 public PBody getSourceBody() {
83 return body;
84 }
85
86 @Override
87 public String toString() {
88 StringBuilder sb = new StringBuilder();
89
90 sb.append("{\n");
91 for(ISearchOperation operation : this.getOperations()){
92 sb.append("\t");
93 sb.append(operation);
94 sb.append("\n");
95 }
96 sb.append("}");
97 return sb.toString();
98 }
99}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SearchPlanExecutor.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SearchPlanExecutor.java
new file mode 100644
index 00000000..4a4e2450
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SearchPlanExecutor.java
@@ -0,0 +1,210 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Akos Horvath, Gergely Varro Zoltan Ujhelyi and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10 package tools.refinery.viatra.runtime.localsearch.plan;
11
12
13import java.util.Collections;
14import java.util.List;
15import java.util.Map;
16import java.util.concurrent.CopyOnWriteArrayList;
17import java.util.stream.Collectors;
18
19import org.apache.log4j.Logger;
20import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
21import tools.refinery.viatra.runtime.localsearch.exceptions.LocalSearchException;
22import tools.refinery.viatra.runtime.localsearch.matcher.ILocalSearchAdaptable;
23import tools.refinery.viatra.runtime.localsearch.matcher.ILocalSearchAdapter;
24import tools.refinery.viatra.runtime.localsearch.matcher.ISearchContext;
25import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
26import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation.ISearchOperationExecutor;
27import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
28import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
29import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
30import tools.refinery.viatra.runtime.matchers.util.Preconditions;
31
32/**
33 * A search plan executor is used to execute {@link SearchPlan} instances.
34 * @noinstantiate This class is not intended to be instantiated by clients.
35 */
36public class SearchPlanExecutor implements ILocalSearchAdaptable {
37
38 private int currentOperation;
39 private final List<ISearchOperationExecutor> operations;
40 private final SearchPlan plan;
41 private final ISearchContext context;
42 private final List<ILocalSearchAdapter> adapters = new CopyOnWriteArrayList<>();
43
44 /**
45 * @since 2.0
46 */
47 public Map<Integer, PVariable> getVariableMapping() {
48 return plan.getVariableMapping();
49 }
50
51 public int getCurrentOperation() {
52 return currentOperation;
53 }
54
55 public SearchPlan getSearchPlan() {
56 return plan;
57 }
58
59 /**
60 * @since 1.7
61 */
62 public TupleMask getParameterMask() {
63 return plan.getParameterMask();
64 }
65
66 @Override
67 public void addAdapters(List<ILocalSearchAdapter> adapters) {
68 for(ILocalSearchAdapter adapter : adapters){
69 if (!this.adapters.contains(adapter)){
70 this.adapters.add(adapter);
71 adapter.adapterRegistered(this);
72 }
73 }
74 }
75
76 @Override
77 public void removeAdapters(List<ILocalSearchAdapter> adapters) {
78 for (ILocalSearchAdapter adapter : adapters) {
79 if (this.adapters.remove(adapter)){
80 adapter.adapterUnregistered(this);
81 }
82 }
83 }
84
85 /**
86 * @since 2.0
87 */
88 public SearchPlanExecutor(SearchPlan plan, ISearchContext context) {
89 Preconditions.checkArgument(context != null, "Context cannot be null");
90 this.plan = plan;
91 this.context = context;
92 operations = plan.getOperations().stream().map(ISearchOperation::createExecutor).collect(Collectors.toList());
93 this.currentOperation = -1;
94 }
95
96
97 private void init(MatchingFrame frame) {
98 if (currentOperation == -1) {
99 currentOperation++;
100 ISearchOperationExecutor operation = operations.get(currentOperation);
101 if (!adapters.isEmpty()){
102 for (ILocalSearchAdapter adapter : adapters) {
103 adapter.executorInitializing(plan, frame);
104 }
105 }
106 operation.onInitialize(frame, context);
107 } else if (currentOperation == operations.size()) {
108 currentOperation--;
109 } else {
110 throw new LocalSearchException(LocalSearchException.PLAN_EXECUTION_ERROR);
111 }
112 }
113
114
115 /**
116 * Calculates the cost of the search plan.
117 */
118 public double cost() {
119 /* default generated stub */
120 return 0.0;
121 }
122
123 /**
124 * @throws ViatraQueryRuntimeException
125 */
126 public boolean execute(MatchingFrame frame) {
127 int upperBound = operations.size() - 1;
128 init(frame);
129 operationSelected(frame, currentOperation, false);
130 while (currentOperation >= 0 && currentOperation <= upperBound) {
131 if (operations.get(currentOperation).execute(frame, context)) {
132 operationExecuted(frame, currentOperation, true);
133 currentOperation++;
134 operationSelected(frame, currentOperation, false);
135 if (currentOperation <= upperBound) {
136 ISearchOperationExecutor operation = operations.get(currentOperation);
137 operation.onInitialize(frame, context);
138 }
139 } else {
140 operationExecuted(frame, currentOperation, false);
141 ISearchOperationExecutor operation = operations.get(currentOperation);
142 operation.onBacktrack(frame, context);
143 currentOperation--;
144 operationSelected(frame, currentOperation, true);
145 }
146 }
147 boolean matchFound = currentOperation > upperBound;
148 if (matchFound && !adapters.isEmpty()) {
149 for (ILocalSearchAdapter adapter : adapters) {
150 adapter.matchFound(plan, frame);
151 }
152 }
153 return matchFound;
154 }
155
156 public void resetPlan() {
157 currentOperation = -1;
158 }
159
160 public void printDebugInformation() {
161 for (int i = 0; i < operations.size(); i++) {
162 Logger.getRootLogger().debug("[" + i + "]\t" + operations.get(i).toString());
163 }
164 }
165
166 private void operationExecuted(MatchingFrame frame, int operationIndex, boolean isSuccessful) {
167 if (!adapters.isEmpty()){
168 for (ILocalSearchAdapter adapter : adapters) {
169 adapter.operationExecuted(plan, operations.get(operationIndex).getOperation(), frame, isSuccessful);
170 }
171 }
172 }
173
174 private void operationSelected(MatchingFrame frame, int operationIndex, boolean isBacktrack) {
175 if (!adapters.isEmpty() && operationIndex >= 0 && operationIndex < operations.size()){
176 for (ILocalSearchAdapter adapter : adapters) {
177 adapter.operationSelected(plan, operations.get(operationIndex).getOperation(), frame, isBacktrack);
178 }
179 }
180 }
181
182 public ISearchContext getContext() {
183 return context;
184 }
185
186 @Override
187 public List<ILocalSearchAdapter> getAdapters() {
188 return Collections.<ILocalSearchAdapter>unmodifiableList(this.adapters);
189 }
190
191 @Override
192 public void addAdapter(ILocalSearchAdapter adapter) {
193 addAdapters(Collections.singletonList(adapter));
194 }
195
196 @Override
197 public void removeAdapter(ILocalSearchAdapter adapter) {
198 removeAdapters(Collections.singletonList(adapter));
199 }
200
201 @Override
202 public String toString() {
203 if (operations == null) {
204 return "Unspecified plan";
205 } else {
206 return operations.stream().map(Object::toString).collect(Collectors.joining("\n"));
207 }
208 }
209
210}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SearchPlanForBody.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SearchPlanForBody.java
new file mode 100644
index 00000000..e0300da4
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SearchPlanForBody.java
@@ -0,0 +1,115 @@
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.localsearch.plan;
10
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collection;
14import java.util.List;
15import java.util.Map;
16import java.util.stream.Collectors;
17
18import tools.refinery.viatra.runtime.localsearch.matcher.CallWithAdornment;
19import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
20import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
21import tools.refinery.viatra.runtime.matchers.psystem.PBody;
22import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
23import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
24
25/**
26 * This class is responsible for storing the results of the planner and operation compiler for a selected body.
27 * @since 2.0
28 * @noinstantiate This class is not intended to be instantiated by clients.
29 */
30public class SearchPlanForBody {
31
32 private final PBody body;
33 private final Map<PVariable, Integer> variableKeys;
34 private final int[] parameterKeys;
35 private final SubPlan plan;
36 private final List<ISearchOperation> compiledOperations;
37 private final Collection<CallWithAdornment> dependencies;
38 private final double cost;
39 private final Object internalRepresentation;
40
41 /**
42 * @since 2.1
43 */
44 public SearchPlanForBody(PBody body, Map<PVariable, Integer> variableKeys,
45 SubPlan plan, List<ISearchOperation> compiledOperations, Collection<CallWithAdornment> dependencies,
46 Object internalRepresentation, double cost) {
47 super();
48 this.body = body;
49 this.variableKeys = variableKeys;
50 this.plan = plan;
51 this.internalRepresentation = internalRepresentation;
52 this.cost = cost;
53 List<PVariable> parameters = body.getSymbolicParameterVariables();
54 parameterKeys = new int[parameters.size()];
55 for (int i=0; i<parameters.size(); i++) {
56 parameterKeys[i] = variableKeys.get(parameters.get(i));
57 }
58 this.compiledOperations = new ArrayList<>(compiledOperations.size()+1);
59 this.compiledOperations.addAll(compiledOperations);
60
61 this.dependencies = new ArrayList<>(dependencies);
62 }
63
64 public PBody getBody() {
65 return body;
66 }
67
68 public Map<PVariable, Integer> getVariableKeys() {
69 return variableKeys;
70 }
71
72 public int[] getParameterKeys() {
73 return Arrays.copyOf(parameterKeys, parameterKeys.length);
74 }
75
76 public List<ISearchOperation> getCompiledOperations() {
77 return compiledOperations;
78 }
79
80 public SubPlan getPlan() {
81 return plan;
82 }
83
84 public Collection<CallWithAdornment> getDependencies() {
85 return dependencies;
86 }
87
88 public TupleMask calculateParameterMask() {
89 return TupleMask.fromSelectedIndices(variableKeys.size(), parameterKeys);
90 }
91
92 @Override
93 public String toString() {
94 return compiledOperations.stream().map(Object::toString).collect(Collectors.joining("\n"));
95 }
96
97 /**
98 * @since 2.1
99 */
100 public double getCost() {
101 return cost;
102 }
103
104 /**
105 * @return The internal representation of the search plan, if any, for traceability
106 * @since 2.1
107 */
108 public Object getInternalRepresentation() {
109 return internalRepresentation;
110 }
111
112
113
114
115}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SimplePlanProvider.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SimplePlanProvider.java
new file mode 100644
index 00000000..ed31bcb0
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/plan/SimplePlanProvider.java
@@ -0,0 +1,49 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.plan;
10
11import java.util.Collection;
12
13import org.apache.log4j.Logger;
14import tools.refinery.viatra.runtime.localsearch.matcher.MatcherReference;
15import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHints;
16import tools.refinery.viatra.runtime.localsearch.planner.LocalSearchPlanner;
17import tools.refinery.viatra.runtime.localsearch.planner.compiler.IOperationCompiler;
18import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
19import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
20
21/**
22 * A plan provider implementation which caches previously calculated plans to avoid re-planning for the same adornment
23 *
24 * @author Grill Balázs
25 * @since 1.7
26 *
27 */
28public class SimplePlanProvider implements IPlanProvider {
29
30 private final Logger logger;
31
32 public SimplePlanProvider(Logger logger) {
33 this.logger = logger;
34 }
35
36 @Override
37 public IPlanDescriptor getPlan(IQueryBackendContext backend, IOperationCompiler compiler,
38 final ResultProviderRequestor resultRequestor,
39 final LocalSearchHints configuration, MatcherReference key) {
40
41 LocalSearchPlanner planner = new LocalSearchPlanner(backend, compiler, logger, configuration, resultRequestor);
42
43 Collection<SearchPlanForBody> plansForBodies = planner.plan(key.getQuery(), key.getAdornment());
44
45 IPlanDescriptor plan = new PlanDescriptor(key.getQuery(), plansForBodies, key.getAdornment());
46 return plan;
47 }
48
49}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ILocalSearchPlanner.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ILocalSearchPlanner.java
new file mode 100644
index 00000000..dfd9a3c8
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ILocalSearchPlanner.java
@@ -0,0 +1,38 @@
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.localsearch.planner;
10
11import java.util.Collection;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.localsearch.plan.SearchPlanForBody;
15import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
16import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
17import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
18
19/**
20 * @author Zoltan Ujhelyi
21 * @since 1.7
22 */
23public interface ILocalSearchPlanner {
24
25 /**
26 * Creates executable plans for the provided query. It is required to call one of the
27 * <code>initializePlanner()</code> methods before calling this method.
28 *
29 * @param querySpec
30 * @param boundParameters
31 * a set of bound parameters
32 * @return a mapping between ISearchOperation list and a mapping, that holds a PVariable-Integer mapping for the
33 * list of ISearchOperations
34 * @throws ViatraQueryRuntimeException
35 */
36 Collection<SearchPlanForBody> plan(PQuery querySpec, Set<PParameter> boundParameters);
37
38} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ISearchPlanCodeGenerator.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ISearchPlanCodeGenerator.java
new file mode 100644
index 00000000..72218337
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/ISearchPlanCodeGenerator.java
@@ -0,0 +1,23 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, 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.localsearch.planner;
10
11import java.util.List;
12
13import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
14
15/**
16 * @author Marton Bur
17 *
18 */
19public interface ISearchPlanCodeGenerator {
20
21 void compile(List<List<ISearchOperation>> plans);
22
23}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchPlanner.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchPlanner.java
new file mode 100644
index 00000000..f44be655
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchPlanner.java
@@ -0,0 +1,140 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, 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.localsearch.planner;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.HashMap;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Map;
17import java.util.Set;
18
19import org.apache.log4j.Logger;
20import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHints;
21import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
22import tools.refinery.viatra.runtime.localsearch.plan.SearchPlanForBody;
23import tools.refinery.viatra.runtime.localsearch.planner.compiler.IOperationCompiler;
24import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
25import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
26import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
27import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
28import tools.refinery.viatra.runtime.matchers.psystem.PBody;
29import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
30import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
31import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
32import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
33import tools.refinery.viatra.runtime.matchers.psystem.rewriters.PBodyNormalizer;
34import tools.refinery.viatra.runtime.matchers.psystem.rewriters.PDisjunctionRewriter;
35import tools.refinery.viatra.runtime.matchers.psystem.rewriters.PDisjunctionRewriterCacher;
36import tools.refinery.viatra.runtime.matchers.psystem.rewriters.PQueryFlattener;
37
38/**
39 *
40 * @author Marton Bur
41 * @noreference This class is not intended to be referenced by clients.
42 */
43public class LocalSearchPlanner implements ILocalSearchPlanner {
44
45 // Externally set tools for planning
46 private final PDisjunctionRewriter preprocessor;
47 private final LocalSearchRuntimeBasedStrategy plannerStrategy;
48 private final IQueryRuntimeContext runtimeContext;
49 private final LocalSearchHints configuration;
50 private final IOperationCompiler operationCompiler;
51 private final IQueryBackendContext context;
52 private final ResultProviderRequestor resultRequestor;
53
54 /**
55 * @param resultRequestor
56 * @since 1.7
57 */
58 public LocalSearchPlanner(IQueryBackendContext backendContext, IOperationCompiler compiler, Logger logger,
59 final LocalSearchHints configuration, ResultProviderRequestor resultRequestor)
60 {
61
62 this.runtimeContext = backendContext.getRuntimeContext();
63 this.configuration = configuration;
64 this.operationCompiler = compiler;
65 this.resultRequestor = resultRequestor;
66 PQueryFlattener flattener = new PQueryFlattener(configuration.getFlattenCallPredicate());
67 /*
68 * TODO https://bugs.eclipse.org/bugs/show_bug.cgi?id=439358: The normalizer is initialized with the false
69 * parameter to turn off unary constraint elimination to work around an issue related to plan ordering: the
70 * current implementation of the feature target checking operations expect that the source types were checked
71 * before. However, this causes duplicate constraint checks in the search plan that might affect performance
72 * negatively.
73 */
74 PBodyNormalizer normalizer = new PBodyNormalizer(runtimeContext.getMetaContext()) {
75
76 @Override
77 protected boolean shouldCalculateImpliedTypes(PQuery query) {
78 return false;
79 }
80 };
81 preprocessor = new PDisjunctionRewriterCacher(flattener, normalizer);
82
83 plannerStrategy = new LocalSearchRuntimeBasedStrategy();
84
85 context = backendContext;
86 }
87
88 /**
89 * Creates executable plans for the provided query. It is required to call one of the
90 * <code>initializePlanner()</code> methods before calling this method.
91 *
92 * @param querySpec
93 * @param boundParameters
94 * a set of bound parameters
95 * @return a mapping between ISearchOperation list and a mapping, that holds a PVariable-Integer mapping for the
96 * list of ISearchOperations
97 */
98 @Override
99 public Collection<SearchPlanForBody> plan(PQuery querySpec, Set<PParameter> boundParameters) {
100 // 1. Preparation
101 preprocessor.setTraceCollector(configuration.getTraceCollector());
102 Set<PBody> normalizedBodies = preprocessor.rewrite(querySpec.getDisjunctBodies()).getBodies();
103
104 List<SearchPlanForBody> plansForBodies = new ArrayList<>(normalizedBodies.size());
105
106 for (PBody normalizedBody : normalizedBodies) {
107 // 2. Plan creation
108 // Context has matchers for the referred Queries (IQuerySpecifications)
109 Set<PVariable> boundVariables = calculatePatternAdornmentForPlanner(boundParameters, normalizedBody);
110 PlanState searchPlanInternal = plannerStrategy.plan(normalizedBody, boundVariables, context, resultRequestor, configuration);
111 SubPlan plan = plannerStrategy.convertPlan(boundVariables, searchPlanInternal);
112 // 3. PConstraint -> POperation compilation step
113 // * Pay extra caution to extend operations, when more than one variables are unbound
114 List<ISearchOperation> compiledOperations = operationCompiler.compile(plan, boundParameters);
115 // Store the variable mappings for the plans for debug purposes (traceability information)
116 SearchPlanForBody compiledPlan = new SearchPlanForBody(normalizedBody,
117 operationCompiler.getVariableMappings(), plan, compiledOperations,
118 operationCompiler.getDependencies(),
119 searchPlanInternal, searchPlanInternal.getCost());
120
121 plansForBodies.add(compiledPlan);
122 }
123
124 return plansForBodies;
125 }
126
127 private Set<PVariable> calculatePatternAdornmentForPlanner(Set<PParameter> boundParameters, PBody normalizedBody) {
128 Map<PParameter, PVariable> parameterMapping = new HashMap<>();
129 for (ExportedParameter constraint : normalizedBody.getSymbolicParameters()) {
130 parameterMapping.put(constraint.getPatternParameter(), constraint.getParameterVariable());
131 }
132 Set<PVariable> boundVariables = new HashSet<>();
133 for (PParameter parameter : boundParameters) {
134 PVariable mappedParameter = parameterMapping.get(parameter);
135 boundVariables.add(mappedParameter);
136 }
137 return boundVariables;
138 }
139
140}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchRuntimeBasedStrategy.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchRuntimeBasedStrategy.java
new file mode 100644
index 00000000..1bebe37e
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/LocalSearchRuntimeBasedStrategy.java
@@ -0,0 +1,257 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, 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.localsearch.planner;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.HashMap;
15import java.util.List;
16import java.util.Map;
17import java.util.Set;
18
19import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchHints;
20import tools.refinery.viatra.runtime.localsearch.planner.cost.ICostFunction;
21import tools.refinery.viatra.runtime.localsearch.planner.util.OperationCostComparator;
22import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
23import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
24import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
25import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory;
26import tools.refinery.viatra.runtime.matchers.planning.operations.PApply;
27import tools.refinery.viatra.runtime.matchers.planning.operations.PProject;
28import tools.refinery.viatra.runtime.matchers.planning.operations.PStart;
29import tools.refinery.viatra.runtime.matchers.psystem.PBody;
30import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
31import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
32import tools.refinery.viatra.runtime.matchers.util.Sets;
33
34/**
35 * This class contains the logic for local search plan calculation based on costs of the operations.
36 * Its name refers to the fact that the strategy tries to use as much information as available about
37 * the model on which the matching is initiated. When no runtime info is available, it falls back to
38 * the information available from the metamodel durint operation cost calculation.
39 *
40 * The implementation is based on the paper "Gergely Varró, Frederik Deckwerth, Martin Wieber, and Andy Schürr:
41 * An algorithm for generating model-sensitive search plans for pattern matching on EMF models"
42 * (DOI: 10.1007/s10270-013-0372-2)
43 *
44 * @author Marton Bur
45 * @noreference This class is not intended to be referenced by clients.
46 */
47public class LocalSearchRuntimeBasedStrategy {
48
49 private final OperationCostComparator infoComparator = new OperationCostComparator();
50
51
52 /**
53 * Converts a plan to the standard format
54 */
55 protected SubPlan convertPlan(Set<PVariable> initialBoundVariables, PlanState searchPlan) {
56 PBody pBody;
57 pBody = searchPlan.getAssociatedPBody();
58
59 // Create a starting plan
60 SubPlanFactory subPlanFactory = new SubPlanFactory(pBody);
61
62 // We assume that the adornment (now the bound variables) is previously set
63 SubPlan plan = subPlanFactory.createSubPlan(new PStart(initialBoundVariables));
64
65 List<PConstraintInfo> operations = searchPlan.getOperations();
66 for (PConstraintInfo pConstraintPlanInfo : operations) {
67 PConstraint pConstraint = pConstraintPlanInfo.getConstraint();
68 plan = subPlanFactory.createSubPlan(new PApply(pConstraint), plan);
69 }
70
71 return subPlanFactory.createSubPlan(new PProject(pBody.getSymbolicParameterVariables()), plan);
72 }
73
74 /**
75 * The implementation of a local search-based algorithm to create a search plan for a flattened (and normalized)
76 * PBody
77 * @param pBody for which the plan is to be created
78 * @param initialBoundVariables variables that are known to have already assigned values
79 * @param context the backend context
80 * @param resultProviderRequestor requestor for accessing result providers of called patterns
81 * @param configuration the planner configuration
82 * @return the complete search plan for the given {@link PBody}
83 * @since 2.1
84 */
85 protected PlanState plan(PBody pBody, Set<PVariable> initialBoundVariables,
86 IQueryBackendContext context, final ResultProviderRequestor resultProviderRequestor,
87 LocalSearchHints configuration) {
88 final ICostFunction costFunction = configuration.getCostFunction();
89 PConstraintInfoInferrer pConstraintInfoInferrer = new PConstraintInfoInferrer(
90 configuration.isUseBase(), context, resultProviderRequestor, costFunction::apply);
91
92 // Create mask infos
93 Set<PConstraint> constraintSet = pBody.getConstraints();
94 List<PConstraintInfo> constraintInfos =
95 pConstraintInfoInferrer.createPConstraintInfos(constraintSet);
96
97 // Calculate the characteristic function
98 // The characteristic function tells whether a given adornment is backward reachable from the (B)* state, where
99 // each variable is bound.
100 // The characteristic function is represented as a set of set of variables
101 // TODO this calculation is not not implemented yet, thus the contents of the returned set is not considered later
102 List<Set<PVariable>> reachableBoundVariableSets = reachabilityAnalysis(pBody, constraintInfos);
103 int k = configuration.getRowCount();
104 PlanState searchPlan = calculateSearchPlan(pBody, initialBoundVariables, k, reachableBoundVariableSets, constraintInfos);
105 return searchPlan;
106 }
107
108 private PlanState calculateSearchPlan(PBody pBody, Set<PVariable> initialBoundVariables, int k,
109 List<Set<PVariable>> reachableBoundVariableSets, List<PConstraintInfo> allMaskInfos) {
110
111 List<PConstraintInfo> allPotentialExtendInfos = new ArrayList<>();
112 List<PConstraintInfo> allPotentialCheckInfos = new ArrayList<>();
113 Map<PVariable, List<PConstraintInfo>> checkOpsByVariables = new HashMap<>();
114 Map<PVariable, Collection<PConstraintInfo>> extendOpsByBoundVariables = new HashMap<>();
115
116 for (PConstraintInfo op : allMaskInfos) {
117 if (op.getFreeVariables().isEmpty()) { // CHECK
118 allPotentialCheckInfos.add(op);
119 } else { // EXTEND
120 allPotentialExtendInfos.add(op);
121 for (PVariable variable : op.getBoundVariables()) {
122 extendOpsByBoundVariables.computeIfAbsent(variable, v -> new ArrayList<>()).add(op);
123 }
124 }
125 }
126 // For CHECKs only, we must start from lists that are ordered by the cost of the constraint application
127 Collections.sort(allPotentialCheckInfos, infoComparator); // costs are eagerly needed for check ops
128 for (PConstraintInfo op : allPotentialCheckInfos) {
129 for (PVariable variable : op.getBoundVariables()) {
130 checkOpsByVariables.computeIfAbsent(variable, v -> new ArrayList<>()).add(op);
131 }
132 }
133 // costs are not needed for extend ops until they are first applied (TODO make cost computaiton on demand)
134
135
136 // rename for better understanding
137 Set<PVariable> boundVariables = initialBoundVariables;
138 Set<PVariable> freeVariables = Sets.difference(pBody.getUniqueVariables(), initialBoundVariables);
139
140 int variableCount = pBody.getUniqueVariables().size();
141 int n = freeVariables.size();
142
143 List<List<PlanState>> stateTable = initializeStateTable(k, n);
144
145 // Set initial state: begin with an empty operation list
146 PlanState initialState = new PlanState(pBody, boundVariables);
147
148 // Initial state creation, categorizes all operations; add present checks to operationsList
149 initialState.updateExtends(allPotentialExtendInfos);
150 initialState.applyChecks(allPotentialCheckInfos);
151 stateTable.get(n).add(0, initialState);
152
153 // stateTable.get(0) will contain the states with adornment B*
154 for (int i = n; i > 0; i--) {
155 for (int j = 0; j < k && j < stateTable.get(i).size(); j++) {
156 PlanState currentState = stateTable.get(i).get(j);
157
158 for (PConstraintInfo constraintInfo : currentState.getPresentExtends()) {
159 // for each present EXTEND operation
160 PlanState newState = calculateNextState(currentState, constraintInfo);
161 // also eagerly perform any CHECK operations that become applicable (extends still deferred)
162 newState.applyChecksBasedOnDelta(checkOpsByVariables);
163
164 if(currentState.getBoundVariables().size() == newState.getBoundVariables().size()){
165 // This means no variable binding was done, go on with the next constraint info
166 continue;
167 }
168 int i2 = variableCount - newState.getBoundVariables().size();
169
170 List<Integer> newIndices = determineIndices(stateTable, i2, newState, k);
171 int a = newIndices.get(0);
172 int c = newIndices.get(1);
173
174 if (checkInsertCondition(stateTable.get(i2), newState, reachableBoundVariableSets, a, c, k)) {
175 updateExtends(newState, currentState, extendOpsByBoundVariables); // preprocess next steps
176 insert(stateTable,i2, newState, a, c, k);
177 }
178 }
179 }
180 }
181
182 return stateTable.get(0).get(0);
183 }
184
185 private List<List<PlanState>> initializeStateTable(int k, int n) {
186 List<List<PlanState>> stateTable = new ArrayList<>();
187 // Initialize state table and fill it with null
188 for (int i = 0; i <= n ; i++) {
189 stateTable.add(new ArrayList<>());
190 }
191 return stateTable;
192 }
193
194 private void insert(List<List<PlanState>> stateTable, int idx, PlanState newState, int a, int c, int k) {
195 stateTable.get(idx).add(c, newState);
196 while(stateTable.get(idx).size() > k){
197 // Truncate back to size k when grows too big
198 stateTable.set(idx, stateTable.get(idx).subList(0, k));
199 }
200 }
201
202 private void updateExtends(PlanState newState, PlanState currentState,
203 Map<PVariable, ? extends Collection<PConstraintInfo>> extendOpsByBoundVariables)
204 {
205 List<PConstraintInfo> presentExtends = currentState.getPresentExtends();
206
207 // Recategorize operations
208 newState.updateExtendsBasedOnDelta(presentExtends, extendOpsByBoundVariables);
209
210 return;
211 }
212
213 private boolean checkInsertCondition(List<PlanState> list, PlanState newState,
214 List<Set<PVariable>> reachableBoundVariableSets, int a, int c, int k) {
215// boolean isAmongBestK = (a == (k + 1)) && c < a && reachableBoundVariableSets.contains(newState.getBoundVariables());
216 boolean isAmongBestK = a == k && c < a ;
217 boolean isBetterThanCurrent = a < k && c <= a;
218
219 return isAmongBestK || isBetterThanCurrent;
220 }
221
222 private List<Integer> determineIndices(List<List<PlanState>> stateTable, int i2, PlanState newState, int k) {
223 int a = k;
224 int c = 0;
225 List<Integer> acIndices = new ArrayList<>();
226 for (int j = 0; j < k; j++) {
227 if (j < stateTable.get(i2).size()) {
228 PlanState stateInTable = stateTable.get(i2).get(j);
229 if (newState.getBoundVariables().equals(stateInTable.getBoundVariables())) {
230 // The new state has the same adornment as the stored one - they are not adornment disjoint
231 a = j;
232 }
233 if (newState.getCost() >= stateInTable.getCost()) {
234 c = j + 1;
235 }
236 } else {
237 break;
238 }
239 }
240
241 acIndices.add(a);
242 acIndices.add(c);
243 return acIndices;
244 }
245
246 private PlanState calculateNextState(PlanState currentState, PConstraintInfo constraintInfo) {
247 return currentState.cloneWithApplied(constraintInfo);
248 }
249
250 private List<Set<PVariable>> reachabilityAnalysis(PBody pBody, List<PConstraintInfo> constraintInfos) {
251 // TODO implement reachability analisys, also save/persist the results somewhere
252 List<Set<PVariable>> reachableBoundVariableSets = new ArrayList<>();
253 return reachableBoundVariableSets;
254 }
255
256
257}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintCategory.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintCategory.java
new file mode 100644
index 00000000..b98dd12e
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintCategory.java
@@ -0,0 +1,41 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.planner;
10
11
12/**
13 * Expresses the state of a PConstraint application
14 * condition with respect to a given adornment.
15 *
16 * @author Marton Bur
17 * @noreference This enum is not intended to be referenced by clients.
18 */
19public enum PConstraintCategory {
20 /*
21 * During plan creation an operation is considered a past
22 * operation, if an already bound variable is free in the
23 * mask of the operation.
24 * (Mask of the operation: the required binding state of
25 * the affected variables)
26 */
27 PAST,
28 /*
29 * The binding states of the variables in the operation
30 * mask correspond to the current binding states of the
31 * variables in the search plan
32 */
33 PRESENT,
34 /*
35 * There is at least one bound variable in the mask of
36 * a future operation that is still free at the current
37 * state of the plan. Also, a future operation can't be
38 * PAST.
39 */
40 FUTURE;
41}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfo.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfo.java
new file mode 100644
index 00000000..c2c76ef2
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfo.java
@@ -0,0 +1,142 @@
1/**
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Danil 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.localsearch.planner;
10
11import java.util.Collections;
12import java.util.LinkedHashSet;
13import java.util.Set;
14import java.util.function.Function;
15
16import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
17import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
18import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
19import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess;
20import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
21import tools.refinery.viatra.runtime.matchers.psystem.PBody;
22import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
23import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
24import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
25
26/**
27 * Wraps a PConstraint together with information required for the planner. Currently contains information about the expected binding state of
28 * the affected variables also called application condition, and the cost of the enforcement, based on the meta and/or the runtime context.
29 *
30 * @author Marton Bur
31 * @noreference This class is not intended to be referenced by clients.
32 */
33public class PConstraintInfo implements IConstraintEvaluationContext {
34
35 private PConstraint constraint;
36 private Set<PVariable> boundMaskVariables;
37 private Set<PVariable> freeMaskVariables;
38 private Set<PConstraintInfo> sameWithDifferentBindings;
39 private IQueryRuntimeContext runtimeContext;
40 private QueryAnalyzer queryAnalyzer;
41 private IQueryResultProviderAccess resultProviderAccess;
42 private ResultProviderRequestor resultRequestor;
43
44 private Double cost;
45 private Function<IConstraintEvaluationContext, Double> costFunction;
46
47
48 /**
49 * Instantiates the wrapper
50 * @param constraintfor which the information is added and stored
51 * @param boundMaskVariables the bound variables in the operation mask
52 * @param freeMaskVariables the free variables in the operation mask
53 * @param sameWithDifferentBindings during the planning process, multiple operation adornments are considered for a constraint, so that it
54 * is represented by multiple plan infos. This parameter contains all plan infos that are for the same
55 * constraint, but with different adornment
56 * @param context the query backend context
57 */
58 public PConstraintInfo(PConstraint constraint, Set<PVariable> boundMaskVariables, Set<PVariable> freeMaskVariables,
59 Set<PConstraintInfo> sameWithDifferentBindings,
60 IQueryBackendContext context,
61 ResultProviderRequestor resultRequestor,
62 Function<IConstraintEvaluationContext, Double> costFunction) {
63 this.constraint = constraint;
64 this.costFunction = costFunction;
65 this.boundMaskVariables = new LinkedHashSet<>(boundMaskVariables);
66 this.freeMaskVariables = new LinkedHashSet<>(freeMaskVariables);
67 this.sameWithDifferentBindings = sameWithDifferentBindings;
68 this.resultRequestor = resultRequestor;
69 this.runtimeContext = context.getRuntimeContext();
70 this.queryAnalyzer = context.getQueryAnalyzer();
71 this.resultProviderAccess = context.getResultProviderAccess();
72
73 this.cost = null; // cost will be computed lazily (esp. important for pattern calls)
74 }
75
76 @Override
77 public IQueryRuntimeContext getRuntimeContext() {
78 return runtimeContext;
79 }
80
81 @Override
82 public QueryAnalyzer getQueryAnalyzer() {
83 return queryAnalyzer;
84 }
85
86 @Override
87 public PConstraint getConstraint() {
88 return constraint;
89 }
90
91 @Override
92 public Set<PVariable> getFreeVariables() {
93 return freeMaskVariables;
94 }
95
96 @Override
97 public Set<PVariable> getBoundVariables() {
98 return boundMaskVariables;
99 }
100
101 public Set<PConstraintInfo> getSameWithDifferentBindings() {
102 return sameWithDifferentBindings;
103 }
104
105 public double getCost() {
106 if (cost == null) {
107 // Calculate cost of the constraint based on its type
108 cost = costFunction.apply(this);
109 }
110 return cost;
111 }
112
113 public PConstraintCategory getCategory(PBody pBody, Set<PVariable> boundVariables) {
114 if (!Collections.disjoint(boundVariables, this.freeMaskVariables)) {
115 return PConstraintCategory.PAST;
116 } else if (!boundVariables.containsAll(this.boundMaskVariables)) {
117 return PConstraintCategory.FUTURE;
118 } else {
119 return PConstraintCategory.PRESENT;
120 }
121 }
122
123 @Override
124 public String toString() {
125 return String.format("%s, bound variables: %s, cost: \"%.2f\"", constraint.toString(), boundMaskVariables.toString(), cost);
126 }
127
128 /**
129 * @deprecated use {@link #resultProviderRequestor()}
130 */
131 @Override
132 @Deprecated
133 public IQueryResultProviderAccess resultProviderAccess() {
134 return resultProviderAccess;
135 }
136
137 @Override
138 public ResultProviderRequestor resultProviderRequestor() {
139 return resultRequestor;
140 }
141
142}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfoInferrer.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfoInferrer.java
new file mode 100644
index 00000000..96fda930
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PConstraintInfoInferrer.java
@@ -0,0 +1,326 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.planner;
10
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collections;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Objects;
17import java.util.Set;
18import java.util.function.Function;
19import java.util.function.Predicate;
20import java.util.stream.Collectors;
21import java.util.stream.Stream;
22
23import org.eclipse.emf.ecore.EReference;
24import org.eclipse.emf.ecore.EStructuralFeature;
25import tools.refinery.viatra.runtime.base.api.BaseIndexOptions;
26import tools.refinery.viatra.runtime.base.comprehension.EMFModelComprehension;
27import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey;
28import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
29import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
30import tools.refinery.viatra.runtime.matchers.context.IInputKey;
31import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
32import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
33import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
34import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint;
35import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
36import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation;
37import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality;
38import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.PatternMatchCounter;
39import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint;
40import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.AbstractTransitiveClosure;
41import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
42import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
43import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
44import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
45import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameterDirection;
46import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
47import tools.refinery.viatra.runtime.matchers.util.Sets;
48
49
50/**
51 * @author Grill Balázs
52 * @noreference This class is not intended to be referenced by clients.
53 */
54class PConstraintInfoInferrer {
55
56 private static final Predicate<PVariable> SINGLE_USE_VARIABLE = input -> input != null && input.getReferringConstraints().size() == 1;
57
58 private final boolean useIndex;
59 private final Function<IConstraintEvaluationContext, Double> costFunction;
60 private final EMFModelComprehension modelComprehension;
61 private final IQueryBackendContext context;
62 private final ResultProviderRequestor resultRequestor;
63
64
65 public PConstraintInfoInferrer(boolean useIndex,
66 IQueryBackendContext backendContext,
67 ResultProviderRequestor resultRequestor,
68 Function<IConstraintEvaluationContext, Double> costFunction) {
69 this.useIndex = useIndex;
70 this.context = backendContext;
71 this.resultRequestor = resultRequestor;
72 this.costFunction = costFunction;
73 this.modelComprehension = new EMFModelComprehension(new BaseIndexOptions());
74 }
75
76
77 /**
78 * Create all possible application condition for all constraint
79 *
80 * @param constraintSet the set of constraints
81 * @param runtimeContext the model dependent runtime contest
82 * @return a collection of the wrapper PConstraintInfo objects with all the allowed application conditions
83 */
84 public List<PConstraintInfo> createPConstraintInfos(Set<PConstraint> constraintSet) {
85 List<PConstraintInfo> constraintInfos = new ArrayList<>();
86
87 for (PConstraint pConstraint : constraintSet) {
88 createPConstraintInfoDispatch(constraintInfos, pConstraint);
89 }
90 return constraintInfos;
91 }
92
93 private void createPConstraintInfoDispatch(List<PConstraintInfo> resultList, PConstraint pConstraint){
94 if(pConstraint instanceof ExportedParameter){
95 createConstraintInfoExportedParameter(resultList, (ExportedParameter) pConstraint);
96 } else if(pConstraint instanceof TypeConstraint){
97 createConstraintInfoTypeConstraint(resultList, (TypeConstraint)pConstraint);
98 } else if(pConstraint instanceof TypeFilterConstraint){
99 createConstraintInfoTypeFilterConstraint(resultList, (TypeFilterConstraint)pConstraint);
100 } else if(pConstraint instanceof ConstantValue){
101 createConstraintInfoConstantValue(resultList, (ConstantValue)pConstraint);
102 } else if (pConstraint instanceof Inequality){
103 createConstraintInfoInequality(resultList, (Inequality) pConstraint);
104 } else if (pConstraint instanceof ExpressionEvaluation){
105 createConstraintInfoExpressionEvaluation(resultList, (ExpressionEvaluation)pConstraint);
106 } else if (pConstraint instanceof AggregatorConstraint){
107 createConstraintInfoAggregatorConstraint(resultList, pConstraint, ((AggregatorConstraint) pConstraint).getResultVariable());
108 } else if (pConstraint instanceof PatternMatchCounter){
109 createConstraintInfoAggregatorConstraint(resultList, pConstraint, ((PatternMatchCounter) pConstraint).getResultVariable());
110 } else if (pConstraint instanceof PositivePatternCall){
111 createConstraintInfoPositivePatternCall(resultList, (PositivePatternCall) pConstraint);
112 } else if (pConstraint instanceof AbstractTransitiveClosure) {
113 createConstraintInfoBinaryTransitiveClosure(resultList, (AbstractTransitiveClosure) pConstraint);
114 } else{
115 createConstraintInfoGeneric(resultList, pConstraint);
116 }
117 }
118
119 private void createConstraintInfoConstantValue(List<PConstraintInfo> resultList,
120 ConstantValue pConstraint) {
121 // A ConstantValue constraint has a single variable, which is allowed to be unbound
122 // (extending through ConstantValue is considered a cheap operation)
123 Set<PVariable> affectedVariables = pConstraint.getAffectedVariables();
124 Set<? extends Set<PVariable>> bindings = Sets.powerSet(affectedVariables);
125 doCreateConstraintInfos(resultList, pConstraint, affectedVariables, bindings);
126 }
127
128
129 private void createConstraintInfoPositivePatternCall(List<PConstraintInfo> resultList,
130 PositivePatternCall pCall) {
131 // A pattern call can have any of its variables unbound
132 Set<PVariable> affectedVariables = pCall.getAffectedVariables();
133 // IN parameters cannot be unbound and
134 // OUT parameters cannot be bound
135 Tuple variables = pCall.getVariablesTuple();
136 final Set<PVariable> inVariables = new HashSet<>();
137 Set<PVariable> inoutVariables = new HashSet<>();
138 List<PParameter> parameters = pCall.getReferredQuery().getParameters();
139 for(int i=0;i<parameters.size();i++){
140 switch(parameters.get(i).getDirection()){
141 case IN:
142 inVariables.add((PVariable) variables.get(i));
143 break;
144 case INOUT:
145 inoutVariables.add((PVariable) variables.get(i));
146 break;
147 case OUT:
148 default:
149 break;
150
151 }
152 }
153 Iterable<Set<PVariable>> bindings = Sets.powerSet(inoutVariables).stream()
154 .map(input -> Stream.concat(input.stream(), inVariables.stream()).collect(Collectors.toSet()))
155 .collect(Collectors.toSet());
156
157 doCreateConstraintInfos(resultList, pCall, affectedVariables, bindings);
158 }
159
160 private void createConstraintInfoBinaryTransitiveClosure(List<PConstraintInfo> resultList,
161 AbstractTransitiveClosure closure) {
162 // A pattern call can have any of its variables unbound
163
164 List<PParameter> parameters = closure.getReferredQuery().getParameters();
165 Tuple variables = closure.getVariablesTuple();
166
167 Set<Set<PVariable>> bindings = new HashSet<>();
168 PVariable firstVariable = (PVariable) variables.get(0);
169 PVariable secondVariable = (PVariable) variables.get(1);
170 // Check is always supported
171 bindings.add(new HashSet<>(Arrays.asList(firstVariable, secondVariable)));
172 // If first parameter is not bound mandatorily, it can be left out
173 if (parameters.get(0).getDirection() != PParameterDirection.IN) {
174 bindings.add(Collections.singleton(secondVariable));
175 }
176 // If second parameter is not bound mandatorily, it can be left out
177 if (parameters.get(1).getDirection() != PParameterDirection.IN) {
178 bindings.add(Collections.singleton(firstVariable));
179 }
180
181 doCreateConstraintInfos(resultList, closure, closure.getAffectedVariables(), bindings);
182 }
183
184
185
186 private void createConstraintInfoExportedParameter(List<PConstraintInfo> resultList,
187 ExportedParameter parameter) {
188 // In case of an exported parameter constraint, the parameter must be bound in order to execute
189 Set<PVariable> affectedVariables = parameter.getAffectedVariables();
190 doCreateConstraintInfos(resultList, parameter, affectedVariables, Collections.singleton(affectedVariables));
191 }
192
193 private void createConstraintInfoExpressionEvaluation(List<PConstraintInfo> resultList,
194 ExpressionEvaluation expressionEvaluation) {
195 // An expression evaluation can only have its output variable unbound. All other variables shall be bound
196 PVariable output = expressionEvaluation.getOutputVariable();
197 Set<Set<PVariable>> bindings = new HashSet<>();
198 Set<PVariable> affectedVariables = expressionEvaluation.getAffectedVariables();
199 // All variables bound -> check
200 bindings.add(affectedVariables);
201 // Output variable is not bound -> extend
202 bindings.add(affectedVariables.stream().filter(var -> !Objects.equals(var, output)).collect(Collectors.toSet()));
203 doCreateConstraintInfos(resultList, expressionEvaluation, affectedVariables, bindings);
204 }
205
206 private void createConstraintInfoTypeFilterConstraint(List<PConstraintInfo> resultList,
207 TypeFilterConstraint filter){
208 // In case of type filter, all affected variables must be bound in order to execute
209 Set<PVariable> affectedVariables = filter.getAffectedVariables();
210 doCreateConstraintInfos(resultList, filter, affectedVariables, Collections.singleton(affectedVariables));
211 }
212
213 private void createConstraintInfoInequality(List<PConstraintInfo> resultList,
214 Inequality inequality){
215 // In case of inequality, all affected variables must be bound in order to execute
216 Set<PVariable> affectedVariables = inequality.getAffectedVariables();
217 doCreateConstraintInfos(resultList, inequality, affectedVariables, Collections.singleton(affectedVariables));
218 }
219
220 private void createConstraintInfoAggregatorConstraint(List<PConstraintInfo> resultList,
221 PConstraint pConstraint, PVariable resultVariable){
222 Set<PVariable> affectedVariables = pConstraint.getAffectedVariables();
223
224 // The only variables which can be unbound are single-use
225 Set<PVariable> canBeUnboundVariables =
226 Stream.concat(Stream.of(resultVariable), affectedVariables.stream().filter(SINGLE_USE_VARIABLE)).collect(Collectors.toSet());
227
228 Set<Set<PVariable>> bindings = calculatePossibleBindings(canBeUnboundVariables, affectedVariables);
229
230 doCreateConstraintInfos(resultList, pConstraint, affectedVariables, bindings);
231 }
232
233 /**
234 *
235 * @param canBeUnboundVariables Variables which are allowed to be unbound
236 * @param affectedVariables All affected variables
237 * @return The set of possible bound variable sets
238 */
239 private Set<Set<PVariable>> calculatePossibleBindings(Set<PVariable> canBeUnboundVariables, Set<PVariable> affectedVariables){
240 final Set<PVariable> mustBindVariables = affectedVariables.stream().filter(input -> !canBeUnboundVariables.contains(input)).collect(Collectors.toSet());
241 return Sets.powerSet(canBeUnboundVariables).stream()
242 .map(input -> {
243 //some variables have to be bound before executing this constraint
244 Set<PVariable> result= new HashSet<>(input);
245 result.addAll(mustBindVariables);
246 return result;
247 })
248 .collect(Collectors.toSet());
249 }
250
251 private void createConstraintInfoGeneric(List<PConstraintInfo> resultList, PConstraint pConstraint){
252 Set<PVariable> affectedVariables = pConstraint.getAffectedVariables();
253
254 // The only variables which can be unbound are single use variables
255 Set<PVariable> canBeUnboundVariables = affectedVariables.stream().filter(SINGLE_USE_VARIABLE).collect(Collectors.toSet());
256
257 Set<Set<PVariable>> bindings = calculatePossibleBindings(canBeUnboundVariables, affectedVariables);
258
259 doCreateConstraintInfos(resultList, pConstraint, affectedVariables, bindings);
260 }
261
262 private boolean canPerformInverseNavigation(EStructuralFeature feature){
263 return ( // Feature has opposite (this only possible for references)
264 hasEOpposite(feature)
265 ||
266 (feature instanceof EReference) && ((EReference)feature).isContainment()
267 || ( // Indexing is enabled, and the feature can be indexed (not a non-well-behaving derived feature).
268 useIndex && modelComprehension.representable(feature)
269 ));
270 }
271
272 private void createConstraintInfoTypeConstraint(List<PConstraintInfo> resultList,
273 TypeConstraint typeConstraint) {
274 Set<PVariable> affectedVariables = typeConstraint.getAffectedVariables();
275 Set<? extends Set<PVariable>> bindings = null;
276
277 IInputKey inputKey = typeConstraint.getSupplierKey();
278 if(inputKey.isEnumerable()){
279 bindings = Sets.powerSet(affectedVariables);
280 }else{
281 // For not enumerable types, this constraint can only be a check
282 bindings = Collections.singleton(affectedVariables);
283 }
284
285 if(inputKey instanceof EStructuralFeatureInstancesKey){
286 final EStructuralFeature feature = ((EStructuralFeatureInstancesKey) inputKey).getEmfKey();
287 if(!canPerformInverseNavigation(feature)){
288 // When inverse navigation is not allowed or not possible, filter out operation masks, where
289 // the first variable would be free AND the feature is an EReference and has no EOpposite
290 bindings = excludeUnnavigableOperationMasks(typeConstraint, bindings);
291 }
292 }
293 doCreateConstraintInfos(resultList, typeConstraint, affectedVariables, bindings);
294 }
295
296 private void doCreateConstraintInfos(List<PConstraintInfo> constraintInfos,
297 PConstraint pConstraint, Set<PVariable> affectedVariables, Iterable<? extends Set<PVariable>> bindings) {
298 Set<PConstraintInfo> sameWithDifferentBindings = new HashSet<>();
299 for (Set<PVariable> boundVariables : bindings) {
300
301 PConstraintInfo info = new PConstraintInfo(pConstraint, boundVariables,
302 affectedVariables.stream().filter(input -> !boundVariables.contains(input)).collect(Collectors.toSet()),
303 sameWithDifferentBindings, context, resultRequestor, costFunction);
304 constraintInfos.add(info);
305 sameWithDifferentBindings.add(info);
306 }
307 }
308
309 private Set<Set<PVariable>> excludeUnnavigableOperationMasks(TypeConstraint typeConstraint, Set<? extends Set<PVariable>> bindings) {
310 PVariable firstVariable = typeConstraint.getVariableInTuple(0);
311 return bindings.stream().filter(
312 boundVariablesSet -> (boundVariablesSet.isEmpty() || boundVariablesSet.contains(firstVariable)))
313 .collect(Collectors.toSet());
314 }
315
316 private boolean hasEOpposite(EStructuralFeature feature) {
317 if(feature instanceof EReference){
318 EReference eOpposite = ((EReference) feature).getEOpposite();
319 if(eOpposite != null){
320 return true;
321 }
322 }
323 return false;
324 }
325
326}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PlanState.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PlanState.java
new file mode 100644
index 00000000..e93b07bc
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/PlanState.java
@@ -0,0 +1,283 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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.localsearch.planner;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Comparator;
15import java.util.HashSet;
16import java.util.List;
17import java.util.Map;
18import java.util.Set;
19
20import tools.refinery.viatra.runtime.localsearch.planner.util.OperationCostComparator;
21import tools.refinery.viatra.runtime.matchers.algorithms.OrderedIterableMerge;
22import tools.refinery.viatra.runtime.matchers.psystem.PBody;
23import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
24import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
25
26/**
27 * This class represents the state of the plan during planning.
28 *
29 * <p> A PlanState represents a sequence of operations (operationsList) and caches the computed cost
30 * for this operation sequence. The list and the cost are initialized in the constructor.
31 * However, #categorizeChecks() also updates the operations list (by suffixing checks)
32 *
33 * @author Marton Bur
34 * @noreference This class is not intended to be referenced by clients.
35 */
36public class PlanState {
37
38 private final PBody pBody;
39 private final List<PConstraintInfo> operationsList;
40 private final Set<PVariable> boundVariables;
41 private final Collection<PVariable> deltaVariables; /* bound since ancestor plan */
42 private final Set<PConstraint> enforcedConstraints;
43
44 private double cummulativeProduct;
45 private double cost;
46
47 private static Comparator<PConstraintInfo> infoComparator = new OperationCostComparator();
48
49 /*
50 * For a short explanation of past, present and future operations,
51 * see class
52 */
53 private List<PConstraintInfo> presentExtends;
54
55 /**
56 * Creates an initial state
57 */
58 public PlanState(PBody pBody, Set<PVariable> boundVariables) {
59
60 this(pBody, new ArrayList<>(), boundVariables, boundVariables /* also the delta */,
61 0.0 /* initial cost */, 1.0 /*initial branch count */);
62 }
63
64 public PlanState cloneWithApplied(PConstraintInfo op) {
65 // Create operation list based on the current state
66 ArrayList<PConstraintInfo> newOperationsList =
67 // pre-reserve excess capacity for later addition of CHECK ops
68 new ArrayList<>(pBody.getConstraints().size());
69 newOperationsList.addAll(this.getOperations());
70 newOperationsList.add(op);
71
72 // Bind the variables of the op
73 Collection<PVariable> deltaVariables = op.getFreeVariables();
74 Set<PVariable> allBoundVariables =
75 // pre-reserve exact capacity as variables are known
76 // (will not be affected by adding CHECK ops later)
77 new HashSet<>(this.getBoundVariables().size() + deltaVariables.size());
78 allBoundVariables.addAll(this.getBoundVariables());
79 allBoundVariables.addAll(deltaVariables);
80
81 PlanState newState = new PlanState(getAssociatedPBody(), newOperationsList, allBoundVariables, deltaVariables,
82 cost, cummulativeProduct);
83 newState.accountNewOperation(op);
84 return newState;
85 }
86
87 private PlanState(PBody pBody, List<PConstraintInfo> operationsList,
88 Set<PVariable> boundVariables, Collection<PVariable> deltaVariables,
89 double cost, double cummulativeProduct)
90 {
91 this.pBody = pBody;
92 this.operationsList = operationsList;
93 this.boundVariables = boundVariables;
94 this.enforcedConstraints = new HashSet<>();
95 this.deltaVariables = deltaVariables;
96 this.cost = cost;
97 this.cummulativeProduct = cummulativeProduct;
98 }
99
100 // NOT included for EXTEND: bind all variables of op
101 private void accountNewOperation(PConstraintInfo constraintInfo) {
102 this.enforcedConstraints.add(constraintInfo.getConstraint());
103 accountCost(constraintInfo);
104 }
105
106 private void accountCost(PConstraintInfo constraintInfo) {
107 double constraintCost = constraintInfo.getCost();
108 double branchFactor = constraintCost;
109 if (constraintCost > 0){
110 cost += cummulativeProduct * constraintCost;
111 cummulativeProduct *= branchFactor;
112 }
113 }
114
115
116 public Set<PConstraint> getEnforcedConstraints() {
117 return enforcedConstraints;
118 }
119
120 /**
121 * Re-categorizes given extend operations into already applied or no longer applicable ones (discarded),
122 * immediately applicable ones (saved as presently viable extends),
123 * and not yet applicable ones (discarded).
124 *
125 * @param allPotentialExtendInfos all other extends that may be applicable
126 * to this plan state now or in the future;
127 * MUST consist of "extend" constraint applications only (at least one free variable)
128 */
129 public void updateExtends(Iterable<PConstraintInfo> allPotentialExtendInfos) {
130 presentExtends = new ArrayList<>();
131
132
133 // categorize future/present extend constraint infos
134 for (PConstraintInfo op : allPotentialExtendInfos) {
135 updateExtendInternal(op);
136 }
137 }
138
139 /**
140 * Re-categorizes given extend operations into already applied or no longer applicable ones (discarded),
141 * immediately applicable ones (saved as presently viable extends),
142 * and not yet applicable ones (discarded).
143 *
144 * @param extendOpsByBoundVariables all EXTEND operations indexed by affected <i>bound</i> variables
145 * MUST consist of "extend" constraint applications only (at least one free variable)
146 */
147 public void updateExtendsBasedOnDelta(
148 Iterable<PConstraintInfo> previousPresentExtends,
149 Map<PVariable, ? extends Collection<PConstraintInfo>> extendOpsByBoundVariables)
150 {
151 presentExtends = new ArrayList<>();
152 if (operationsList.isEmpty())
153 throw new IllegalStateException("Not applicable as starting step");
154
155 for (PConstraintInfo extend: previousPresentExtends) {
156 updateExtendInternal(extend);
157 }
158
159 Set<PConstraintInfo> affectedExtends = new HashSet<>();
160 for (PVariable variable : deltaVariables) {
161 // only those check ops may become applicable that have an affected variable in the delta
162 Collection<PConstraintInfo> extendsForVariable = extendOpsByBoundVariables.get(variable);
163 if (null != extendsForVariable) {
164 affectedExtends.addAll(extendsForVariable);
165 }
166 }
167 for (PConstraintInfo extend: affectedExtends) {
168 updateExtendInternal(extend);
169 }
170 }
171
172 private void updateExtendInternal(PConstraintInfo op) {
173 if(!enforcedConstraints.contains(op.getConstraint())) {
174 categorizeExtend(op);
175 }
176 }
177
178 /**
179 * Check operations that newly became applicable (see {@link #getDeltaVariables()})
180 * are appended to operations lists.
181 *
182 * <p> Will never discover degenerate checks (of PConstraints with zero variables),
183 * so must not use on initial state.
184 *
185 * @param allPotentialCheckInfos all CHECK operations
186 * MUST consist of "check" constraint applications only (no free variables)
187 * and must be iterable in decreasing order of cost
188 *
189 *
190 */
191 public void applyChecks(List<PConstraintInfo> allPotentialCheckInfos) {
192 applyChecksInternal(allPotentialCheckInfos);
193 }
194
195 /**
196 * Immediately applicable checks are appended to operations lists.
197 *
198 * @param checkOpsByVariables all CHECK operations indexed by affected variables
199 * MUST consist of "check" constraint applications only (no free variables)
200 * and each bucket must be iterable in decreasing order of cost
201 */
202 public void applyChecksBasedOnDelta(Map<PVariable, List<PConstraintInfo>> checkOpsByVariables) {
203 if (operationsList.isEmpty())
204 throw new IllegalStateException("Not applicable as starting step");
205
206 Iterable<PConstraintInfo> affectedChecks = Collections.emptyList();
207
208 for (PVariable variable : deltaVariables) {
209 // only those check ops may become applicable that have an affected variable in the delta
210 List<PConstraintInfo> checksForVariable = checkOpsByVariables.get(variable);
211 if (null != checksForVariable) {
212 affectedChecks = OrderedIterableMerge.mergeUniques(affectedChecks, checksForVariable, infoComparator);
213 }
214 }
215
216 // checks retain their order, no re-sorting needed
217 applyChecksInternal(affectedChecks);
218 }
219
220 private void applyChecksInternal(Iterable<PConstraintInfo> checks) {
221 for (PConstraintInfo checkInfo : checks) {
222 if (this.boundVariables.containsAll(checkInfo.getBoundVariables()) &&
223 !enforcedConstraints.contains(checkInfo.getConstraint()))
224 {
225 operationsList.add(checkInfo);
226 accountNewOperation(checkInfo);
227 }
228 }
229 }
230
231
232 private void categorizeExtend(PConstraintInfo constraintInfo) {
233 PConstraintCategory category = constraintInfo.getCategory(pBody, boundVariables);
234 if (category == PConstraintCategory.PRESENT) {
235 presentExtends.add(constraintInfo);
236 } else {
237 // do not categorize past/future operations
238 }
239 }
240
241
242 public PBody getAssociatedPBody() {
243 return pBody;
244 }
245
246 public List<PConstraintInfo> getOperations() {
247 return operationsList;
248 }
249
250 public Set<PVariable> getBoundVariables() {
251 return boundVariables;
252 }
253
254 /**
255 * @return the derived cost of the plan contained in the state
256 */
257 public double getCost() {
258 return cost;
259 }
260
261
262 /**
263 * @return cumulative branching factor
264 * @since 2.1
265 */
266 public double getCummulativeProduct() {
267 return cummulativeProduct;
268 }
269
270 public List<PConstraintInfo> getPresentExtends() {
271 return presentExtends;
272 }
273
274 /**
275 * Contains only those variables that are added by the newest extend
276 * (or the initially bound ones if no extend yet)
277 */
278 public Collection<PVariable> getDeltaVariables() {
279 return deltaVariables;
280 }
281
282
283}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/AbstractOperationCompiler.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/AbstractOperationCompiler.java
new file mode 100644
index 00000000..73312dc9
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/AbstractOperationCompiler.java
@@ -0,0 +1,430 @@
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.localsearch.planner.compiler;
10
11import java.util.ArrayList;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Map;
16import java.util.Set;
17import java.util.stream.Collectors;
18import java.util.stream.Stream;
19
20import tools.refinery.viatra.runtime.localsearch.matcher.CallWithAdornment;
21import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
22import tools.refinery.viatra.runtime.localsearch.operations.check.AggregatorCheck;
23import tools.refinery.viatra.runtime.localsearch.operations.check.BinaryTransitiveClosureCheck;
24import tools.refinery.viatra.runtime.localsearch.operations.check.CheckConstant;
25import tools.refinery.viatra.runtime.localsearch.operations.check.CheckPositivePatternCall;
26import tools.refinery.viatra.runtime.localsearch.operations.check.CountCheck;
27import tools.refinery.viatra.runtime.localsearch.operations.check.ExpressionCheck;
28import tools.refinery.viatra.runtime.localsearch.operations.check.ExpressionEvalCheck;
29import tools.refinery.viatra.runtime.localsearch.operations.check.InequalityCheck;
30import tools.refinery.viatra.runtime.localsearch.operations.check.NACOperation;
31import tools.refinery.viatra.runtime.localsearch.operations.extend.AggregatorExtend;
32import tools.refinery.viatra.runtime.localsearch.operations.extend.CountOperation;
33import tools.refinery.viatra.runtime.localsearch.operations.extend.ExpressionEval;
34import tools.refinery.viatra.runtime.localsearch.operations.extend.ExtendBinaryTransitiveClosure;
35import tools.refinery.viatra.runtime.localsearch.operations.extend.ExtendConstant;
36import tools.refinery.viatra.runtime.localsearch.operations.extend.ExtendPositivePatternCall;
37import tools.refinery.viatra.runtime.localsearch.operations.util.CallInformation;
38import tools.refinery.viatra.runtime.localsearch.planner.util.CompilerHelper;
39import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
40import tools.refinery.viatra.runtime.matchers.context.IInputKey;
41import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
42import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
43import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
44import tools.refinery.viatra.runtime.matchers.planning.operations.PApply;
45import tools.refinery.viatra.runtime.matchers.planning.operations.POperation;
46import tools.refinery.viatra.runtime.matchers.planning.operations.PProject;
47import tools.refinery.viatra.runtime.matchers.planning.operations.PStart;
48import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
49import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
50import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint;
51import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
52import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation;
53import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality;
54import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.NegativePatternCall;
55import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.PatternMatchCounter;
56import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint;
57import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryReflexiveTransitiveClosure;
58import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
59import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
60import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
61import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
62import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
63
64/**
65 * @author Zoltan Ujhelyi
66 * @since 1.7
67 *
68 */
69public abstract class AbstractOperationCompiler implements IOperationCompiler {
70
71 protected static final String UNSUPPORTED_TYPE_MESSAGE = "Unsupported type: ";
72
73 protected abstract void createExtend(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping);
74
75 /**
76 * @throws ViatraQueryRuntimeException
77 */
78 protected abstract void createCheck(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping);
79
80 /**
81 * @throws ViatraQueryRuntimeException
82 */
83 protected abstract void createCheck(TypeFilterConstraint typeConstraint, Map<PVariable, Integer> variableMapping);
84
85 /**
86 * @since 2.0
87 * @throws ViatraQueryRuntimeException
88 */
89 protected abstract void createUnaryTypeCheck(IInputKey type, int position);
90
91 protected List<ISearchOperation> operations;
92 protected Set<CallWithAdornment> dependencies = new HashSet<>();
93 protected Map<PConstraint, Set<Integer>> variableBindings;
94 private Map<PVariable, Integer> variableMappings;
95 protected final IQueryRuntimeContext runtimeContext;
96
97 public AbstractOperationCompiler(IQueryRuntimeContext runtimeContext) {
98 this.runtimeContext = runtimeContext;
99 }
100
101 /**
102 * Compiles a plan of <code>POperation</code>s to a list of type <code>List&ltISearchOperation></code>
103 *
104 * @param plan
105 * @param boundParameters
106 * @return an ordered list of POperations that make up the compiled search plan
107 * @throws ViatraQueryRuntimeException
108 */
109 @Override
110 public List<ISearchOperation> compile(SubPlan plan, Set<PParameter> boundParameters) {
111
112 variableMappings = CompilerHelper.createVariableMapping(plan);
113 variableBindings = CompilerHelper.cacheVariableBindings(plan, variableMappings, boundParameters);
114
115 operations = new ArrayList<>();
116
117 List<POperation> operationList = CompilerHelper.createOperationsList(plan);
118 for (POperation pOperation : operationList) {
119 compile(pOperation, variableMappings);
120 }
121
122 return operations;
123 }
124
125 private void compile(POperation pOperation, Map<PVariable, Integer> variableMapping) {
126
127 if (pOperation instanceof PApply) {
128 PApply pApply = (PApply) pOperation;
129 PConstraint pConstraint = pApply.getPConstraint();
130
131 if (isCheck(pConstraint, variableMapping)) {
132 // check
133 createCheckDispatcher(pConstraint, variableMapping);
134 } else {
135 // extend
136 createExtendDispatcher(pConstraint, variableMapping);
137 }
138
139 } else if (pOperation instanceof PStart) {
140 // nop
141 } else if (pOperation instanceof PProject) {
142 // nop
143 } else {
144 throw new QueryProcessingException("PStart, PApply or PProject was expected, received: " + pOperation.getClass(), null,"Unexpected POperation type", null);
145 }
146
147 }
148
149 private void createCheckDispatcher(PConstraint pConstraint, Map<PVariable, Integer> variableMapping) {
150
151
152 // DeferredPConstraint subclasses
153
154 // Equalities are normalized
155
156 if (pConstraint instanceof Inequality) {
157 createCheck((Inequality) pConstraint, variableMapping);
158 } else if (pConstraint instanceof PositivePatternCall){
159 createCheck((PositivePatternCall) pConstraint, variableMapping);
160 } else if (pConstraint instanceof NegativePatternCall) {
161 createCheck((NegativePatternCall) pConstraint,variableMapping);
162 } else if (pConstraint instanceof AggregatorConstraint) {
163 createCheck((AggregatorConstraint) pConstraint, variableMapping);
164 } else if (pConstraint instanceof PatternMatchCounter) {
165 createCheck((PatternMatchCounter) pConstraint, variableMapping);
166 } else if (pConstraint instanceof ExpressionEvaluation) {
167 createCheck((ExpressionEvaluation) pConstraint, variableMapping);
168 } else if (pConstraint instanceof TypeFilterConstraint) {
169 createCheck((TypeFilterConstraint) pConstraint,variableMapping);
170 } else if (pConstraint instanceof ExportedParameter) {
171 // Nothing to do here
172 } else
173
174 // EnumerablePConstraint subclasses
175
176 if (pConstraint instanceof BinaryTransitiveClosure) {
177 createCheck((BinaryTransitiveClosure) pConstraint, variableMapping);
178 } else if (pConstraint instanceof BinaryReflexiveTransitiveClosure) {
179 createCheck((BinaryReflexiveTransitiveClosure)pConstraint, variableMapping);
180 } else if (pConstraint instanceof ConstantValue) {
181 createCheck((ConstantValue) pConstraint, variableMapping);
182 } else if (pConstraint instanceof TypeConstraint) {
183 createCheck((TypeConstraint) pConstraint,variableMapping);
184 } else {
185 String msg = "Unsupported Check constraint: "+pConstraint.toString();
186 throw new QueryProcessingException(msg, null, msg, null);
187 }
188
189 }
190
191 protected void createExtendDispatcher(PConstraint pConstraint, Map<PVariable, Integer> variableMapping) {
192
193 // DeferredPConstraint subclasses
194
195 // Equalities are normalized
196 if (pConstraint instanceof PositivePatternCall) {
197 createExtend((PositivePatternCall)pConstraint, variableMapping);
198 } else if (pConstraint instanceof AggregatorConstraint) {
199 createExtend((AggregatorConstraint) pConstraint, variableMapping);
200 } else if (pConstraint instanceof PatternMatchCounter) {
201 createExtend((PatternMatchCounter) pConstraint, variableMapping);
202 } else if (pConstraint instanceof ExpressionEvaluation) {
203 createExtend((ExpressionEvaluation) pConstraint, variableMapping);
204 } else if (pConstraint instanceof ExportedParameter) {
205 // ExportedParameters are compiled to NOP
206 } else
207
208 // EnumerablePConstraint subclasses
209
210 if (pConstraint instanceof ConstantValue) {
211 createExtend((ConstantValue) pConstraint, variableMapping);
212 } else if (pConstraint instanceof TypeConstraint) {
213 createExtend((TypeConstraint) pConstraint, variableMapping);
214 } else if (pConstraint instanceof BinaryTransitiveClosure) {
215 createExtend((BinaryTransitiveClosure)pConstraint, variableMapping);
216 } else if (pConstraint instanceof BinaryReflexiveTransitiveClosure) {
217 createExtend((BinaryReflexiveTransitiveClosure)pConstraint, variableMapping);
218 } else {
219 String msg = "Unsupported Extend constraint: "+pConstraint.toString();
220 throw new QueryProcessingException(msg, null, msg, null);
221 }
222 }
223
224 private boolean isCheck(PConstraint pConstraint, final Map<PVariable, Integer> variableMapping) {
225 if (pConstraint instanceof NegativePatternCall){
226 return true;
227 }else if (pConstraint instanceof PositivePatternCall){
228 // Positive pattern call is check if all non-single used variables are bound
229 List<Integer> callVariables = pConstraint.getAffectedVariables().stream()
230 .filter(input -> input.getReferringConstraints().size() > 1)
231 .map(variableMapping::get)
232 .collect(Collectors.toList());
233 return variableBindings.get(pConstraint).containsAll(callVariables);
234 }else if (pConstraint instanceof AggregatorConstraint){
235 PVariable outputvar = ((AggregatorConstraint) pConstraint).getResultVariable();
236 return variableBindings.get(pConstraint).contains(variableMapping.get(outputvar));
237 }else if (pConstraint instanceof PatternMatchCounter){
238 PVariable outputvar = ((PatternMatchCounter) pConstraint).getResultVariable();
239 return variableBindings.get(pConstraint).contains(variableMapping.get(outputvar));
240 }else if (pConstraint instanceof ExpressionEvaluation){
241 PVariable outputvar = ((ExpressionEvaluation) pConstraint).getOutputVariable();
242 return outputvar == null || variableBindings.get(pConstraint).contains(variableMapping.get(outputvar));
243 } else {
244 // In other cases, all variables shall be bound to be a check
245 Set<PVariable> affectedVariables = pConstraint.getAffectedVariables();
246 Set<Integer> varIndices = new HashSet<>();
247 for (PVariable variable : affectedVariables) {
248 varIndices.add(variableMapping.get(variable));
249 }
250 return variableBindings.get(pConstraint).containsAll(varIndices);
251 }
252 }
253
254 @Override
255 public Set<CallWithAdornment> getDependencies() {
256 return dependencies;
257 }
258
259 /**
260 * @return the cached variable bindings for the previously created plan
261 */
262 @Override
263 public Map<PVariable, Integer> getVariableMappings() {
264 return variableMappings;
265 }
266
267 protected void createCheck(PatternMatchCounter counter, Map<PVariable, Integer> variableMapping) {
268 CallInformation information = CallInformation.create(counter, variableMapping, variableBindings.get(counter));
269 operations.add(new CountCheck(information, variableMapping.get(counter.getResultVariable())));
270 dependencies.add(information.getCallWithAdornment());
271 }
272
273 protected void createCheck(PositivePatternCall pCall, Map<PVariable, Integer> variableMapping) {
274 CallInformation information = CallInformation.create(pCall, variableMapping, variableBindings.get(pCall));
275 operations.add(new CheckPositivePatternCall(information));
276 dependencies.add(information.getCallWithAdornment());
277 }
278
279 protected void createCheck(ConstantValue constant, Map<PVariable, Integer> variableMapping) {
280 int position = variableMapping.get(constant.getVariablesTuple().get(0));
281 operations.add(new CheckConstant(position, constant.getSupplierKey()));
282 }
283
284 protected void createCheck(BinaryTransitiveClosure binaryTransitiveClosure, Map<PVariable, Integer> variableMapping) {
285 int sourcePosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(0));
286 int targetPosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(1));
287
288 //The second parameter is NOT bound during execution!
289 CallInformation information = CallInformation.create(binaryTransitiveClosure, variableMapping, Stream.of(sourcePosition).collect(Collectors.toSet()));
290 operations.add(new BinaryTransitiveClosureCheck(information, sourcePosition, targetPosition, false));
291 dependencies.add(information.getCallWithAdornment());
292 }
293
294 /**
295 * @since 2.0
296 */
297 protected void createCheck(BinaryReflexiveTransitiveClosure binaryTransitiveClosure, Map<PVariable, Integer> variableMapping) {
298 int sourcePosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(0));
299 int targetPosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(1));
300
301 //The second parameter is NOT bound during execution!
302 CallInformation information = CallInformation.create(binaryTransitiveClosure, variableMapping, Stream.of(sourcePosition).collect(Collectors.toSet()));
303 createUnaryTypeCheck(binaryTransitiveClosure.getUniverseType(), sourcePosition);
304 operations.add(new BinaryTransitiveClosureCheck(information, sourcePosition, targetPosition, true));
305 dependencies.add(information.getCallWithAdornment());
306 }
307
308 protected void createCheck(ExpressionEvaluation expressionEvaluation, Map<PVariable, Integer> variableMapping) {
309 // Fill unbound variables with null; simply copy all variables. Unbound variables will be null anyway
310 Iterable<String> inputParameterNames = expressionEvaluation.getEvaluator().getInputParameterNames();
311 Map<String, Integer> nameMap = new HashMap<>();
312
313 for (String pVariableName : inputParameterNames) {
314 PVariable pVariable = expressionEvaluation.getPSystem().getVariableByNameChecked(pVariableName);
315 nameMap.put(pVariableName, variableMapping.get(pVariable));
316 }
317
318 // output variable can be null; if null it is an ExpressionCheck
319 if(expressionEvaluation.getOutputVariable() == null){
320 operations.add(new ExpressionCheck(expressionEvaluation.getEvaluator(), nameMap));
321 } else {
322 operations.add(new ExpressionEvalCheck(expressionEvaluation.getEvaluator(), nameMap, expressionEvaluation.isUnwinding(), variableMapping.get(expressionEvaluation.getOutputVariable())));
323 }
324 }
325
326 protected void createCheck(AggregatorConstraint aggregator, Map<PVariable, Integer> variableMapping) {
327 CallInformation information = CallInformation.create(aggregator, variableMapping, variableBindings.get(aggregator));
328 operations.add(new AggregatorCheck(information, aggregator, variableMapping.get(aggregator.getResultVariable())));
329 dependencies.add(information.getCallWithAdornment());
330 }
331
332 protected void createCheck(NegativePatternCall negativePatternCall, Map<PVariable, Integer> variableMapping) {
333 CallInformation information = CallInformation.create(negativePatternCall, variableMapping, variableBindings.get(negativePatternCall));
334 operations.add(new NACOperation(information));
335 dependencies.add(information.getCallWithAdornment());
336 }
337
338 protected void createCheck(Inequality inequality, Map<PVariable, Integer> variableMapping) {
339 operations.add(new InequalityCheck(variableMapping.get(inequality.getWho()), variableMapping.get(inequality.getWithWhom())));
340 }
341
342 protected void createExtend(PositivePatternCall pCall, Map<PVariable, Integer> variableMapping) {
343 CallInformation information = CallInformation.create(pCall, variableMapping, variableBindings.get(pCall));
344 operations.add(new ExtendPositivePatternCall(information));
345 dependencies.add(information.getCallWithAdornment());
346 }
347
348 protected void createExtend(BinaryTransitiveClosure binaryTransitiveClosure, Map<PVariable, Integer> variableMapping) {
349 int sourcePosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(0));
350 int targetPosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(1));
351
352 boolean sourceBound = variableBindings.get(binaryTransitiveClosure).contains(sourcePosition);
353 boolean targetBound = variableBindings.get(binaryTransitiveClosure).contains(targetPosition);
354
355 CallInformation information = CallInformation.create(binaryTransitiveClosure, variableMapping, variableBindings.get(binaryTransitiveClosure));
356
357 if (sourceBound && !targetBound) {
358 operations.add(new ExtendBinaryTransitiveClosure.Forward(information, sourcePosition, targetPosition, false));
359 dependencies.add(information.getCallWithAdornment());
360 } else if (!sourceBound && targetBound) {
361 operations.add(new ExtendBinaryTransitiveClosure.Backward(information, sourcePosition, targetPosition, false));
362 dependencies.add(information.getCallWithAdornment());
363 } else {
364 String msg = "Binary transitive closure not supported with two unbound parameters";
365 throw new QueryProcessingException(msg, null, msg, binaryTransitiveClosure.getPSystem().getPattern());
366 }
367 }
368
369 /**
370 * @since 2.0
371 */
372 protected void createExtend(BinaryReflexiveTransitiveClosure binaryTransitiveClosure, Map<PVariable, Integer> variableMapping) {
373 int sourcePosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(0));
374 int targetPosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(1));
375
376 boolean sourceBound = variableBindings.get(binaryTransitiveClosure).contains(sourcePosition);
377 boolean targetBound = variableBindings.get(binaryTransitiveClosure).contains(targetPosition);
378
379 CallInformation information = CallInformation.create(binaryTransitiveClosure, variableMapping, variableBindings.get(binaryTransitiveClosure));
380
381 if (sourceBound && !targetBound) {
382 createUnaryTypeCheck(binaryTransitiveClosure.getUniverseType(), sourcePosition);
383 operations.add(new ExtendBinaryTransitiveClosure.Forward(information, sourcePosition, targetPosition, true));
384 dependencies.add(information.getCallWithAdornment());
385 } else if (!sourceBound && targetBound) {
386 createUnaryTypeCheck(binaryTransitiveClosure.getUniverseType(), targetPosition);
387 operations.add(new ExtendBinaryTransitiveClosure.Backward(information, sourcePosition, targetPosition, true));
388 dependencies.add(information.getCallWithAdornment());
389 } else {
390 String msg = "Binary reflective transitive closure not supported with two unbound parameters";
391 throw new QueryProcessingException(msg, null, msg, binaryTransitiveClosure.getPSystem().getPattern());
392 }
393 }
394
395 protected void createExtend(ConstantValue constant, Map<PVariable, Integer> variableMapping) {
396 int position = variableMapping.get(constant.getVariablesTuple().get(0));
397 operations.add(new ExtendConstant(position, constant.getSupplierKey()));
398 }
399
400 protected void createExtend(ExpressionEvaluation expressionEvaluation, Map<PVariable, Integer> variableMapping) {
401 // Fill unbound variables with null; simply copy all variables. Unbound variables will be null anyway
402 Iterable<String> inputParameterNames = expressionEvaluation.getEvaluator().getInputParameterNames();
403 Map<String, Integer> nameMap = new HashMap<>();
404
405 for (String pVariableName : inputParameterNames) {
406 PVariable pVariable = expressionEvaluation.getPSystem().getVariableByNameChecked(pVariableName);
407 nameMap.put(pVariableName, variableMapping.get(pVariable));
408 }
409
410 // output variable can be null; if null it is an ExpressionCheck
411 if(expressionEvaluation.getOutputVariable() == null){
412 operations.add(new ExpressionCheck(expressionEvaluation.getEvaluator(), nameMap));
413 } else {
414 operations.add(new ExpressionEval(expressionEvaluation.getEvaluator(), nameMap, expressionEvaluation.isUnwinding(), variableMapping.get(expressionEvaluation.getOutputVariable())));
415 }
416 }
417
418 protected void createExtend(AggregatorConstraint aggregator, Map<PVariable, Integer> variableMapping) {
419 CallInformation information = CallInformation.create(aggregator, variableMapping, variableBindings.get(aggregator));
420 operations.add(new AggregatorExtend(information, aggregator, variableMapping.get(aggregator.getResultVariable())));
421 dependencies.add(information.getCallWithAdornment());
422 }
423
424 protected void createExtend(PatternMatchCounter patternMatchCounter, Map<PVariable, Integer> variableMapping) {
425 CallInformation information = CallInformation.create(patternMatchCounter, variableMapping, variableBindings.get(patternMatchCounter));
426 operations.add(new CountOperation(information, variableMapping.get(patternMatchCounter.getResultVariable())));
427 dependencies.add(information.getCallWithAdornment());
428 }
429
430} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/EMFOperationCompiler.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/EMFOperationCompiler.java
new file mode 100644
index 00000000..7fd86f3c
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/EMFOperationCompiler.java
@@ -0,0 +1,198 @@
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.localsearch.planner.compiler;
10
11import java.util.Map;
12
13import org.eclipse.emf.ecore.EReference;
14import org.eclipse.emf.ecore.EStructuralFeature;
15import tools.refinery.viatra.runtime.emf.EMFQueryRuntimeContext;
16import tools.refinery.viatra.runtime.emf.types.EClassTransitiveInstancesKey;
17import tools.refinery.viatra.runtime.emf.types.EClassUnscopedTransitiveInstancesKey;
18import tools.refinery.viatra.runtime.emf.types.EDataTypeInSlotsKey;
19import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey;
20import tools.refinery.viatra.runtime.localsearch.operations.check.InstanceOfClassCheck;
21import tools.refinery.viatra.runtime.localsearch.operations.check.InstanceOfDataTypeCheck;
22import tools.refinery.viatra.runtime.localsearch.operations.check.InstanceOfJavaClassCheck;
23import tools.refinery.viatra.runtime.localsearch.operations.check.StructuralFeatureCheck;
24import tools.refinery.viatra.runtime.localsearch.operations.check.nobase.ScopeCheck;
25import tools.refinery.viatra.runtime.localsearch.operations.extend.ExtendToEStructuralFeatureSource;
26import tools.refinery.viatra.runtime.localsearch.operations.extend.ExtendToEStructuralFeatureTarget;
27import tools.refinery.viatra.runtime.localsearch.operations.extend.IterateOverContainers;
28import tools.refinery.viatra.runtime.localsearch.operations.extend.IterateOverEClassInstances;
29import tools.refinery.viatra.runtime.localsearch.operations.extend.IterateOverEDatatypeInstances;
30import tools.refinery.viatra.runtime.matchers.context.IInputKey;
31import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
32import tools.refinery.viatra.runtime.matchers.context.common.JavaTransitiveInstancesKey;
33import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
34import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
35import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint;
36import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
37import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
38
39/**
40 * Operation compiler implementation that uses EMF-specific operations.
41 *
42 * @author Zoltan Ujhelyi
43 * @since 1.7
44 *
45 */
46public class EMFOperationCompiler extends AbstractOperationCompiler {
47
48 private boolean baseIndexAvailable;
49
50 private final EMFQueryRuntimeContext runtimeContext;
51
52 public EMFOperationCompiler(IQueryRuntimeContext runtimeContext) {
53 this(runtimeContext, false);
54 }
55
56 public EMFOperationCompiler(IQueryRuntimeContext runtimeContext, boolean baseIndexAvailable) {
57 super(runtimeContext);
58 this.runtimeContext = (EMFQueryRuntimeContext) runtimeContext;
59 this.baseIndexAvailable = baseIndexAvailable;
60 }
61
62 @Override
63 protected void createCheck(TypeFilterConstraint typeConstraint, Map<PVariable, Integer> variableMapping) {
64 final IInputKey inputKey = typeConstraint.getInputKey();
65 if (inputKey instanceof JavaTransitiveInstancesKey) {
66 doCreateInstanceofJavaTypeCheck((JavaTransitiveInstancesKey) inputKey, variableMapping.get(typeConstraint.getVariablesTuple().get(0)));
67 } else if (inputKey instanceof EDataTypeInSlotsKey) { // TODO probably only occurs as TypeConstraint
68 doCreateInstanceofDatatypeCheck((EDataTypeInSlotsKey) inputKey, variableMapping.get(typeConstraint.getVariablesTuple().get(0)));
69 } else if (inputKey instanceof EClassUnscopedTransitiveInstancesKey) {
70 doCreateInstanceofUnscopedClassCheck((EClassUnscopedTransitiveInstancesKey) inputKey, variableMapping.get(typeConstraint.getVariablesTuple().get(0)));
71 } else {
72 String msg = UNSUPPORTED_TYPE_MESSAGE + inputKey;
73 throw new QueryProcessingException(msg, null, msg, null);
74 }
75 }
76
77 @Override
78 protected void createCheck(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping) {
79 final IInputKey inputKey = typeConstraint.getSupplierKey();
80 if (inputKey instanceof EClassTransitiveInstancesKey) {
81 doCreateInstanceofClassCheck((EClassTransitiveInstancesKey)inputKey, variableMapping.get(typeConstraint.getVariablesTuple().get(0)));
82 } else if (inputKey instanceof EStructuralFeatureInstancesKey) {
83 int sourcePosition = variableMapping.get(typeConstraint.getVariablesTuple().get(0));
84 int targetPosition = variableMapping.get(typeConstraint.getVariablesTuple().get(1));
85 operations.add(new StructuralFeatureCheck(sourcePosition, targetPosition,
86 ((EStructuralFeatureInstancesKey) inputKey).getEmfKey()));
87 } else if (inputKey instanceof EDataTypeInSlotsKey) {
88 doCreateInstanceofDatatypeCheck((EDataTypeInSlotsKey) inputKey, variableMapping.get(typeConstraint.getVariablesTuple().get(0)));
89 } else {
90 String msg = UNSUPPORTED_TYPE_MESSAGE + inputKey;
91 throw new QueryProcessingException(msg, null, msg, null);
92 }
93 }
94
95 @Override
96 protected void createUnaryTypeCheck(IInputKey inputKey, int position) {
97 if (inputKey instanceof EClassTransitiveInstancesKey) {
98 doCreateInstanceofClassCheck((EClassTransitiveInstancesKey)inputKey, position);
99 } else if (inputKey instanceof EClassUnscopedTransitiveInstancesKey) {
100 doCreateInstanceofUnscopedClassCheck((EClassUnscopedTransitiveInstancesKey)inputKey, position);
101 } else if (inputKey instanceof EDataTypeInSlotsKey) {
102 doCreateInstanceofDatatypeCheck((EDataTypeInSlotsKey) inputKey, position);
103 } else if (inputKey instanceof JavaTransitiveInstancesKey) {
104 doCreateInstanceofJavaTypeCheck((JavaTransitiveInstancesKey) inputKey, position);
105 } else {
106 String msg = UNSUPPORTED_TYPE_MESSAGE + inputKey;
107 throw new QueryProcessingException(msg, null, msg, null);
108 }
109 }
110
111 private void doCreateInstanceofClassCheck(EClassTransitiveInstancesKey inputKey, int position) {
112 operations.add(new InstanceOfClassCheck(position, inputKey.getEmfKey()));
113 operations.add(new ScopeCheck(position, runtimeContext.getEmfScope()));
114 }
115
116 private void doCreateInstanceofUnscopedClassCheck(EClassUnscopedTransitiveInstancesKey inputKey, int position) {
117 operations.add(new InstanceOfClassCheck(position, inputKey.getEmfKey()));
118 }
119
120 private void doCreateInstanceofDatatypeCheck(EDataTypeInSlotsKey inputKey, int position) {
121 operations.add(new InstanceOfDataTypeCheck(position, inputKey.getEmfKey()));
122 }
123 private void doCreateInstanceofJavaTypeCheck(JavaTransitiveInstancesKey inputKey, int position) {
124 operations.add(new InstanceOfJavaClassCheck(position, inputKey.getInstanceClass()));
125 }
126
127 @Override
128 public void createExtend(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping) {
129 final IInputKey inputKey = typeConstraint.getSupplierKey();
130 if (inputKey instanceof EDataTypeInSlotsKey) {
131 if(baseIndexAvailable){
132 operations.add(new IterateOverEDatatypeInstances(variableMapping.get(typeConstraint.getVariableInTuple(0)), ((EDataTypeInSlotsKey) inputKey).getEmfKey()));
133 } else {
134 int position = variableMapping.get(typeConstraint.getVariableInTuple(0));
135 operations
136 .add(new tools.refinery.viatra.runtime.localsearch.operations.extend.nobase.IterateOverEDatatypeInstances(position,
137 ((EDataTypeInSlotsKey) inputKey).getEmfKey(), runtimeContext.getEmfScope()));
138 operations.add(new ScopeCheck(position, runtimeContext.getEmfScope()));
139 }
140 } else if (inputKey instanceof EClassTransitiveInstancesKey) {
141 if(baseIndexAvailable){
142 operations.add(new IterateOverEClassInstances(variableMapping.get(typeConstraint.getVariableInTuple(0)),
143 ((EClassTransitiveInstancesKey) inputKey).getEmfKey()));
144 } else {
145 int position = variableMapping.get(typeConstraint.getVariableInTuple(0));
146 operations
147 .add(new tools.refinery.viatra.runtime.localsearch.operations.extend.nobase.IterateOverEClassInstances(
148 position,
149 ((EClassTransitiveInstancesKey) inputKey).getEmfKey(), runtimeContext.getEmfScope()));
150 operations.add(new ScopeCheck(position, runtimeContext.getEmfScope()));
151 }
152 } else if (inputKey instanceof EStructuralFeatureInstancesKey) {
153 final EStructuralFeature feature = ((EStructuralFeatureInstancesKey) inputKey).getEmfKey();
154
155 int sourcePosition = variableMapping.get(typeConstraint.getVariablesTuple().get(0));
156 int targetPosition = variableMapping.get(typeConstraint.getVariablesTuple().get(1));
157
158 boolean fromBound = variableBindings.get(typeConstraint).contains(sourcePosition);
159 boolean toBound = variableBindings.get(typeConstraint).contains(targetPosition);
160
161 if (fromBound && !toBound) {
162 operations.add(new ExtendToEStructuralFeatureTarget(sourcePosition, targetPosition, feature));
163 operations.add(new ScopeCheck(targetPosition, runtimeContext.getEmfScope()));
164 } else if(!fromBound && toBound){
165 if (feature instanceof EReference && ((EReference)feature).isContainment()) {
166 // The iterate is also used to traverse a single container (third parameter)
167 operations.add(new IterateOverContainers(sourcePosition, targetPosition, false));
168 operations.add(new ScopeCheck(sourcePosition, runtimeContext.getEmfScope()));
169 } else if(baseIndexAvailable){
170 TupleMask mask = TupleMask.fromSelectedIndices(variableMapping.size(), new int[] {targetPosition});
171 operations.add(new ExtendToEStructuralFeatureSource(sourcePosition, targetPosition, feature, mask));
172 } else {
173 operations.add(new tools.refinery.viatra.runtime.localsearch.operations.extend.nobase.ExtendToEStructuralFeatureSource(
174 sourcePosition, targetPosition, feature));
175 operations.add(new ScopeCheck(sourcePosition, runtimeContext.getEmfScope()));
176 }
177 } else {
178 // TODO Elaborate solution based on the navigability of edges
179 // As of now a static solution is implemented
180 if (baseIndexAvailable) {
181 operations.add(new IterateOverEClassInstances(sourcePosition, feature.getEContainingClass()));
182 operations.add(new ExtendToEStructuralFeatureTarget(sourcePosition, targetPosition, feature));
183 } else {
184 operations
185 .add(new tools.refinery.viatra.runtime.localsearch.operations.extend.nobase.IterateOverEClassInstances(
186 sourcePosition, feature.getEContainingClass(), runtimeContext.getEmfScope()));
187 operations.add(new ScopeCheck(sourcePosition, runtimeContext.getEmfScope()));
188 operations.add(new ExtendToEStructuralFeatureTarget(sourcePosition, targetPosition, feature));
189 operations.add(new ScopeCheck(targetPosition, runtimeContext.getEmfScope()));
190 }
191 }
192
193 } else {
194 throw new IllegalArgumentException(UNSUPPORTED_TYPE_MESSAGE + inputKey);
195 }
196 }
197
198}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/GenericOperationCompiler.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/GenericOperationCompiler.java
new file mode 100644
index 00000000..606048b7
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/GenericOperationCompiler.java
@@ -0,0 +1,102 @@
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.localsearch.planner.compiler;
10
11import java.util.ArrayList;
12import java.util.HashSet;
13import java.util.List;
14import java.util.Map;
15import java.util.Set;
16
17import tools.refinery.viatra.runtime.localsearch.operations.generic.GenericTypeCheck;
18import tools.refinery.viatra.runtime.localsearch.operations.generic.GenericTypeExtend;
19import tools.refinery.viatra.runtime.localsearch.operations.generic.GenericTypeExtendSingleValue;
20import tools.refinery.viatra.runtime.matchers.context.IInputKey;
21import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
22import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
23import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint;
24import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
25import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
26import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
27
28/**
29 * @author Zoltan Ujhelyi
30 * @since 1.7
31 *
32 */
33public class GenericOperationCompiler extends AbstractOperationCompiler {
34
35 public GenericOperationCompiler(IQueryRuntimeContext runtimeContext) {
36 super(runtimeContext);
37 }
38
39 @Override
40 protected void createCheck(TypeFilterConstraint typeConstraint, Map<PVariable, Integer> variableMapping) {
41 IInputKey inputKey = typeConstraint.getInputKey();
42 Tuple tuple = typeConstraint.getVariablesTuple();
43 int[] positions = new int[tuple.getSize()];
44 for (int i = 0; i < tuple.getSize(); i++) {
45 PVariable variable = (PVariable) tuple.get(i);
46 positions[i] = variableMapping.get(variable);
47 }
48 operations.add(new GenericTypeCheck(inputKey, positions, TupleMask.fromSelectedIndices(variableMapping.size(), positions)));
49
50 }
51
52 @Override
53 protected void createCheck(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping) {
54 IInputKey inputKey = typeConstraint.getSupplierKey();
55 Tuple tuple = typeConstraint.getVariablesTuple();
56 int[] positions = new int[tuple.getSize()];
57 for (int i = 0; i < tuple.getSize(); i++) {
58 PVariable variable = (PVariable) tuple.get(i);
59 positions[i] = variableMapping.get(variable);
60 }
61 operations.add(new GenericTypeCheck(inputKey, positions, TupleMask.fromSelectedIndices(variableMapping.size(), positions)));
62 }
63
64 @Override
65 protected void createUnaryTypeCheck(IInputKey inputKey, int position) {
66 int[] positions = new int[] {position};
67 operations.add(new GenericTypeCheck(inputKey, positions, TupleMask.fromSelectedIndices(1, positions)));
68 }
69
70 @Override
71 protected void createExtend(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping) {
72 IInputKey inputKey = typeConstraint.getSupplierKey();
73 Tuple tuple = typeConstraint.getVariablesTuple();
74
75 int[] positions = new int[tuple.getSize()];
76 List<Integer> boundVariableIndices = new ArrayList<>();
77 List<Integer> boundVariables = new ArrayList<>();
78 Set<Integer> unboundVariables = new HashSet<>();
79 for (int i = 0; i < tuple.getSize(); i++) {
80 PVariable variable = (PVariable) tuple.get(i);
81 Integer position = variableMapping.get(variable);
82 positions[i] = position;
83 if (variableBindings.get(typeConstraint).contains(position)) {
84 boundVariableIndices.add(i);
85 boundVariables.add(position);
86 } else {
87 unboundVariables.add(position);
88 }
89 }
90 TupleMask indexerMask = TupleMask.fromSelectedIndices(inputKey.getArity(), boundVariableIndices);
91 TupleMask callMask = TupleMask.fromSelectedIndices(variableMapping.size(), boundVariables);
92 if (unboundVariables.size() == 1) {
93 operations.add(new GenericTypeExtendSingleValue(inputKey, positions, callMask, indexerMask, unboundVariables.iterator().next()));
94 } else {
95 operations.add(new GenericTypeExtend(inputKey, positions, callMask, indexerMask, unboundVariables));
96 }
97
98 }
99
100
101
102}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/IOperationCompiler.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/IOperationCompiler.java
new file mode 100644
index 00000000..625a7eb2
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/compiler/IOperationCompiler.java
@@ -0,0 +1,53 @@
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.localsearch.planner.compiler;
10
11import java.util.List;
12import java.util.Map;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.localsearch.matcher.CallWithAdornment;
16import tools.refinery.viatra.runtime.localsearch.matcher.MatcherReference;
17import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
18import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
19import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
20import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
21import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
22
23/**
24 * An operation compiler is responsible for creating executable search plans from the subplan structure.
25 *
26 * @author Zoltan Ujhelyi
27 * @since 1.7
28 *
29 */
30public interface IOperationCompiler {
31
32 /**
33 * Compiles a plan of <code>POperation</code>s to a list of type <code>List&ltISearchOperation></code>
34 *
35 * @param plan
36 * @param boundParameters
37 * @return an ordered list of POperations that make up the compiled search plan
38 * @throws ViatraQueryRuntimeException
39 */
40 List<ISearchOperation> compile(SubPlan plan, Set<PParameter> boundParameters);
41
42 /**
43 * Replaces previous method returning {@link MatcherReference}
44 * @since 2.1
45 */
46 Set<CallWithAdornment> getDependencies();
47
48 /**
49 * @return the cached variable bindings for the previously created plan
50 */
51 Map<PVariable, Integer> getVariableMappings();
52
53} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/IConstraintEvaluationContext.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/IConstraintEvaluationContext.java
new file mode 100644
index 00000000..9b44612b
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/IConstraintEvaluationContext.java
@@ -0,0 +1,63 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.planner.cost;
10
11import tools.refinery.viatra.runtime.matchers.backend.ResultProviderRequestor;
12import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess;
13import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
14import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
15import java.util.Collection;
16import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
17import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
18
19/**
20 * This interface denotes the evaluation context of a constraint, intended for cost estimation. Provides access to information
21 * on which the cost function can base its calculation.
22 *
23 * @author Grill Balázs
24 * @since 1.4
25 * @noimplement
26 */
27public interface IConstraintEvaluationContext {
28
29 /**
30 * Get the constraint to be evaluated
31 */
32 public PConstraint getConstraint();
33
34 /**
35 * Unbound variables at the time of evaluating the constraint
36 */
37 public Collection<PVariable> getFreeVariables();
38
39 /**
40 * Bound variables at the time of evaluating the constraint
41 */
42 public Collection<PVariable> getBoundVariables();
43
44 public IQueryRuntimeContext getRuntimeContext();
45
46 /**
47 * @since 1.5
48 */
49 public QueryAnalyzer getQueryAnalyzer();
50
51 /**
52 * @deprecated use {@link #resultProviderRequestor()}
53 * @since 1.5
54 */
55 @Deprecated
56 public IQueryResultProviderAccess resultProviderAccess();
57
58 /**
59 * @since 2.1
60 */
61 public ResultProviderRequestor resultProviderRequestor();
62
63}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/ICostFunction.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/ICostFunction.java
new file mode 100644
index 00000000..4d9d0708
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/ICostFunction.java
@@ -0,0 +1,22 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.planner.cost;
10
11/**
12 * Common interface for cost function implementation
13 *
14 * @author Grill Balázs
15 * @since 1.4
16 *
17 */
18public interface ICostFunction{
19
20 public double apply(IConstraintEvaluationContext input);
21
22}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/HybridMatcherConstraintCostFunction.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/HybridMatcherConstraintCostFunction.java
new file mode 100644
index 00000000..df9292f0
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/HybridMatcherConstraintCostFunction.java
@@ -0,0 +1,91 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.planner.cost.impl;
10
11import java.util.HashMap;
12import java.util.List;
13import java.util.Map;
14import java.util.Set;
15import java.util.stream.Collectors;
16
17import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
18import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
19import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
20import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
21import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
22import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
23import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
24import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
25
26/**
27 * This cost function is intended to be used on hybrid configuration, with the strict restriction than any
28 * non-flattened positive pattern call is executed with Rete engine. This implementation provides the exact number
29 * of matches by invoking the result provider for the called pattern.
30 *
31 * @deprecated {@link StatisticsBasedConstraintCostFunction} should use {@link IQueryResultProvider#estimateCardinality(tools.refinery.viatra.runtime.matchers.tuple.TupleMask, org.eclipse.viatra.query.runtime.matchers.util.Accuracy)}
32 */
33@Deprecated
34public class HybridMatcherConstraintCostFunction extends IndexerBasedConstraintCostFunction {
35
36 @Override
37 protected double _calculateCost(PositivePatternCall patternCall, IConstraintEvaluationContext context) {
38 // Determine local constant constraints which is used to filter results
39 Tuple variables = patternCall.getVariablesTuple();
40 Set<Object> variablesSet = variables.getDistinctElements();
41 final Map<PVariable, Object> constantMap = new HashMap<>();
42 for (PConstraint _constraint : patternCall.getPSystem().getConstraints()) {
43 if (_constraint instanceof ConstantValue){
44 ConstantValue constraint = (ConstantValue) _constraint;
45 PVariable variable = (PVariable) constraint.getVariablesTuple().get(0);
46 if (variablesSet.contains(variable) && context.getBoundVariables().contains(variable)) {
47 constantMap.put(variable, constraint.getSupplierKey());
48 }
49 }
50 }
51
52 // Determine filter
53 Object[] filter = new Object[variables.getSize()];
54 for(int i=0; i < variables.getSize(); i++){
55 filter[i] = constantMap.get(variables.get(i));
56 }
57
58 // aggregate keys are the bound and not filtered variables
59 // These will be fixed in runtime, but unknown at planning time
60 // This is represented by indices to ease working with result tuples
61 final Map<Object, Integer> variableIndices = variables.invertIndex();
62 List<Integer> aggregateKeys = context.getBoundVariables().stream()
63 .filter(input -> !constantMap.containsKey(input))
64 .map(variableIndices::get)
65 .collect(Collectors.toList());
66
67 IQueryResultProvider resultProvider = context.resultProviderRequestor().requestResultProvider(patternCall, null);
68 Map<Tuple, Integer> aggregatedCounts = new HashMap<>();
69
70 // Iterate over all matches and count together matches that has equal values on
71 // aggregateKeys positions. The cost of the pattern call is considered to be the
72 // Maximum of these counted values
73
74 int result = 0;
75 // NOTE: a stream is not an iterable (cannot be iterated more than once), so to use it in a for-loop
76 // it has to be wrapped; in the following line a lambda is used to implement Iterable#iterator()
77 for (Tuple match : (Iterable<Tuple>) () -> resultProvider.getAllMatches(filter).iterator()) {
78 Tuple extracted = Tuples.flatTupleOf(aggregateKeys.stream().map(match::get).toArray());
79 int count = (aggregatedCounts.containsKey(extracted))
80 ? aggregatedCounts.get(extracted) + 1
81 : 1;
82 aggregatedCounts.put(extracted, count);
83 if (result < count) {
84 result = count;
85 }
86 }
87
88 return result;
89 }
90
91} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/IndexerBasedConstraintCostFunction.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/IndexerBasedConstraintCostFunction.java
new file mode 100644
index 00000000..9e2c8680
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/IndexerBasedConstraintCostFunction.java
@@ -0,0 +1,49 @@
1/**
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.planner.cost.impl;
10
11import java.util.Optional;
12
13import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
14import tools.refinery.viatra.runtime.matchers.context.IInputKey;
15import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
16import tools.refinery.viatra.runtime.matchers.util.Accuracy;
17
18/**
19 * Cost function which calculates cost based on the cardinality of items in the runtime model, provided by the base indexer
20 *
21 * @author Grill Balázs
22 * @since 1.4
23 */
24public class IndexerBasedConstraintCostFunction extends StatisticsBasedConstraintCostFunction {
25
26
27
28
29 /**
30 *
31 */
32 public IndexerBasedConstraintCostFunction() {
33 super();
34 }
35
36 /**
37 * @param inverseNavigationPenalty
38 * @since 2.1
39 */
40 public IndexerBasedConstraintCostFunction(double inverseNavigationPenalty) {
41 super(inverseNavigationPenalty);
42 }
43
44 @Override
45 public Optional<Long> projectionSize(IConstraintEvaluationContext input, IInputKey supplierKey, TupleMask groupMask, Accuracy requiredAccuracy) {
46 return input.getRuntimeContext().estimateCardinality(supplierKey, groupMask, requiredAccuracy);
47 }
48
49}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/StatisticsBasedConstraintCostFunction.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/StatisticsBasedConstraintCostFunction.java
new file mode 100644
index 00000000..873be31d
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/StatisticsBasedConstraintCostFunction.java
@@ -0,0 +1,413 @@
1/**
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.planner.cost.impl;
10
11import static tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper.min;
12
13import java.util.ArrayList;
14import java.util.Arrays;
15import java.util.Collection;
16import java.util.Collections;
17import java.util.List;
18import java.util.Map;
19import java.util.Optional;
20import java.util.Set;
21
22import tools.refinery.viatra.runtime.localsearch.matcher.integration.AbstractLocalSearchResultProvider;
23import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
24import tools.refinery.viatra.runtime.localsearch.planner.cost.ICostFunction;
25import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
26import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
27import tools.refinery.viatra.runtime.matchers.context.IInputKey;
28import tools.refinery.viatra.runtime.matchers.planning.helpers.FunctionalDependencyHelper;
29import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference;
30import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
31import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
32import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
33import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint;
34import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
35import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation;
36import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality;
37import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.NegativePatternCall;
38import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.PatternMatchCounter;
39import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint;
40import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryReflexiveTransitiveClosure;
41import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
42import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
43import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
44import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
45import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
46import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
47import tools.refinery.viatra.runtime.matchers.util.Accuracy;
48import tools.refinery.viatra.runtime.matchers.util.Preconditions;
49
50/**
51 * Cost function which calculates cost based on the cardinality of items in the runtime model
52 *
53 * <p> To provide custom statistics, override
54 * {@link #projectionSize(IConstraintEvaluationContext, IInputKey, TupleMask, Accuracy)}
55 * and {@link #bucketSize(IQueryReference, IConstraintEvaluationContext, TupleMask)}.
56 *
57 * @author Grill Balázs
58 * @since 1.4
59 */
60public abstract class StatisticsBasedConstraintCostFunction implements ICostFunction {
61 protected static final double MAX_COST = 250.0;
62
63 protected static final double DEFAULT_COST = StatisticsBasedConstraintCostFunction.MAX_COST - 100.0;
64
65 /**
66 * @since 2.1
67 */
68 public static final double INVERSE_NAVIGATION_PENALTY_DEFAULT = 0.10;
69 /**
70 * @since 2.1
71 */
72 public static final double INVERSE_NAVIGATION_PENALTY_GENERIC = 0.01;
73 /**
74 * @since 2.7
75 */
76 public static final double EVAL_UNWIND_EXTENSION_FACTOR = 3.0;
77
78 private final double inverseNavigationPenalty;
79
80
81 /**
82 * @since 2.1
83 */
84 public StatisticsBasedConstraintCostFunction(double inverseNavigationPenalty) {
85 super();
86 this.inverseNavigationPenalty = inverseNavigationPenalty;
87 }
88 public StatisticsBasedConstraintCostFunction() {
89 this(INVERSE_NAVIGATION_PENALTY_DEFAULT);
90 }
91
92 /**
93 * @deprecated call and implement {@link #projectionSize(IConstraintEvaluationContext, IInputKey, TupleMask, Accuracy)} instead
94 */
95 @Deprecated
96 public long countTuples(final IConstraintEvaluationContext input, final IInputKey supplierKey) {
97 return projectionSize(input, supplierKey, TupleMask.identity(supplierKey.getArity()), Accuracy.EXACT_COUNT).orElse(-1L);
98 }
99
100 /**
101 * Override this to provide custom statistics on edge/node counts.
102 * New implementors shall implement this instead of {@link #countTuples(IConstraintEvaluationContext, IInputKey)}
103 * @since 2.1
104 */
105 public Optional<Long> projectionSize(final IConstraintEvaluationContext input, final IInputKey supplierKey,
106 final TupleMask groupMask, Accuracy requiredAccuracy) {
107 long legacyCount = countTuples(input, supplierKey);
108 return legacyCount < 0 ? Optional.empty() : Optional.of(legacyCount);
109 }
110
111 /**
112 * Override this to provide custom estimates for match set sizes of called patterns.
113 * @since 2.1
114 */
115 public Optional<Double> bucketSize(final IQueryReference patternCall,
116 final IConstraintEvaluationContext input, TupleMask projMask) {
117 IQueryResultProvider resultProvider = input.resultProviderRequestor().requestResultProvider(patternCall, null);
118 // TODO hack: use LS cost instead of true bucket size estimate
119 if (resultProvider instanceof AbstractLocalSearchResultProvider) {
120 double estimatedCost = ((AbstractLocalSearchResultProvider) resultProvider).estimateCost(projMask);
121 return Optional.of(estimatedCost);
122 } else {
123 return resultProvider.estimateAverageBucketSize(projMask, Accuracy.APPROXIMATION);
124 }
125 }
126
127
128
129 @Override
130 public double apply(final IConstraintEvaluationContext input) {
131 return this.calculateCost(input.getConstraint(), input);
132 }
133
134 protected double _calculateCost(final ConstantValue constant, final IConstraintEvaluationContext input) {
135 return 0.0f;
136 }
137
138 protected double _calculateCost(final TypeConstraint constraint, final IConstraintEvaluationContext input) {
139 final Collection<PVariable> freeMaskVariables = input.getFreeVariables();
140 final Collection<PVariable> boundMaskVariables = input.getBoundVariables();
141 IInputKey supplierKey = constraint.getSupplierKey();
142 long arity = supplierKey.getArity();
143
144 if ((arity == 1)) {
145 // unary constraint
146 return calculateUnaryConstraintCost(constraint, input);
147 } else if ((arity == 2)) {
148 // binary constraint
149 PVariable srcVariable = ((PVariable) constraint.getVariablesTuple().get(0));
150 PVariable dstVariable = ((PVariable) constraint.getVariablesTuple().get(1));
151 boolean isInverse = false;
152 // Check if inverse navigation is needed along the edge
153 if ((freeMaskVariables.contains(srcVariable) && boundMaskVariables.contains(dstVariable))) {
154 isInverse = true;
155 }
156 double binaryExtendCost = calculateBinaryCost(supplierKey, srcVariable, dstVariable, isInverse, input);
157 // Make inverse navigation slightly more expensive than forward navigation
158 // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=501078
159 return (isInverse) ? binaryExtendCost + inverseNavigationPenalty : binaryExtendCost;
160 } else {
161 // n-ary constraint
162 throw new UnsupportedOperationException("Cost calculation for arity " + arity + " is not implemented yet");
163 }
164 }
165
166
167 /**
168 * @deprecated use/implement {@link #calculateBinaryCost(IInputKey, PVariable, PVariable, boolean, IConstraintEvaluationContext)} instead
169 */
170 @Deprecated
171 protected double calculateBinaryExtendCost(final IInputKey supplierKey, final PVariable srcVariable,
172 final PVariable dstVariable, final boolean isInverse, long edgeCount /* TODO remove */,
173 final IConstraintEvaluationContext input) {
174 throw new UnsupportedOperationException();
175 }
176
177 /**
178 * @since 2.1
179 */
180 protected double calculateBinaryCost(final IInputKey supplierKey, final PVariable srcVariable,
181 final PVariable dstVariable, final boolean isInverse,
182 final IConstraintEvaluationContext input) {
183 final Collection<PVariable> freeMaskVariables = input.getFreeVariables();
184 final PConstraint constraint = input.getConstraint();
185
186// IQueryMetaContext metaContext = input.getRuntimeContext().getMetaContext();
187// Collection<InputKeyImplication> implications = metaContext.getImplications(supplierKey);
188
189 Optional<Long> edgeUpper = projectionSize(input, supplierKey, TupleMask.identity(2), Accuracy.BEST_UPPER_BOUND);
190 Optional<Long> srcUpper = projectionSize(input, supplierKey, TupleMask.selectSingle(0, 2), Accuracy.BEST_UPPER_BOUND);
191 Optional<Long> dstUpper = projectionSize(input, supplierKey, TupleMask.selectSingle(1, 2), Accuracy.BEST_UPPER_BOUND);
192
193 if (freeMaskVariables.contains(srcVariable) && freeMaskVariables.contains(dstVariable)) {
194 Double branchCount = edgeUpper.map(Long::doubleValue).orElse(
195 srcUpper.map(Long::doubleValue).orElse(DEFAULT_COST)
196 *
197 dstUpper.map(Long::doubleValue).orElse(DEFAULT_COST)
198 );
199 return branchCount;
200
201 } else {
202
203 Optional<Long> srcLower = projectionSize(input, supplierKey, TupleMask.selectSingle(0, 2), Accuracy.APPROXIMATION);
204 Optional<Long> dstLower = projectionSize(input, supplierKey, TupleMask.selectSingle(1, 2), Accuracy.APPROXIMATION);
205
206 List<Optional<Long>> nodeLower = Arrays.asList(srcLower, dstLower);
207 List<Optional<Long>> nodeUpper = Arrays.asList(srcUpper, dstUpper);
208
209 int from = isInverse ? 1 : 0;
210 int to = isInverse ? 0 : 1;
211
212 Optional<Double> costEstimate = Optional.empty();
213
214 if (!freeMaskVariables.contains(srcVariable) && !freeMaskVariables.contains(dstVariable)) {
215 // both variables bound, this is a simple check
216 costEstimate = min(costEstimate, 0.9);
217 } // TODO use bucket size estimation in the runtime context
218 costEstimate = min(costEstimate,
219 edgeUpper.flatMap(edges ->
220 nodeLower.get(from).map(fromNodes ->
221 // amortize edges over start nodes
222 (fromNodes == 0) ? 0.0 : (((double) edges) / fromNodes)
223 )));
224 if (navigatesThroughFunctionalDependencyInverse(input, constraint)) {
225 costEstimate = min(costEstimate, nodeUpper.get(to).flatMap(toNodes ->
226 nodeLower.get(from).map(fromNodes ->
227 // due to a reverse functional dependency, the destination count is an upper bound for the edge count
228 (fromNodes == 0) ? 0.0 : ((double) toNodes) / fromNodes
229 )));
230 }
231 if (! edgeUpper.isPresent()) {
232 costEstimate = min(costEstimate, nodeUpper.get(to).flatMap(toNodes ->
233 nodeLower.get(from).map(fromNodes ->
234 // If count is 0, no such element exists in the model, so there will be no branching
235 // TODO rethink, why dstNodeCount / srcNodeCount instead of dstNodeCount?
236 // The universally valid bound would be something like sparseEdgeEstimate = dstNodeCount + 1.0
237 // If we assume sparseness, we can reduce it by a SPARSENESS_FACTOR (e.g. 0.1).
238 // Alternatively, discount dstNodeCount * srcNodeCount on a SPARSENESS_EXPONENT (e.g 0.75) and then amortize over srcNodeCount.
239 fromNodes != 0 ? Math.max(1.0, ((double) toNodes) / fromNodes) : 1.0
240 )));
241 }
242 if (navigatesThroughFunctionalDependency(input, constraint)) {
243 // At most one destination value
244 costEstimate = min(costEstimate, 1.0);
245 }
246
247 return costEstimate.orElse(DEFAULT_COST);
248
249 }
250 }
251
252 /**
253 * @since 1.7
254 */
255 protected boolean navigatesThroughFunctionalDependency(final IConstraintEvaluationContext input,
256 final PConstraint constraint) {
257 return navigatesThroughFunctionalDependency(input, constraint, input.getBoundVariables(), input.getFreeVariables());
258 }
259 /**
260 * @since 2.1
261 */
262 protected boolean navigatesThroughFunctionalDependencyInverse(final IConstraintEvaluationContext input,
263 final PConstraint constraint) {
264 return navigatesThroughFunctionalDependency(input, constraint, input.getFreeVariables(), input.getBoundVariables());
265 }
266 /**
267 * @since 2.1
268 */
269 protected boolean navigatesThroughFunctionalDependency(final IConstraintEvaluationContext input,
270 final PConstraint constraint, Collection<PVariable> determining, Collection<PVariable> determined) {
271 final QueryAnalyzer queryAnalyzer = input.getQueryAnalyzer();
272 final Map<Set<PVariable>, Set<PVariable>> functionalDependencies = queryAnalyzer
273 .getFunctionalDependencies(Collections.singleton(constraint), false);
274 final Set<PVariable> impliedVariables = FunctionalDependencyHelper.closureOf(determining,
275 functionalDependencies);
276 return ((impliedVariables != null) && impliedVariables.containsAll(determined));
277 }
278
279 protected double calculateUnaryConstraintCost(final TypeConstraint constraint,
280 final IConstraintEvaluationContext input) {
281 PVariable variable = (PVariable) constraint.getVariablesTuple().get(0);
282 if (input.getBoundVariables().contains(variable)) {
283 return 0.9;
284 } else {
285 return projectionSize(input, constraint.getSupplierKey(), TupleMask.identity(1), Accuracy.APPROXIMATION)
286 .map(count -> 1.0 + count).orElse(DEFAULT_COST);
287 }
288 }
289
290 protected double _calculateCost(final ExportedParameter exportedParam, final IConstraintEvaluationContext input) {
291 return 0.0;
292 }
293
294 protected double _calculateCost(final TypeFilterConstraint exportedParam,
295 final IConstraintEvaluationContext input) {
296 return 0.0;
297 }
298
299 protected double _calculateCost(final PositivePatternCall patternCall, final IConstraintEvaluationContext input) {
300 final List<Integer> boundPositions = new ArrayList<>();
301 final List<PParameter> parameters = patternCall.getReferredQuery().getParameters();
302 for (int i = 0; (i < parameters.size()); i++) {
303 final PVariable variable = patternCall.getVariableInTuple(i);
304 if (input.getBoundVariables().contains(variable)) boundPositions.add(i);
305 }
306 TupleMask projMask = TupleMask.fromSelectedIndices(parameters.size(), boundPositions);
307
308 return bucketSize(patternCall, input, projMask).orElse(DEFAULT_COST);
309 }
310
311
312 /**
313 * @since 1.7
314 */
315 protected double _calculateCost(final ExpressionEvaluation evaluation, final IConstraintEvaluationContext input) {
316 // Even if there are multiple results here, if all output variable is bound eval unwind will not result in
317 // multiple branches in search graph
318 final double multiplier = evaluation.isUnwinding() && !input.getFreeVariables().isEmpty()
319 ? EVAL_UNWIND_EXTENSION_FACTOR
320 : 1.0;
321 return _calculateCost((PConstraint) evaluation, input) * multiplier;
322 }
323
324 /**
325 * @since 1.7
326 */
327 protected double _calculateCost(final Inequality inequality, final IConstraintEvaluationContext input) {
328 return _calculateCost((PConstraint)inequality, input);
329 }
330
331 /**
332 * @since 1.7
333 */
334 protected double _calculateCost(final AggregatorConstraint aggregator, final IConstraintEvaluationContext input) {
335 return _calculateCost((PConstraint)aggregator, input);
336 }
337
338 /**
339 * @since 1.7
340 */
341 protected double _calculateCost(final NegativePatternCall call, final IConstraintEvaluationContext input) {
342 return _calculateCost((PConstraint)call, input);
343 }
344
345 /**
346 * @since 1.7
347 */
348 protected double _calculateCost(final PatternMatchCounter counter, final IConstraintEvaluationContext input) {
349 return _calculateCost((PConstraint)counter, input);
350 }
351
352 /**
353 * @since 1.7
354 */
355 protected double _calculateCost(final BinaryTransitiveClosure closure, final IConstraintEvaluationContext input) {
356 // if (input.getFreeVariables().size() == 1) return 3.0;
357 return StatisticsBasedConstraintCostFunction.DEFAULT_COST;
358 }
359
360 /**
361 * @since 2.0
362 */
363 protected double _calculateCost(final BinaryReflexiveTransitiveClosure closure, final IConstraintEvaluationContext input) {
364 // if (input.getFreeVariables().size() == 1) return 3.0;
365 return StatisticsBasedConstraintCostFunction.DEFAULT_COST;
366 }
367
368 /**
369 * Default cost calculation strategy
370 */
371 protected double _calculateCost(final PConstraint constraint, final IConstraintEvaluationContext input) {
372 if (input.getFreeVariables().isEmpty()) {
373 return 1.0;
374 } else {
375 return StatisticsBasedConstraintCostFunction.DEFAULT_COST;
376 }
377 }
378
379 /**
380 * @throws ViatraQueryRuntimeException
381 */
382 public double calculateCost(final PConstraint constraint, final IConstraintEvaluationContext input) {
383 Preconditions.checkArgument(constraint != null, "Set constraint value correctly");
384 if (constraint instanceof ExportedParameter) {
385 return _calculateCost((ExportedParameter) constraint, input);
386 } else if (constraint instanceof TypeFilterConstraint) {
387 return _calculateCost((TypeFilterConstraint) constraint, input);
388 } else if (constraint instanceof ConstantValue) {
389 return _calculateCost((ConstantValue) constraint, input);
390 } else if (constraint instanceof PositivePatternCall) {
391 return _calculateCost((PositivePatternCall) constraint, input);
392 } else if (constraint instanceof TypeConstraint) {
393 return _calculateCost((TypeConstraint) constraint, input);
394 } else if (constraint instanceof ExpressionEvaluation) {
395 return _calculateCost((ExpressionEvaluation) constraint, input);
396 } else if (constraint instanceof Inequality) {
397 return _calculateCost((Inequality) constraint, input);
398 } else if (constraint instanceof AggregatorConstraint) {
399 return _calculateCost((AggregatorConstraint) constraint, input);
400 } else if (constraint instanceof NegativePatternCall) {
401 return _calculateCost((NegativePatternCall) constraint, input);
402 } else if (constraint instanceof PatternMatchCounter) {
403 return _calculateCost((PatternMatchCounter) constraint, input);
404 } else if (constraint instanceof BinaryTransitiveClosure) {
405 return _calculateCost((BinaryTransitiveClosure) constraint, input);
406 } else if (constraint instanceof BinaryReflexiveTransitiveClosure) {
407 return _calculateCost((BinaryReflexiveTransitiveClosure) constraint, input);
408 } else {
409 // Default cost calculation
410 return _calculateCost(constraint, input);
411 }
412 }
413}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/VariableBindingBasedCostFunction.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/VariableBindingBasedCostFunction.java
new file mode 100644
index 00000000..a517af25
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/cost/impl/VariableBindingBasedCostFunction.java
@@ -0,0 +1,95 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Balazs Grill, Akos Horvath, 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.localsearch.planner.cost.impl;
10
11import java.util.Set;
12
13import tools.refinery.viatra.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
14import tools.refinery.viatra.runtime.localsearch.planner.cost.ICostFunction;
15import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
16import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
17import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.AggregatorConstraint;
18import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
19import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.NegativePatternCall;
20import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
21import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
22
23/**
24 * This class can be used to calculate cost of application of a constraint with a given adornment.
25 *
26 * For now the logic is based on the following principles:
27 *
28 * <li>The transitive closures, NACs and count finds are the most expensive operations
29 *
30 * <li>The number of free variables increase the cost
31 *
32 * <li>If all the variables of a constraint are free, then its cost equals to twice the number of its parameter
33 * variables. This solves the problem of unnecessary iteration over instances at the beginning of a plan (thus causing
34 * very long run times when executing the plan) by applying constraints based on structural features as soon as
35 * possible.
36 *
37 * <br>
38 *
39 * @author Marton Bur
40 * @since 1.4
41 *
42 */
43public class VariableBindingBasedCostFunction implements ICostFunction {
44
45 // Static cost definitions
46 private static int MAX = 1000;
47 private static int exportedParameterCost = MAX - 20;
48 private static int binaryTransitiveClosureCost = MAX - 50;
49 private static int nacCost = MAX - 100;
50 private static int aggregatorCost = MAX - 200;
51 private static int constantCost = 0;
52
53 @Override
54 public double apply(IConstraintEvaluationContext input) {
55 PConstraint constraint = input.getConstraint();
56 Set<PVariable> affectedVariables = constraint.getAffectedVariables();
57
58 int cost = 0;
59
60 // For constants the cost is determined to be 0.0
61 // The following constraints should be checks:
62 // * Binary transitive closure
63 // * NAC
64 // * count
65 // * exported parameter - only a metadata
66 if (constraint instanceof ConstantValue) {
67 cost = constantCost;
68 } else if (constraint instanceof BinaryTransitiveClosure) {
69 cost = binaryTransitiveClosureCost;
70 } else if (constraint instanceof NegativePatternCall) {
71 cost = nacCost;
72 } else if (constraint instanceof AggregatorConstraint) {
73 cost = aggregatorCost;
74 } else if (constraint instanceof ExportedParameter) {
75 cost = exportedParameterCost;
76 } else {
77 // In case of other constraints count the number of unbound variables
78 for (PVariable pVariable : affectedVariables) {
79 if (input.getFreeVariables().contains(pVariable)) {
80 // For each free variable ('without-value-variable') increase cost
81 cost += 1;
82 }
83 }
84 if (cost == affectedVariables.size()) {
85 // If all the variables are free, double the cost.
86 // This ensures that iteration costs more
87 cost *= 2;
88 }
89
90 }
91
92 return Float.valueOf(cost);
93 }
94
95}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/CompilerHelper.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/CompilerHelper.java
new file mode 100644
index 00000000..9b4e9ea5
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/CompilerHelper.java
@@ -0,0 +1,209 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Daniel Segesdi, Akos Horvath, 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.localsearch.planner.util;
10
11import java.util.ArrayList;
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Map;
17import java.util.Set;
18
19import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
20import tools.refinery.viatra.runtime.matchers.planning.operations.PApply;
21import tools.refinery.viatra.runtime.matchers.planning.operations.POperation;
22import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
23import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
24import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
25import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
26
27/**
28 *
29 * Helper methods for compiling SubPlans
30 *
31 * @author Marton Bur
32 *
33 */
34public class CompilerHelper {
35
36 private CompilerHelper() {/*Utility class constructor*/}
37
38 private static boolean isUserSpecified(PVariable var) {
39 return !var.isVirtual() && !var.getName().startsWith("_");
40 }
41
42 public static Map<PVariable, Integer> createVariableMapping(SubPlan plan) {
43 Map<PVariable, Integer> variableMapping = new HashMap<>();
44
45 int variableNumber = 0;
46
47 // Important note: this list might contain duplications when parameters are made equal inside the pattern
48 // This is the expected and normal behavior
49 List<PVariable> symbolicParameterVariables = plan.getBody().getSymbolicParameterVariables();
50 for (PVariable pVariable : symbolicParameterVariables) {
51 if (!variableMapping.containsKey(pVariable)) {
52 variableMapping.put(pVariable, variableNumber++);
53 }
54 }
55
56 List<PVariable> allVariables = new ArrayList<>(plan.getBody().getUniqueVariables());
57 Collections.sort(allVariables, (left, right) -> {
58 boolean leftUserSpecified = isUserSpecified(left);
59 boolean rightUserSpecified = isUserSpecified(right);
60 if (leftUserSpecified && !rightUserSpecified) {
61 return -1;
62 } else if (!leftUserSpecified && rightUserSpecified) {
63 return +1;
64 } else {
65 return left.getName().compareTo(right.getName());
66 }
67 });
68 for (PVariable pVariable : allVariables) {
69 if (!variableMapping.containsKey(pVariable)) {
70 variableMapping.put(pVariable, variableNumber++);
71 }
72 }
73
74 return variableMapping;
75 }
76
77 public static Map<PConstraint, Set<Integer>> cacheVariableBindings(SubPlan plan,
78 Map<PVariable, Integer> variableMappings, Set<PParameter> adornment) {
79
80 Set<Integer> externallyBoundVariables = getVariableIndicesForParameters(plan, variableMappings,
81 adornment);
82
83 Map<PConstraint, Set<Integer>> variableBindings = new HashMap<>();
84
85 List<SubPlan> allPlansInHierarchy = getAllParentPlans(plan);
86 for (SubPlan subPlan : allPlansInHierarchy) {
87 POperation operation = subPlan.getOperation();
88
89 if (operation instanceof PApply) {
90 PConstraint pConstraint = ((PApply) operation).getPConstraint();
91 Set<Integer> boundVariableIndices = getParametersBoundByParentPlan(variableMappings, subPlan);
92 boundVariableIndices.addAll(externallyBoundVariables);
93
94 variableBindings.put(pConstraint, boundVariableIndices);
95 }
96 }
97 return variableBindings;
98 }
99
100 /**
101 * Returns the list of variable indexes that are bound by the parent plan.
102 */
103 private static Set<Integer> getParametersBoundByParentPlan(Map<PVariable, Integer> variableMappings,
104 SubPlan subPlan) {
105 if (!subPlan.getParentPlans().isEmpty()) {
106 SubPlan parentPlan = subPlan.getParentPlans().get(0);
107 Set<PConstraint> enforcedConstraints = parentPlan.getAllEnforcedConstraints();
108 Set<PVariable> affectedVariables = getAffectedVariables(enforcedConstraints);
109 return getVariableIndices(variableMappings, affectedVariables);
110 }
111 return Collections.emptySet();
112 }
113
114 /**
115 * @param plan
116 * @return all the ancestor plans including the given plan
117 */
118 private static List<SubPlan> getAllParentPlans(SubPlan plan) {
119 SubPlan currentPlan = plan;
120 List<SubPlan> allPlans = new ArrayList<>();
121 allPlans.add(plan);
122 while (!currentPlan.getParentPlans().isEmpty()) {
123 // In the local search it is assumed that only a single parent exists
124 currentPlan = currentPlan.getParentPlans().get(0);
125 allPlans.add(currentPlan);
126 }
127
128 return allPlans;
129 }
130
131 /**
132 * @param variableMappings
133 * the mapping between variables and their indices
134 * @param variables
135 * variables to get the indices for
136 * @return the set of variable indices for the given variables
137 */
138 private static Set<Integer> getVariableIndices(Map<PVariable, Integer> variableMappings,
139 Iterable<PVariable> variables) {
140 Set<Integer> variableIndices = new HashSet<>();
141 for (PVariable pVariable : variables) {
142 variableIndices.add(variableMappings.get(pVariable));
143 }
144 return variableIndices;
145 }
146
147 /**
148 * Returns all affected variables of the given PConstraints.
149 */
150 private static Set<PVariable> getAffectedVariables(Set<PConstraint> pConstraints) {
151 Set<PVariable> allDeducedVariables = new HashSet<>();
152 for (PConstraint pConstraint : pConstraints) {
153 allDeducedVariables.addAll(pConstraint.getAffectedVariables());
154 }
155 return allDeducedVariables;
156 }
157
158 /**
159 * Transforms the index of a parameter into the index of a variable of the normalized body.
160 *
161 * @param plan
162 * the SubPlan containing the original body and its parameters
163 * @param variableMappings
164 * the mapping of PVariables to their indices
165 * @param parameters
166 * a set of parameters
167 * @return the index of the variable corresponding to the parameter at the given index
168 */
169 private static Set<Integer> getVariableIndicesForParameters(SubPlan plan,
170 Map<PVariable, Integer> variableMappings, Set<PParameter> parameters) {
171 Map<PParameter, PVariable> parameterMapping = new HashMap<>();
172 for (ExportedParameter constraint : plan.getBody().getSymbolicParameters()) {
173 parameterMapping.put(constraint.getPatternParameter(), constraint.getParameterVariable());
174 }
175
176 Set<Integer> variableIndices = new HashSet<>();
177 for (PParameter parameter : parameters) {
178 PVariable parameterVariable = parameterMapping.get(parameter);
179 if (parameterVariable == null) {
180 // XXX In case of older (pre-1.4) VIATRA versions, PParameters were not stable, see bug 498348
181 parameterVariable = plan.getBody().getVariableByNameChecked(parameter.getName());
182 }
183 Integer variableIndex = variableMappings.get(parameterVariable);
184 variableIndices.add(variableIndex);
185 }
186 return variableIndices;
187 }
188
189 /**
190 * Extracts the operations from a SubPlan into a list of POperations in the order of execution
191 *
192 * @param plan
193 * the SubPlan from wich the POperations should be extracted
194 * @return list of POperations extracted from the <code>plan</code>
195 */
196 public static List<POperation> createOperationsList(SubPlan plan) {
197 List<POperation> operationsList = new ArrayList<>();
198 while (plan.getParentPlans().size() > 0) {
199 operationsList.add(plan.getOperation());
200 SubPlan parentPlan = plan.getParentPlans().get(0);
201 plan = parentPlan;
202 }
203 operationsList.add(plan.getOperation());
204
205 Collections.reverse(operationsList);
206 return operationsList;
207 }
208
209}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/OperationCostComparator.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/OperationCostComparator.java
new file mode 100644
index 00000000..58f4fc41
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/planner/util/OperationCostComparator.java
@@ -0,0 +1,26 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Danil 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.localsearch.planner.util;
10
11import java.util.Comparator;
12
13import tools.refinery.viatra.runtime.localsearch.planner.PConstraintInfo;
14
15/**
16 * @author Marton Bur
17 *
18 */
19public class OperationCostComparator implements Comparator<PConstraintInfo>{
20
21 @Override
22 public int compare(PConstraintInfo o1, PConstraintInfo o2) {
23 return Double.compare(o1.getCost(), o2.getCost());
24 }
25
26}
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/profiler/LocalSearchProfilerAdapter.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/profiler/LocalSearchProfilerAdapter.java
new file mode 100644
index 00000000..8c50c694
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/profiler/LocalSearchProfilerAdapter.java
@@ -0,0 +1,83 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.localsearch.profiler;
10
11import java.util.HashMap;
12import java.util.List;
13import java.util.Map;
14import java.util.stream.Collectors;
15
16import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
17import tools.refinery.viatra.runtime.localsearch.matcher.ILocalSearchAdapter;
18import tools.refinery.viatra.runtime.localsearch.matcher.LocalSearchMatcher;
19import tools.refinery.viatra.runtime.localsearch.matcher.MatcherReference;
20import tools.refinery.viatra.runtime.localsearch.operations.ISearchOperation;
21import tools.refinery.viatra.runtime.localsearch.plan.SearchPlan;
22import tools.refinery.viatra.runtime.localsearch.plan.SearchPlanExecutor;
23
24/**
25 * This is a simple {@link ILocalSearchAdapter} which capable of counting
26 * each search operation execution then printing it in human readably form
27 * (along with the executed plans) using {@link #toString()}
28 * @author Grill Balázs
29 * @since 1.5
30 *
31 */
32public class LocalSearchProfilerAdapter implements ILocalSearchAdapter {
33
34 private final Map<MatcherReference, List<SearchPlan>> planReference = new HashMap<>();
35
36 private final Map<ISearchOperation, Integer> successfulOperationCounts = new HashMap<>();
37 private final Map<ISearchOperation, Integer> failedOperationCounts = new HashMap<>();
38
39 @Override
40 public void patternMatchingStarted(LocalSearchMatcher lsMatcher) {
41 MatcherReference key = new MatcherReference(lsMatcher.getPlanDescriptor().getQuery(),
42 lsMatcher.getPlanDescriptor().getAdornment());
43 planReference.put(key, lsMatcher.getPlan().stream().map(SearchPlanExecutor::getSearchPlan).collect(Collectors.toList()));
44 }
45
46 @Override
47 public void operationExecuted(SearchPlan plan, ISearchOperation operation, MatchingFrame frame, boolean isSuccessful) {
48 Map<ISearchOperation, Integer> counts = isSuccessful ? successfulOperationCounts : failedOperationCounts;
49 counts.merge(operation,
50 /*no previous entry*/1,
51 /*increase previous value*/(oldValue, v) -> oldValue + 1);
52 }
53
54 @Override
55 public String toString() {
56 StringBuilder sb = new StringBuilder();
57 for (java.util.Map.Entry<MatcherReference, List<SearchPlan>> entry: planReference.entrySet()){
58 sb.append(entry.getKey());
59 sb.append("\n");
60
61 sb.append(entry.getValue());
62
63 List<SearchPlan> bodies = entry.getValue();
64 sb.append("{\n");
65 for(int i=0;i<bodies.size();i++){
66 sb.append("\tbody #");sb.append(i);sb.append("(\n");
67 for(ISearchOperation operation : bodies.get(i).getOperations()){
68 final int successCount = successfulOperationCounts.computeIfAbsent(operation, op -> 0);
69 final int failCount = failedOperationCounts.computeIfAbsent(operation, op -> 0);
70 sb.append("\t\t");sb.append(successCount);
71 sb.append("\t");sb.append(failCount);
72 sb.append("\t");sb.append(successCount + failCount);
73 sb.append("\t");sb.append(operation);
74 sb.append("\n");
75 }
76 sb.append("\t)\n");
77 }
78 sb.append("}\n");
79 }
80 return sb.toString();
81 }
82
83}
diff --git a/subprojects/viatra-runtime-matchers/about.html b/subprojects/viatra-runtime-matchers/about.html
new file mode 100644
index 00000000..d1d5593a
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/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-matchers/build.gradle.kts b/subprojects/viatra-runtime-matchers/build.gradle.kts
new file mode 100644
index 00000000..20a8c2c3
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/build.gradle.kts
@@ -0,0 +1,15 @@
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 implementation(libs.slf4j.log4j)
13 implementation(libs.eclipseCollections.api)
14 implementation(libs.eclipseCollections)
15}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java
new file mode 100644
index 00000000..83f6f766
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java
@@ -0,0 +1,42 @@
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.matchers;
10
11/**
12 * A common base class for all exceptions thrown by various VIATRA Query Runtime APIs.
13 *
14 * @author Zoltan Ujhelyi
15 * @since 2.0
16 */
17public abstract class ViatraQueryRuntimeException extends RuntimeException {
18
19 private static final long serialVersionUID = -8505253058035069310L;
20
21 public ViatraQueryRuntimeException() {
22 super();
23 }
24
25 public ViatraQueryRuntimeException(String message) {
26 super(message);
27 }
28
29 public ViatraQueryRuntimeException(Throwable cause) {
30 super(cause);
31 }
32
33 public ViatraQueryRuntimeException(String message, Throwable cause) {
34 super(message, cause);
35 }
36
37 public ViatraQueryRuntimeException(String message, Throwable cause, boolean enableSuppression,
38 boolean writableStackTrace) {
39 super(message, cause, enableSuppression, writableStackTrace);
40 }
41
42}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java
new file mode 100644
index 00000000..2c09ede1
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java
@@ -0,0 +1,24 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.aggregators;
10
11/**
12 * @since 2.0
13 */
14class AverageAccumulator<Domain> {
15 Domain value;
16 long count;
17
18 public AverageAccumulator(Domain value, long count) {
19 super();
20 this.value = value;
21 this.count = count;
22 }
23
24} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java
new file mode 100644
index 00000000..e8a26afd
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java
@@ -0,0 +1,82 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.aggregators;
10
11import java.util.OptionalDouble;
12import java.util.stream.Stream;
13
14import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
15
16/**
17 * @author Zoltan Ujhelyi
18 * @since 2.0
19 */
20public class DoubleAverageOperator implements IMultisetAggregationOperator<Double, AverageAccumulator<Double>, Double> {
21
22 public static final DoubleAverageOperator INSTANCE = new DoubleAverageOperator();
23
24 private DoubleAverageOperator() {
25 // Singleton, do not call.
26 }
27
28 @Override
29 public String getShortDescription() {
30 return "avg<Integer> incrementally computes the average of java.lang.Integer values";
31 }
32
33 @Override
34 public String getName() {
35 return "avg<Integer>";
36 }
37
38 @Override
39 public AverageAccumulator<Double> createNeutral() {
40 return new AverageAccumulator<Double>(0d, 0l);
41 }
42
43 @Override
44 public boolean isNeutral(AverageAccumulator<Double> result) {
45 return result.count == 0l;
46 }
47
48 @Override
49 public AverageAccumulator<Double> update(AverageAccumulator<Double> oldResult, Double updateValue,
50 boolean isInsertion) {
51 if (isInsertion) {
52 oldResult.value += updateValue;
53 oldResult.count++;
54 } else {
55 oldResult.value -= updateValue;
56 oldResult.count--;
57 }
58 return oldResult;
59 }
60
61 @Override
62 public Double getAggregate(AverageAccumulator<Double> result) {
63 return (result.count == 0)
64 ? null
65 : result.value/result.count;
66 }
67
68 @Override
69 public Double aggregateStream(Stream<Double> stream) {
70 final OptionalDouble averageOpt = stream.mapToDouble(Double::doubleValue).average();
71 return averageOpt.isPresent() ? averageOpt.getAsDouble() : null;
72 }
73
74 /**
75 * @since 2.4
76 */
77 @Override
78 public AverageAccumulator<Double> clone(AverageAccumulator<Double> original) {
79 return new AverageAccumulator<Double>(original.value, original.count);
80 }
81
82} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java
new file mode 100644
index 00000000..744b0cd1
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java
@@ -0,0 +1,62 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.matchers.aggregators;
10
11import java.util.stream.Stream;
12
13import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator;
14
15/**
16 * Incrementally computes the sum of java.lang.Double values
17 * @author Gabor Bergmann
18 * @since 1.4
19 */
20public class DoubleSumOperator extends AbstractMemorylessAggregationOperator<Double, Double> {
21 public static final DoubleSumOperator INSTANCE = new DoubleSumOperator();
22
23 private DoubleSumOperator() {
24 // Singleton, do not call.
25 }
26
27 @Override
28 public String getShortDescription() {
29 return "sum<Double> incrementally computes the sum of java.lang.Double values";
30 }
31 @Override
32 public String getName() {
33 return "sum<Double>";
34 }
35
36 @Override
37 public Double createNeutral() {
38 return 0d;
39 }
40
41 @Override
42 public boolean isNeutral(Double result) {
43 return createNeutral().equals(result);
44 }
45
46 @Override
47 public Double update(Double oldResult, Double updateValue, boolean isInsertion) {
48 return isInsertion ?
49 oldResult + updateValue :
50 oldResult - updateValue;
51 }
52
53 /**
54 * @since 2.0
55 */
56 @Override
57 public Double aggregateStream(Stream<Double> stream) {
58 return stream.mapToDouble(Double::doubleValue).sum();
59 }
60
61
62}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java
new file mode 100644
index 00000000..ee4ceeb8
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java
@@ -0,0 +1,135 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.matchers.aggregators;
10
11import java.util.Comparator;
12import java.util.SortedMap;
13import java.util.TreeMap;
14import java.util.stream.Stream;
15
16import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
17
18/**
19 * Incrementally computes the minimum or maximum of java.lang.Comparable values, using the default comparison
20 *
21 * @author Gabor Bergmann
22 * @since 1.4
23 */
24public class ExtremumOperator<T extends Comparable<T>>
25 implements IMultisetAggregationOperator<T, SortedMap<T, Integer>, T> {
26
27 public enum Extreme {
28 MIN, MAX;
29
30 /**
31 * @since 2.0
32 */
33 public <T> T pickFrom(SortedMap<T, Integer> nonEmptyMultiSet) {
34 switch(this) {
35 case MIN:
36 return nonEmptyMultiSet.firstKey();
37 case MAX:
38 return nonEmptyMultiSet.lastKey();
39 default:
40 return null;
41 }
42 }
43 }
44
45 private static final ExtremumOperator MIN_OP = new ExtremumOperator<>(Extreme.MIN);
46 private static final ExtremumOperator MAX_OP = new ExtremumOperator<>(Extreme.MAX);
47
48 public static <T extends Comparable<T>> ExtremumOperator<T> getMin() {
49 return MIN_OP;
50 }
51 public static <T extends Comparable<T>> ExtremumOperator<T> getMax() {
52 return MAX_OP;
53 }
54
55 Extreme extreme;
56 private ExtremumOperator(Extreme extreme) {
57 super();
58 this.extreme = extreme;
59 }
60
61 @Override
62 public String getShortDescription() {
63 String opName = getName();
64 return String.format(
65 "%s incrementally computes the %simum of java.lang.Comparable values, using the default comparison",
66 opName, opName);
67 }
68
69 @Override
70 public String getName() {
71 return extreme.name().toLowerCase();
72 }
73
74 /**
75 * @since 2.0
76 */
77 @Override
78 public SortedMap<T, Integer> createNeutral() {
79 return new TreeMap<>();
80 }
81
82 /**
83 * @since 2.0
84 */
85 @Override
86 public boolean isNeutral(SortedMap<T, Integer> result) {
87 return result.isEmpty();
88 }
89
90 /**
91 * @since 2.0
92 */
93 @Override
94 public SortedMap<T, Integer> update(SortedMap<T, Integer> oldResult, T updateValue, boolean isInsertion) {
95 oldResult.compute(updateValue, (value, c) -> {
96 int count = (c == null) ? 0 : c;
97 int result = (isInsertion) ? count+1 : count-1;
98 return (result == 0) ? null : result;
99 });
100 return oldResult;
101 }
102
103 /**
104 * @since 2.0
105 */
106 @Override
107 public T getAggregate(SortedMap<T, Integer> result) {
108 return result.isEmpty() ? null :
109 extreme.pickFrom(result);
110 }
111
112 /**
113 * @since 2.0
114 */
115 @Override
116 public T aggregateStream(Stream<T> stream) {
117 switch (extreme) {
118 case MIN:
119 return stream.min(Comparator.naturalOrder()).orElse(null);
120 case MAX:
121 return stream.max(Comparator.naturalOrder()).orElse(null);
122 default:
123 return null;
124 }
125 }
126
127 /**
128 * @since 2.4
129 */
130 @Override
131 public SortedMap<T, Integer> clone(SortedMap<T, Integer> original) {
132 return new TreeMap<T, Integer>(original);
133 }
134
135}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java
new file mode 100644
index 00000000..bf422476
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java
@@ -0,0 +1,82 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.aggregators;
10
11import java.util.OptionalDouble;
12import java.util.stream.Stream;
13
14import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
15
16/**
17 * @author Zoltan Ujhelyi
18 * @since 2.0
19 */
20public class IntegerAverageOperator implements IMultisetAggregationOperator<Integer, AverageAccumulator<Integer>, Double> {
21
22 public static final IntegerAverageOperator INSTANCE = new IntegerAverageOperator();
23
24 private IntegerAverageOperator() {
25 // Singleton, do not call.
26 }
27
28 @Override
29 public String getShortDescription() {
30 return "avg<Integer> incrementally computes the average of java.lang.Integer values";
31 }
32
33 @Override
34 public String getName() {
35 return "avg<Integer>";
36 }
37
38 @Override
39 public AverageAccumulator<Integer> createNeutral() {
40 return new AverageAccumulator<Integer>(0, 0l);
41 }
42
43 @Override
44 public boolean isNeutral(AverageAccumulator<Integer> result) {
45 return result.count == 0l;
46 }
47
48 @Override
49 public AverageAccumulator<Integer> update(AverageAccumulator<Integer> oldResult, Integer updateValue,
50 boolean isInsertion) {
51 if (isInsertion) {
52 oldResult.value += updateValue;
53 oldResult.count++;
54 } else {
55 oldResult.value -= updateValue;
56 oldResult.count--;
57 }
58 return oldResult;
59 }
60
61 @Override
62 public Double getAggregate(AverageAccumulator<Integer> result) {
63 return (result.count == 0)
64 ? null
65 : ((double)result.value)/result.count;
66 }
67
68 @Override
69 public Double aggregateStream(Stream<Integer> stream) {
70 final OptionalDouble averageOpt = stream.mapToInt(Integer::intValue).average();
71 return averageOpt.isPresent() ? averageOpt.getAsDouble() : null;
72 }
73
74 /**
75 * @since 2.4
76 */
77 @Override
78 public AverageAccumulator<Integer> clone(AverageAccumulator<Integer> original) {
79 return new AverageAccumulator<Integer>(original.value, original.count);
80 }
81
82} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java
new file mode 100644
index 00000000..18584256
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java
@@ -0,0 +1,61 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.matchers.aggregators;
10
11import java.util.stream.Stream;
12
13import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator;
14
15/**
16 * Incrementally computes the sum of java.lang.Integer values
17 * @author Gabor Bergmann
18 * @since 1.4
19 */
20public class IntegerSumOperator extends AbstractMemorylessAggregationOperator<Integer, Integer> {
21 public static final IntegerSumOperator INSTANCE = new IntegerSumOperator();
22
23 private IntegerSumOperator() {
24 // Singleton, do not call.
25 }
26
27 @Override
28 public String getShortDescription() {
29 return "sum<Integer> incrementally computes the sum of java.lang.Integer values";
30 }
31 @Override
32 public String getName() {
33 return "sum<Integer>";
34 }
35
36 @Override
37 public Integer createNeutral() {
38 return 0;
39 }
40
41 @Override
42 public boolean isNeutral(Integer result) {
43 return createNeutral().equals(result);
44 }
45
46 @Override
47 public Integer update(Integer oldResult, Integer updateValue, boolean isInsertion) {
48 return isInsertion ?
49 oldResult + updateValue :
50 oldResult - updateValue;
51 }
52
53 /**
54 * @since 2.0
55 */
56 @Override
57 public Integer aggregateStream(Stream<Integer> stream) {
58 return stream.mapToInt(Integer::intValue).sum();
59 }
60
61}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java
new file mode 100644
index 00000000..d56c9507
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java
@@ -0,0 +1,82 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.aggregators;
10
11import java.util.OptionalDouble;
12import java.util.stream.Stream;
13
14import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
15
16/**
17 * @author Zoltan Ujhelyi
18 * @since 2.0
19 */
20public class LongAverageOperator implements IMultisetAggregationOperator<Long, AverageAccumulator<Long>, Double> {
21
22 public static final LongAverageOperator INSTANCE = new LongAverageOperator();
23
24 private LongAverageOperator() {
25 // Singleton, do not call.
26 }
27
28 @Override
29 public String getShortDescription() {
30 return "avg<Integer> incrementally computes the average of java.lang.Integer values";
31 }
32
33 @Override
34 public String getName() {
35 return "avg<Integer>";
36 }
37
38 @Override
39 public AverageAccumulator<Long> createNeutral() {
40 return new AverageAccumulator<Long>(0l, 0l);
41 }
42
43 @Override
44 public boolean isNeutral(AverageAccumulator<Long> result) {
45 return result.count == 0l;
46 }
47
48 @Override
49 public AverageAccumulator<Long> update(AverageAccumulator<Long> oldResult, Long updateValue,
50 boolean isInsertion) {
51 if (isInsertion) {
52 oldResult.value += updateValue;
53 oldResult.count++;
54 } else {
55 oldResult.value -= updateValue;
56 oldResult.count--;
57 }
58 return oldResult;
59 }
60
61 @Override
62 public Double getAggregate(AverageAccumulator<Long> result) {
63 return (result.count == 0)
64 ? null
65 : ((double)result.value)/result.count;
66 }
67
68 @Override
69 public Double aggregateStream(Stream<Long> stream) {
70 final OptionalDouble averageOpt = stream.mapToLong(Long::longValue).average();
71 return averageOpt.isPresent() ? averageOpt.getAsDouble() : null;
72 }
73
74 /**
75 * @since 2.4
76 */
77 @Override
78 public AverageAccumulator<Long> clone(AverageAccumulator<Long> original) {
79 return new AverageAccumulator<Long>(original.value, original.count);
80 }
81
82} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java
new file mode 100644
index 00000000..29ded090
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java
@@ -0,0 +1,61 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.matchers.aggregators;
10
11import java.util.stream.Stream;
12
13import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator;
14
15/**
16 * Incrementally computes the sum of java.lang.Long values
17 * @author Gabor Bergmann
18 * @since 1.4
19 */
20public class LongSumOperator extends AbstractMemorylessAggregationOperator<Long, Long> {
21 public static final LongSumOperator INSTANCE = new LongSumOperator();
22
23 private LongSumOperator() {
24 // Singleton, do not call.
25 }
26
27 @Override
28 public String getShortDescription() {
29 return "sum<Long> incrementally computes the sum of java.lang.Long values";
30 }
31 @Override
32 public String getName() {
33 return "sum<Long>";
34 }
35
36 @Override
37 public Long createNeutral() {
38 return 0L;
39 }
40
41 @Override
42 public boolean isNeutral(Long result) {
43 return createNeutral().equals(result);
44 }
45
46 @Override
47 public Long update(Long oldResult, Long updateValue, boolean isInsertion) {
48 return isInsertion ?
49 oldResult + updateValue :
50 oldResult - updateValue;
51 }
52
53 /**
54 * @since 2.0
55 */
56 @Override
57 public Long aggregateStream(Stream<Long> stream) {
58 return stream.mapToLong(Long::longValue).sum();
59 }
60
61}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java
new file mode 100644
index 00000000..c25678aa
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java
@@ -0,0 +1,39 @@
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.matchers.aggregators;
10
11import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType;
12import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator;
13import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory;
14
15/**
16 * This aggregator calculates the average of the values of a selected aggregate parameter of a called pattern. The aggregate
17 * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The
18 * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the
19 * summation.
20 *
21 * @since 2.0
22 *
23 */
24@AggregatorType(
25 parameterTypes = {Integer.class, Double.class, Long.class},
26 returnTypes = {Double.class, Double.class, Double.class})
27public final class avg implements IAggregatorFactory {
28
29 @Override
30 public BoundAggregator getAggregatorLogic(Class<?> domainClass) {
31 if (Integer.class.equals(domainClass))
32 return new BoundAggregator(IntegerAverageOperator.INSTANCE, Integer.class, Double.class);
33 if (Double.class.equals(domainClass))
34 return new BoundAggregator(DoubleAverageOperator.INSTANCE, Double.class, Double.class);
35 if (Long.class.equals(domainClass))
36 return new BoundAggregator(LongAverageOperator.INSTANCE, Long.class, Double.class);
37 else throw new IllegalArgumentException();
38 }
39}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java
new file mode 100644
index 00000000..8310a0ce
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java
@@ -0,0 +1,33 @@
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.matchers.aggregators;
10
11import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType;
12import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator;
13import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory;
14
15/**
16 * An aggregator to count the number of matches a pattern has. The return of the aggregator is an non-negative integer
17 * number.
18 *
19 * @since 1.4
20 *
21 */
22@AggregatorType(parameterTypes = {Void.class}, returnTypes = {Integer.class})
23public final class count implements IAggregatorFactory {
24
25 @Override
26 public BoundAggregator getAggregatorLogic(Class<?> domainClass) {
27 if (Void.class.equals(domainClass))
28 return new BoundAggregator(null, Void.class, Integer.class);
29 else throw new IllegalArgumentException();
30 }
31
32
33}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java
new file mode 100644
index 00000000..e0236223
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java
@@ -0,0 +1,44 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.matchers.aggregators;
10
11import java.math.BigDecimal;
12import java.math.BigInteger;
13import java.util.Calendar;
14import java.util.Date;
15
16import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType;
17import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator;
18import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory;
19
20/**
21 * This aggregator calculates the maximum value of a selected aggregate parameter of a called pattern. The aggregate
22 * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The
23 * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the
24 * minimum calculation.
25 *
26 * @since 1.4
27 * @author Gabor Bergmann
28 */
29@AggregatorType(
30 // TODO T extends Comparable?
31 parameterTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class,
32 Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class},
33 returnTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class,
34 Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class})
35public final class max implements IAggregatorFactory {
36
37 @Override
38 public BoundAggregator getAggregatorLogic(Class<?> domainClass) {
39 if (Comparable.class.isAssignableFrom(domainClass))
40 return new BoundAggregator(ExtremumOperator.getMax(), domainClass, domainClass);
41 else throw new IllegalArgumentException();
42 }
43
44}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java
new file mode 100644
index 00000000..6408c57b
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java
@@ -0,0 +1,44 @@
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.matchers.aggregators;
10
11import java.math.BigDecimal;
12import java.math.BigInteger;
13import java.util.Calendar;
14import java.util.Date;
15
16import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType;
17import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator;
18import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory;
19
20/**
21 * This aggregator calculates the minimum value of a selected aggregate parameter of a called pattern. The aggregate
22 * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The
23 * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the
24 * minimum calculation.
25 *
26 * @since 1.4
27 *
28 */
29@AggregatorType(
30 // TODO T extends Comparable?
31 parameterTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class,
32 Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class},
33 returnTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class,
34 Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class})
35public final class min implements IAggregatorFactory {
36
37 @Override
38 public BoundAggregator getAggregatorLogic(Class<?> domainClass) {
39 if (Comparable.class.isAssignableFrom(domainClass))
40 return new BoundAggregator(ExtremumOperator.getMin(), domainClass, domainClass);
41 else throw new IllegalArgumentException();
42 }
43
44}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java
new file mode 100644
index 00000000..69ff2e75
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java
@@ -0,0 +1,39 @@
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.matchers.aggregators;
10
11import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType;
12import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator;
13import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory;
14
15/**
16 * This aggregator calculates the sum of the values of a selected aggregate parameter of a called pattern. The aggregate
17 * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The
18 * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the
19 * summation.
20 *
21 * @since 1.4
22 *
23 */
24@AggregatorType(
25 parameterTypes = {Integer.class, Double.class, Long.class},
26 returnTypes = {Integer.class, Double.class, Long.class})
27public final class sum implements IAggregatorFactory {
28
29 @Override
30 public BoundAggregator getAggregatorLogic(Class<?> domainClass) {
31 if (Integer.class.equals(domainClass))
32 return new BoundAggregator(IntegerSumOperator.INSTANCE, Integer.class, Integer.class);
33 if (Double.class.equals(domainClass))
34 return new BoundAggregator(DoubleSumOperator.INSTANCE, Double.class, Double.class);
35 if (Long.class.equals(domainClass))
36 return new BoundAggregator(LongSumOperator.INSTANCE, Long.class, Long.class);
37 else throw new IllegalArgumentException();
38 }
39}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java
new file mode 100644
index 00000000..1917e909
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java
@@ -0,0 +1,83 @@
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.matchers.algorithms;
10
11import java.util.Comparator;
12import java.util.Iterator;
13import java.util.NoSuchElementException;
14
15/**
16 * @author Gabor Bergmann
17 * @since 2.1
18 *
19 */
20public class OrderedIterableMerge {
21
22 private OrderedIterableMerge() {
23 // Hidden utility class constructor
24 }
25
26 /**
27 * Lazily merges two iterables, each ordered according to a given comparator.
28 * Retains order in the result, and also eliminates any duplicates that appear in both arguments.
29 */
30 public static <T> Iterable<T> mergeUniques(Iterable<T> first, Iterable<T> second, Comparator<T> comparator) {
31 return () -> new Iterator<T>() {
32 Iterator<T> firstIterator = first.iterator();
33 Iterator<T> secondIterator = second.iterator();
34 T firstItem;
35 T secondItem;
36
37 {
38 fetchFirst();
39 fetchSecond();
40 }
41
42 private T fetchFirst() {
43 T previous = firstItem;
44 if (firstIterator.hasNext())
45 firstItem = firstIterator.next();
46 else
47 firstItem = null;
48 return previous;
49 }
50 private T fetchSecond() {
51 T previous = secondItem;
52 if (secondIterator.hasNext())
53 secondItem = secondIterator.next();
54 else
55 secondItem = null;
56 return previous;
57 }
58
59 @Override
60 public boolean hasNext() {
61 return firstItem != null || secondItem != null;
62 }
63 @Override
64 public T next() {
65 if (!hasNext()) throw new NoSuchElementException();
66 if (firstItem != null && secondItem != null) {
67 if (secondItem == firstItem) { // duplicates
68 fetchFirst();
69 return fetchSecond();
70 } else if (comparator.compare(firstItem, secondItem) < 0) {
71 return fetchFirst();
72 } else {
73 return fetchSecond();
74 }
75 } else if (firstItem != null) {
76 return fetchFirst();
77 } else { // secondItem must be non-null
78 return fetchSecond();
79 }
80 }
81 };
82 }
83}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java
new file mode 100644
index 00000000..c69f08e5
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java
@@ -0,0 +1,214 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.matchers.algorithms;
11
12import java.util.Collection;
13import java.util.HashSet;
14import java.util.Iterator;
15import java.util.Map;
16import java.util.Set;
17
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19
20/**
21 * Union-find data structure implementation. Note that the implementation relies on the correct implementation of the
22 * equals method of the type parameter's class.
23 *
24 * @author Tamas Szabo
25 *
26 * @param <V>
27 * the type parameter of the element's stored in the union-find data structure
28 */
29public class UnionFind<V> {
30
31 private final Map<V, UnionFindNodeProperty<V>> nodeMap;
32 final Map<V, Set<V>> setMap;
33
34 /**
35 * Instantiate a new union-find data structure.
36 */
37 public UnionFind() {
38 nodeMap = CollectionsFactory.createMap();
39 setMap = CollectionsFactory.createMap();
40 }
41
42 /**
43 * Instantiate a new union-find data structure with the given elements as separate sets.
44 */
45 public UnionFind(Iterable<V> elements) {
46 this();
47 for (V element : elements) {
48 makeSet(element);
49 }
50 }
51
52 /**
53 * Creates a new union set from a collection of elements.
54 *
55 * @param nodes
56 * the collection of elements
57 * @return the root element
58 */
59 public V makeSet(Collection<V> nodes) {
60 if (!nodes.isEmpty()) {
61 Iterator<V> iterator = nodes.iterator();
62 V root = makeSet(iterator.next());
63 while (iterator.hasNext()) {
64 root = union(root, iterator.next());
65 }
66 return root;
67 } else {
68 return null;
69 }
70 }
71
72 /**
73 * This method creates a single set containing the given node.
74 *
75 * @param node
76 * the root node of the set
77 * @return the root element
78 */
79 public V makeSet(V node) {
80 if (!nodeMap.containsKey(node)) {
81 UnionFindNodeProperty<V> prop = new UnionFindNodeProperty<V>(0, node);
82 nodeMap.put(node, prop);
83 Set<V> set = new HashSet<V>();
84 set.add(node);
85 setMap.put(node, set);
86 }
87 return node;
88 }
89
90 /**
91 * Find method with path compression.
92 *
93 * @param node
94 * the node to find
95 * @return the root node of the set in which the given node can be found
96 */
97 public V find(V node) {
98 UnionFindNodeProperty<V> prop = nodeMap.get(node);
99
100 if (prop != null) {
101 if (prop.parent.equals(node)) {
102 return node;
103 } else {
104 prop.parent = find(prop.parent);
105 return prop.parent;
106 }
107 }
108 return null;
109 }
110
111 /**
112 * Union by rank implementation of the two sets which contain x and y; x and/or y can be a single element from the
113 * universe.
114 *
115 * @param x
116 * set or single element of the universe
117 * @param y
118 * set or single element of the universe
119 * @return the new root of the two sets
120 */
121 public V union(V x, V y) {
122 V xRoot = find(x);
123 V yRoot = find(y);
124
125 if ((xRoot == null) || (yRoot == null)) {
126 return union( (xRoot == null) ? makeSet(x) : xRoot, (yRoot == null) ? makeSet(y) : yRoot);
127 }
128 else if (!xRoot.equals(yRoot)) {
129 UnionFindNodeProperty<V> xRootProp = nodeMap.get(xRoot);
130 UnionFindNodeProperty<V> yRootProp = nodeMap.get(yRoot);
131
132 if (xRootProp.rank < yRootProp.rank) {
133 xRootProp.parent = yRoot;
134 setMap.get(yRoot).addAll(setMap.get(xRoot));
135 setMap.remove(xRoot);
136 return yRoot;
137 } else {// (xRootProp.rank >= yRootProp.rank)
138 yRootProp.parent = xRoot;
139 yRootProp.rank = (xRootProp.rank == yRootProp.rank) ? yRootProp.rank + 1 : yRootProp.rank;
140 setMap.get(xRoot).addAll(setMap.get(yRoot));
141 setMap.remove(yRoot);
142 return xRoot;
143 }
144 } else {
145 return xRoot;
146 }
147 }
148
149 /**
150 * Places the given elements in to the same partition.
151 */
152 public void unite(Set<V> elements) {
153 if (elements.size() > 1) {
154 V current = null;
155 for (V element : elements) {
156 if (current != null) {
157 if (getPartition(element) != null) {
158 union(current, element);
159 }
160 } else {
161 if (getPartition(element) != null) {
162 current = element;
163 }
164 }
165 }
166 }
167 }
168
169 /**
170 * Delete the set whose root is the given node.
171 *
172 * @param root
173 * the root node
174 */
175 public void deleteSet(V root) {
176 // if (setMap.containsKey(root))
177 for (V n : setMap.get(root)) {
178 nodeMap.remove(n);
179 }
180 setMap.remove(root);
181 }
182
183 /**
184 * Returns if all given elements are in the same partition.
185 */
186 public boolean isSameUnion(Set<V> elements) {
187 for (Set<V> partition : setMap.values()) {
188 if (partition.containsAll(elements)) {
189 return true;
190 }
191 }
192 return false;
193 }
194
195
196 /**
197 * Returns the partition in which the given element can be found, or null otherwise.
198 */
199 public Set<V> getPartition(V element) {
200 V root = find(element);
201 return setMap.get(root);
202 }
203
204 /**
205 * Returns all partitions.
206 */
207 public Collection<Set<V>> getPartitions() {
208 return setMap.values();
209 }
210
211 public Set<V> getPartitionHeads() {
212 return setMap.keySet();
213 }
214}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java
new file mode 100644
index 00000000..82852f9c
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java
@@ -0,0 +1,32 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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.matchers.algorithms;
11
12public class UnionFindNodeProperty<V> {
13
14 public int rank;
15 public V parent;
16
17 public UnionFindNodeProperty() {
18 this.rank = 0;
19 this.parent = null;
20 }
21
22 public UnionFindNodeProperty(int rank, V parent) {
23 super();
24 this.rank = rank;
25 this.parent = parent;
26 }
27
28 @Override
29 public String toString() {
30 return "[rank:" + rank + ", parent:" + parent.toString() + "]";
31 }
32}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java
new file mode 100644
index 00000000..317293bf
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java
@@ -0,0 +1,36 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.matchers.backend;
10
11import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IRewriterTraceCollector;
12import tools.refinery.viatra.runtime.matchers.psystem.rewriters.NopTraceCollector;
13
14/**
15 * Query evaluation hints applicable to any engine
16 * @since 1.6
17 *
18 */
19public final class CommonQueryHintOptions {
20
21 private CommonQueryHintOptions() {
22 // Hiding constructor for utility class
23 }
24
25 /**
26 * This hint instructs the query backends to record trace information into the given trace collector
27 */
28 public static final QueryHintOption<IRewriterTraceCollector> normalizationTraceCollector =
29 hintOption("normalizationTraceCollector", NopTraceCollector.INSTANCE);
30
31 // internal helper for conciseness
32 private static <T> QueryHintOption<T> hintOption(String hintKeyLocalName, T defaultValue) {
33 return new QueryHintOption<>(CommonQueryHintOptions.class, hintKeyLocalName, defaultValue);
34 }
35
36}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java
new file mode 100644
index 00000000..40853f46
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java
@@ -0,0 +1,89 @@
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.matchers.backend;
10
11import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess;
12import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference;
13import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
14
15/**
16 * Function object that specifies how hints (including backend preferences) shall propagate through pattern calls.
17 *
18 * <p> A few useful default implementations are included as static fields.
19 *
20 * <p> As of 2.1, only suppported by the local search backend, and only if the pattern call is not flattened.
21 *
22 * @author Gabor Bergmann
23 * @since 2.1
24 */
25@FunctionalInterface
26public interface ICallDelegationStrategy {
27
28 /**
29 * Specifies how hints (including backend preferences) shall propagate through pattern calls.
30 *
31 * @param call a {@link PConstraint} in a query that calls another query.
32 * @param callerHint a hint under which the calling pattern is evaluated,
33 * @param callerBackend the actual backend evaluating the calling pattern.
34 * @param calleeHintProvider the provider of hints for the called pattern.
35 * @return the hints, including backend selection,
36 * that the backend responsible for the caller pattern must specify when
37 * requesting the {@link IQueryResultProvider} for the called pattern via {@link IQueryResultProviderAccess}.
38 */
39 public QueryEvaluationHint transformHints(IQueryReference call,
40 QueryEvaluationHint callerHint,
41 IQueryBackend callerBackend,
42 IQueryBackendHintProvider calleeHintProvider);
43
44
45 /**
46 * Options known for callee are used to override caller options, except the backend selection.
47 * Always use the same backend for the callee and the caller, regardless what is specified for the callee pattern.
48 * @author Gabor Bergmann
49 */
50 public static final ICallDelegationStrategy FULL_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> {
51 QueryEvaluationHint calleeHint =
52 calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery());
53 QueryEvaluationHint result =
54 callerHint == null ? calleeHint : callerHint.overrideBy(calleeHint);
55
56 QueryEvaluationHint backendAdhesion = new QueryEvaluationHint(
57 null /* settings-ignorant */, callerBackend.getFactory());
58 result = result.overrideBy(backendAdhesion);
59 return result;
60 };
61 /**
62 * Options known for callee are used to override caller options, including the backend selection.
63 * If callee does not specify a backend requirement, the backend of the caller is kept.
64 * @author Gabor Bergmann
65 */
66 public static final ICallDelegationStrategy PARTIAL_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> {
67 QueryEvaluationHint backendAdhesion = new QueryEvaluationHint(
68 null /* settings-ignorant */, callerBackend.getFactory());
69
70 QueryEvaluationHint result =
71 callerHint == null ? backendAdhesion : callerHint.overrideBy(backendAdhesion);
72
73 QueryEvaluationHint calleeHint = calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery());
74 result = result.overrideBy(calleeHint);
75
76 return result;
77 };
78 /**
79 * Options known for callee are used to override caller options, including the backend selection.
80 * Always use the backend specified for the callee (or the default if none), regardless of the backend of the caller.
81 * @author Gabor Bergmann
82 */
83 public static final ICallDelegationStrategy NO_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> {
84 QueryEvaluationHint calleeHint = calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery());
85 return callerHint == null ? calleeHint : callerHint.overrideBy(calleeHint);
86 };
87
88
89}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java
new file mode 100644
index 00000000..104b68a8
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java
@@ -0,0 +1,26 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.matchers.backend;
10
11/**
12 * Implementations of this interface can be used to decide whether a matcher created by an arbitrary backend can
13 * potentially be used as a substitute for another matcher.
14 *
15 * @author Grill Balázs
16 * @since 1.4
17 *
18 */
19public interface IMatcherCapability {
20
21 /**
22 * Returns true if matchers of this capability can be used as a substitute for a matcher implementing the given capability
23 */
24 public boolean canBeSubstitute(IMatcherCapability capability);
25
26}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java
new file mode 100644
index 00000000..c85f10a4
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java
@@ -0,0 +1,68 @@
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.matchers.backend;
10
11import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
12import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
13
14/**
15 * Internal interface for a VIATRA query specification. Each query is associated with a pattern. Methods instantiate a matcher
16 * of the pattern with various parameters.
17 *
18 * @author Bergmann Gábor
19 * @since 0.9
20 * @noextend This interface is not intended to be extended by users of the VIATRA framework, and should only be used by the query engine
21 */
22public interface IQueryBackend {
23
24 /**
25 * @return true iff this backend is incremental, i.e. it caches the results of queries for quick retrieval,
26 * and can provide update notifications on result set changes.
27 */
28 public boolean isCaching();
29
30 /**
31 * Returns a result provider for a given query. Repeated calls may return the same instance.
32 * @throws ViatraQueryRuntimeException
33 */
34 public IQueryResultProvider getResultProvider(PQuery query);
35
36 /**
37 * Returns a result provider for a given query. Repeated calls may return the same instance.
38 * @param optional hints that may override engine and query defaults (as provided by {@link IQueryBackendHintProvider}). Can be null.
39 * @throws ViatraQueryRuntimeException
40 * @since 1.4
41 */
42 public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint hints);
43
44 /**
45 * Returns an existing result provider for a given query, if it was previously constructed, returns null otherwise.
46 * Will not construct and initialize new result providers.
47 */
48 public IQueryResultProvider peekExistingResultProvider(PQuery query);
49
50 /**
51 * Propagates all pending updates in this query backend. The implementation of this method is optional, and it
52 * can be ignored entirely if the backend does not delay updates.
53 * @since 1.6
54 */
55 public void flushUpdates();
56
57 /**
58 * Disposes the query backend.
59 */
60 public abstract void dispose();
61
62 /**
63 * @return the factory that created this backend, if this functionality is supported
64 * @since 2.1
65 */
66 public IQueryBackendFactory getFactory();
67
68}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java
new file mode 100644
index 00000000..e264ab3f
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java
@@ -0,0 +1,52 @@
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.matchers.backend;
10
11import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
12import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
13
14/**
15 * A Query Backend Factory identifies a query evaluator implementation, and can create an evaluator instance (an {@link IQueryBackend}) tied to a specific VIATRA Query engine upon request.
16 *
17 * <p> The factory is used as a lookup key for the backend instance,
18 * therefore implementors should either be singletons, or implement equals() / hashCode() accordingly.
19 *
20 * @author Bergmann Gabor
21 *
22 */
23public interface IQueryBackendFactory {
24
25 /**
26 * Creates a new {@link IQueryBackend} instance tied to the given context elements.
27 *
28 * @return an instance of the class returned by {@link #getBackendClass()} that operates in the given context.
29 * @since 1.5
30 */
31 public IQueryBackend
32 create(IQueryBackendContext context);
33
34
35 /**
36 * The backend instances created by this factory are guaranteed to conform to the returned class.
37 */
38 public Class<? extends IQueryBackend> getBackendClass();
39
40 /**
41 * Calculate the required capabilities, which are needed to execute the given pattern
42 *
43 * @since 1.4
44 */
45 public IMatcherCapability calculateRequiredCapability(PQuery query, QueryEvaluationHint hint);
46
47 /**
48 * Returns whether the current backend is caching
49 * @since 2.0
50 */
51 public boolean isCaching();
52}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java
new file mode 100644
index 00000000..8787814e
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java
@@ -0,0 +1,46 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.backend;
10
11/**
12 * A provider interface for {@link IQueryBackendFactory} instances.
13 * @since 2.0
14 */
15public interface IQueryBackendFactoryProvider {
16
17 /**
18 * Returns a query backend factory instance. The method should return the same instance in case of repeated calls.
19 */
20 IQueryBackendFactory getFactory();
21
22 /**
23 * Returns whether the given query backend should be considered as system default. If multiple backends are
24 * registered as system default, it is undefined which one will be chosen.
25 */
26 default boolean isSystemDefaultEngine() {
27 return false;
28 }
29
30 /**
31 * Returns whether the given query backend should be considered as system default search backend. If multiple
32 * backends are registered as system default, it is undefined which one will be chosen.
33 */
34 default boolean isSystemDefaultSearchBackend() {
35 return false;
36 }
37
38
39 /**
40 * Returns whether the given query backend should be considered as system default caching backend. If multiple
41 * backends are registered as system default, it is undefined which one will be chosen.
42 */
43 default boolean isSystemDefaultCachingBackend() {
44 return false;
45 }
46}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java
new file mode 100644
index 00000000..9bb76349
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java
@@ -0,0 +1,32 @@
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.matchers.backend;
10
11import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
12
13/**
14 * Provides query evaluation hints consisting of the Engine default hints and
15 * the hints provided by the pattern itself.
16 *
17 * @author Bergmann Gabor
18 * @since 0.9
19 * @noimplement This interface is not intended to be implemented by clients, except in the tools.refinery.viatra.runtime plugin.
20 */
21public interface IQueryBackendHintProvider {
22
23 /**
24 * Suggests query evaluation hints regarding a query. The returned hints reflects the default hints of the
25 * query engine merged with the hints provided by the pattern itself. These can be overridden via specific
26 * advanced API of the engine.
27 *
28 * @since 1.4
29 */
30 QueryEvaluationHint getQueryEvaluationHint(PQuery query);
31
32}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java
new file mode 100644
index 00000000..cd7d050f
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java
@@ -0,0 +1,202 @@
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.matchers.backend;
10
11import java.util.Optional;
12import java.util.stream.Stream;
13
14import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper;
15import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.Accuracy;
19
20/**
21 * An internal interface of the query backend that provides results of a given query.
22 * @author Bergmann Gabor
23 * @noimplement This interface is not intended to be implemented by clients.
24 */
25public interface IQueryResultProvider {
26
27 /**
28 * Decides whether there are any matches of the pattern that conform to the given fixed values of some parameters.
29 *
30 * @param parameters
31 * array where each non-null element binds the corresponding pattern parameter to a fixed value.
32 * @pre size of input array must be equal to the number of parameters.
33 * @since 2.0
34 */
35 public boolean hasMatch(Object[] parameters);
36
37 /**
38 * Decides whether there are any matches of the pattern that conform to the given fixed values of some parameters.
39 *
40 * @param parameterSeedMask
41 * a mask that extracts those parameters of the query (from the entire parameter list) that should be
42 * bound to a fixed value
43 * @param parameters
44 * the tuple of fixed values restricting the match set to be considered, in the same order as given in
45 * parameterSeedMask, so that for each considered match tuple,
46 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold
47 * @since 2.0
48 */
49 public boolean hasMatch(TupleMask parameterSeedMask, ITuple projectedParameterSeed);
50
51 /**
52 * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters.
53 *
54 * @param parameters
55 * array where each non-null element binds the corresponding pattern parameter to a fixed value.
56 * @pre size of input array must be equal to the number of parameters.
57 * @return the number of pattern matches found.
58 */
59 public int countMatches(Object[] parameters);
60
61 /**
62 * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters.
63 *
64 * @param parameterSeedMask
65 * a mask that extracts those parameters of the query (from the entire parameter list) that should be
66 * bound to a fixed value
67 * @param parameters
68 * the tuple of fixed values restricting the match set to be considered, in the same order as given in
69 * parameterSeedMask, so that for each considered match tuple,
70 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold
71 * @return the number of pattern matches found.
72 * @since 1.7
73 */
74 public int countMatches(TupleMask parameterSeedMask, ITuple projectedParameterSeed);
75
76 /**
77 * Gives an estimate of the number of different groups the matches are projected into by the given mask
78 * (e.g. for an identity mask, this means the full match set size). The estimate must meet the required accuracy.
79 *
80 * <p> If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned.
81 * In other words, query backends may deny an answer, or do their best to give an estimate without actually determining the match set of the query.
82 * However, caching backends are expected to simply return the indexed (projection) size, initialized on-demand if necessary.
83 *
84 * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask.
85 *
86 * @return if available, an estimate of the cardinality of the projection of the match set, with the desired accuracy.
87 *
88 * @since 2.1
89 */
90 public Optional<Long> estimateCardinality(TupleMask groupMask, Accuracy requiredAccuracy);
91
92 /**
93 * Gives an estimate of the average size of different groups the matches are projected into by the given mask
94 * (e.g. for an identity mask, this means 1, while for an empty mask, the result is match set size).
95 * The estimate must meet the required accuracy.
96 *
97 * <p> If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned.
98 * In other words, query backends may deny an answer, or do their best to give an estimate without actually determining the match set of the query.
99 * However, caching backends are expected to simply return the exact value from the index, initialized on-demand if necessary.
100 *
101 * <p> For an empty match set, zero is acceptable as an exact answer.
102 *
103 * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask.
104 *
105 * @return if available, an estimate of the average size of each projection group of the match set, with the desired accuracy.
106 *
107 * @since 2.1
108 */
109 public default Optional<Double> estimateAverageBucketSize(TupleMask groupMask, Accuracy requiredAccuracy) {
110 return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, this::estimateCardinality);
111 }
112
113 /**
114 * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters.
115 * Neither determinism nor randomness of selection is guaranteed.
116 *
117 * @param parameters
118 * array where each non-null element binds the corresponding pattern parameter to a fixed value.
119 * @pre size of input array must be equal to the number of parameters.
120 * @return a match represented in the internal {@link Tuple} representation.
121 * @since 2.0
122 */
123 public Optional<Tuple> getOneArbitraryMatch(Object[] parameters);
124
125 /**
126 * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters.
127 * Neither determinism nor randomness of selection is guaranteed.
128 *
129 * @param parameterSeedMask
130 * a mask that extracts those parameters of the query (from the entire parameter list) that should be
131 * bound to a fixed value
132 * @param parameters
133 * the tuple of fixed values restricting the match set to be considered, in the same order as given in
134 * parameterSeedMask, so that for each considered match tuple,
135 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold
136 * @return a match represented in the internal {@link Tuple} representation.
137 * @since 2.0
138 */
139 public Optional<Tuple> getOneArbitraryMatch(TupleMask parameterSeedMask, ITuple parameters);
140
141 /**
142 * Returns the set of all matches of the pattern that conform to the given fixed values of some parameters.
143 *
144 * @param parameters
145 * array where each non-null element binds the corresponding pattern parameter to a fixed value.
146 * @pre size of input array must be equal to the number of parameters.
147 * @return matches represented in the internal {@link Tuple} representation.
148 * @since 2.0
149 */
150 public Stream<Tuple> getAllMatches(Object[] parameters);
151
152 /**
153 * Returns the set of all matches of the pattern that conform to the given fixed values of some parameters.
154 *
155 * @param parameterSeedMask
156 * a mask that extracts those parameters of the query (from the entire parameter list) that should be
157 * bound to a fixed value
158 * @param parameters
159 * the tuple of fixed values restricting the match set to be considered, in the same order as given in
160 * parameterSeedMask, so that for each considered match tuple,
161 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold
162 * @return matches represented in the internal {@link Tuple} representation.
163 * @since 2.0
164 */
165 public Stream<Tuple> getAllMatches(TupleMask parameterSeedMask, ITuple parameters);
166
167 /**
168 * The underlying query evaluator backend.
169 */
170 public IQueryBackend getQueryBackend();
171
172 /**
173 * Internal method that registers low-level callbacks for match appearance and disappearance.
174 *
175 * <p>
176 * <b>Caution: </b> This is a low-level callback that is invoked when the pattern matcher is not necessarily in a
177 * consistent state yet. Importantly, no model modification permitted during the callback.
178 *
179 * <p>
180 * The callback can be unregistered via invoking {@link #removeUpdateListener(Object)} with the same tag.
181 *
182 * @param listener
183 * the listener that will be notified of each new match that appears or disappears, starting from now.
184 * @param listenerTag
185 * a tag by which to identify the listener for later removal by {@link #removeUpdateListener(Object)}.
186 * @param fireNow
187 * if true, the insertion update allback will be immediately invoked on all current matches as a one-time effect.
188 *
189 * @throws UnsupportedOperationException if this is a non-incremental backend
190 * (i.e. {@link IQueryBackend#isCaching()} on {@link #getQueryBackend()} returns false)
191 */
192 public void addUpdateListener(final IUpdateable listener, final Object listenerTag, boolean fireNow);
193
194 /**
195 * Removes an existing listener previously registered with the given tag.
196 *
197 * @throws UnsupportedOperationException if this is a non-incremental backend
198 * (i.e. {@link IQueryBackend#isCaching()} on {@link #getQueryBackend()} returns false)
199 */
200 public void removeUpdateListener(final Object listenerTag);
201
202}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java
new file mode 100644
index 00000000..baf7144a
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java
@@ -0,0 +1,27 @@
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.matchers.backend;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12
13/**
14 * Internal interface for the query backend to singal an update to a query result.
15 * @author Bergmann Gabor
16 * @since 0.9
17 *
18 */
19public interface IUpdateable {
20
21 /**
22 * This callback method must be free of exceptions, even {@link RuntimeException}s (though not {@link Error}s).
23 * @param updateElement the tuple that is changed
24 * @param isInsertion true if the tuple appeared in the result set, false if disappeared from the result set
25 */
26 public void update(Tuple updateElement, boolean isInsertion);
27}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java
new file mode 100644
index 00000000..eab92128
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java
@@ -0,0 +1,241 @@
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.matchers.backend;
10
11import java.util.AbstractMap;
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.Map;
15import java.util.Objects;
16import java.util.stream.Collectors;
17
18import tools.refinery.viatra.runtime.matchers.util.Preconditions;
19
20/**
21 * Provides VIATRA Query with additional hints on how a query should be evaluated. The same hint can be provided to multiple queries.
22 *
23 * <p> This class is immutable. Overriding options will create a new instance.
24 *
25 * <p>
26 * Here be dragons: for advanced users only.
27 *
28 * @author Bergmann Gabor
29 *
30 */
31public class QueryEvaluationHint {
32
33 /**
34 * @since 2.0
35 *
36 */
37 public enum BackendRequirement {
38 /**
39 * The current hint does not specify any backend requirement
40 */
41 UNSPECIFIED,
42 /**
43 * The current hint specifies that the default search backend of the engine should be used
44 */
45 DEFAULT_SEARCH,
46 /**
47 * The current hint specifies that the default caching backend of the engine should be used
48 */
49 DEFAULT_CACHING,
50 /**
51 * The current hint specifies that a specific backend is to be used
52 */
53 SPECIFIC
54 }
55
56 final IQueryBackendFactory queryBackendFactory;
57 final Map<QueryHintOption<?>, Object> backendHintSettings;
58 final BackendRequirement requirement;
59
60 /**
61 * Specifies the suggested query backend requirements, and value settings for additional backend-specific options.
62 *
63 * <p>
64 * The backend requirement type must not be {@link BackendRequirement#SPECIFIC} - for that case, use the constructor
65 * {@link #QueryEvaluationHint(Map, IQueryBackendFactory)}.
66 *
67 * @param backendHintSettings
68 * if non-null, each entry in the map overrides backend-specific options regarding query evaluation
69 * (null-valued map entries permitted to erase hints); passing null means default options associated with
70 * the query
71 * @param backendRequirementType
72 * defines the kind of backend requirement
73 * @since 2.0
74 */
75 public QueryEvaluationHint(Map<QueryHintOption<?>, Object> backendHintSettings, BackendRequirement backendRequirementType) {
76 super();
77 Preconditions.checkArgument(backendRequirementType != null, "Specific requirement needs to be set");
78 Preconditions.checkArgument(backendRequirementType != BackendRequirement.SPECIFIC, "Specific backend requirement needs providing a corresponding backend type");
79 this.queryBackendFactory = null;
80 this.requirement = backendRequirementType;
81 this.backendHintSettings = (backendHintSettings == null)
82 ? Collections.<QueryHintOption<?>, Object> emptyMap()
83 : new HashMap<>(backendHintSettings);
84 }
85
86 /**
87 * Specifies the suggested query backend, and value settings for additional backend-specific options. The first
88 * parameter can be null; if the second parameter is null, it is expected that the other constructor is called
89 * instead with a {@link BackendRequirement#UNSPECIFIED} parameter.
90 *
91 * @param backendHintSettings
92 * if non-null, each entry in the map overrides backend-specific options regarding query evaluation
93 * (null-valued map entries permitted to erase hints); passing null means default options associated with
94 * the query
95 * @param queryBackendFactory
96 * overrides the query evaluator algorithm; passing null retains the default algorithm associated with
97 * the query
98 * @since 1.5
99 */
100 public QueryEvaluationHint(
101 Map<QueryHintOption<?>, Object> backendHintSettings,
102 IQueryBackendFactory queryBackendFactory) {
103 super();
104 this.queryBackendFactory = queryBackendFactory;
105 this.requirement = (queryBackendFactory == null) ? BackendRequirement.UNSPECIFIED : BackendRequirement.SPECIFIC;
106 this.backendHintSettings = (backendHintSettings == null)
107 ? Collections.<QueryHintOption<?>, Object> emptyMap()
108 : new HashMap<>(backendHintSettings);
109 }
110
111 /**
112 * Returns the backend requirement described by this hint. If a specific backend is required, that can be queried by {@link #getQueryBackendFactory()}.
113 * @since 2.0
114 */
115 public BackendRequirement getQueryBackendRequirementType() {
116 return requirement;
117 }
118
119 /**
120 * A suggestion for choosing the query evaluator algorithm.
121 *
122 * <p>
123 * Returns null iff {@link #getQueryBackendRequirementType()} does not return {@link BackendRequirement#SPECIFIC};
124 * in such cases a corresponding default backend is selected inside the engine
125 */
126 public IQueryBackendFactory getQueryBackendFactory() {
127 return queryBackendFactory;
128 }
129
130 /**
131 * Each entry in the immutable map overrides backend-specific options regarding query evaluation.
132 *
133 * <p>The map is non-null, even if empty.
134 * Null-valued map entries are also permitted to erase hints via {@link #overrideBy(QueryEvaluationHint)}.
135 *
136 * @since 1.5
137 */
138 public Map<QueryHintOption<?>, Object> getBackendHintSettings() {
139 return backendHintSettings;
140 }
141
142
143 /**
144 * Override values in this hint and return a consolidated instance.
145 *
146 * @since 1.4
147 */
148 public QueryEvaluationHint overrideBy(QueryEvaluationHint overridingHint){
149 if (overridingHint == null)
150 return this;
151
152 BackendRequirement overriddenRequirement = this.getQueryBackendRequirementType();
153 if (overridingHint.getQueryBackendRequirementType() != BackendRequirement.UNSPECIFIED) {
154 overriddenRequirement = overridingHint.getQueryBackendRequirementType();
155 }
156 Map<QueryHintOption<?>, Object> hints = new HashMap<>(this.getBackendHintSettings());
157 if (overridingHint.getBackendHintSettings() != null) {
158 hints.putAll(overridingHint.getBackendHintSettings());
159 }
160 if (overriddenRequirement == BackendRequirement.SPECIFIC) {
161 IQueryBackendFactory factory = this.getQueryBackendFactory();
162 if (overridingHint.getQueryBackendFactory() != null) {
163 factory = overridingHint.getQueryBackendFactory();
164 }
165 return new QueryEvaluationHint(hints, factory);
166 } else {
167 return new QueryEvaluationHint(hints, overriddenRequirement);
168 }
169 }
170
171 /**
172 * Returns whether the given hint option is overridden.
173 * @since 1.5
174 */
175 public boolean isOptionOverridden(QueryHintOption<?> option) {
176 return getBackendHintSettings().containsKey(option);
177 }
178
179 /**
180 * Returns the value of the given hint option from the given hint collection, or null if not defined.
181 * @since 1.5
182 */
183 @SuppressWarnings("unchecked")
184 public <HintValue> HintValue getValueOrNull(QueryHintOption<HintValue> option) {
185 return (HintValue) getBackendHintSettings().get(option);
186 }
187
188 /**
189 * Returns the value of the given hint option from the given hint collection, or the default value if not defined.
190 * Intended to be called by backends to find out the definitive value that should be considered.
191 * @since 1.5
192 */
193 public <HintValue> HintValue getValueOrDefault(QueryHintOption<HintValue> option) {
194 return option.getValueOrDefault(this);
195 }
196
197 @Override
198 public int hashCode() {
199 return Objects.hash(backendHintSettings, queryBackendFactory, requirement);
200 }
201
202 @Override
203 public boolean equals(Object obj) {
204 if (this == obj)
205 return true;
206 if (obj == null)
207 return false;
208 if (getClass() != obj.getClass())
209 return false;
210 QueryEvaluationHint other = (QueryEvaluationHint) obj;
211 return Objects.equals(backendHintSettings, other.backendHintSettings)
212 &&
213 Objects.equals(queryBackendFactory, other.queryBackendFactory)
214 &&
215 Objects.equals(requirement, other.requirement)
216 ;
217 }
218
219 @Override
220 public String toString() {
221 StringBuilder sb = new StringBuilder();
222
223 if (getQueryBackendFactory() != null)
224 sb.append("backend: ").append(getQueryBackendFactory().getBackendClass().getSimpleName());
225 if (! backendHintSettings.isEmpty()) {
226 sb.append("hints: ");
227 if(backendHintSettings instanceof AbstractMap){
228 sb.append(backendHintSettings.toString());
229 } else {
230 // we have to iterate on the contents
231
232 String joinedHintMap = backendHintSettings.entrySet().stream()
233 .map(setting -> setting.getKey() + "=" + setting.getValue()).collect(Collectors.joining(", "));
234 sb.append('{').append(joinedHintMap).append('}');
235 }
236 }
237
238 final String result = sb.toString();
239 return result.isEmpty() ? "defaults" : result;
240 }
241}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java
new file mode 100644
index 00000000..2c6bb4de
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java
@@ -0,0 +1,122 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.matchers.backend;
10
11import java.util.Map;
12import java.util.Objects;
13
14/**
15 * Each instance of this class corresponds to a given hint option.
16 *
17 * It is recommended to expose options to clients (and query backends) as public static fields.
18 *
19 * @author Gabor Bergmann
20 * @since 1.5
21 */
22public class QueryHintOption<HintValue> {
23
24 private String optionQualifiedName;
25 private HintValue defaultValue;
26
27 /**
28 * Instantiates an option object with the given name and default value.
29 */
30 public QueryHintOption(String optionQualifiedName, HintValue defaultValue) {
31 this.optionQualifiedName = optionQualifiedName;
32 this.defaultValue = defaultValue;
33 }
34
35 /**
36 * This is the recommended constructor for hint options defined as static fields within an enclosing class.
37 * Combines the qualified name of the hint from the (qualified) name of the enclosing class and a local name (unique within that class).
38 */
39 public <T extends HintValue> QueryHintOption(Class<?> optionNamespace, String optionLocalName, T defaultValue) {
40 this(String.format("%s@%s", optionLocalName, optionNamespace.getName()), defaultValue);
41 }
42
43 /**
44 * Returns the qualified name, a unique string identifier of the option.
45 */
46 public String getQualifiedName() {
47 return optionQualifiedName;
48 }
49
50 /**
51 * Returns the default value of this hint option, which is to be used by query backends in the case no overriding value is assigned.
52 */
53 public HintValue getDefaultValue() {
54 return defaultValue;
55 }
56
57 /**
58 * Returns the value of this hint option from the given hint collection, or the default value if not defined.
59 * Intended to be called by backends to find out the definitive value that should be considered.
60 */
61 @SuppressWarnings("unchecked")
62 public HintValue getValueOrDefault(QueryEvaluationHint hints) {
63 Object value = hints.getValueOrNull(this);
64 if (value == null)
65 return getDefaultValue();
66 else {
67 return (HintValue) value;
68 }
69 }
70
71
72 /**
73 * Returns the value of this hint option from the given hint collection, or null if not defined.
74 */
75 public HintValue getValueOrNull(QueryEvaluationHint hints) {
76 return hints.getValueOrNull(this);
77 }
78
79 /**
80 * Returns whether this hint option is defined in the given hint collection.
81 */
82 public boolean isOverriddenIn(QueryEvaluationHint hints) {
83 return hints.isOptionOverridden(this);
84 }
85
86 /**
87 * Puts a value of this hint option into an option-to-value map.
88 *
89 * <p> This method is offered in lieu of a builder API.
90 * Use this method on any number of hint options in order to populate an option-value map.
91 * Then instantiate the immutable {@link QueryEvaluationHint} using the map.
92 *
93 * @see #insertValueIfNondefault(Map, Object)
94 * @return the hint value that was previously present in the map under this hint option, carrying over the semantics of {@link Map#put(Object, Object)}.
95 */
96 @SuppressWarnings("unchecked")
97 public HintValue insertOverridingValue(Map<QueryHintOption<?>, Object> hints, HintValue overridingValue) {
98 return (HintValue) hints.put(this, overridingValue);
99 }
100
101 /**
102 * Puts a value of this hint option into an option-to-value map, if the given value differs from the default value of the option.
103 * If the default value is provided instead, then the map is not updated.
104 *
105 * <p> This method is offered in lieu of a builder API.
106 * Use this method on any number of hint options in order to populate an option-value map.
107 * Then instantiate the immutable {@link QueryEvaluationHint} using the map.
108 *
109 * @see #insertOverridingValue(Map, Object)
110 * @since 2.0
111 */
112 public void insertValueIfNondefault(Map<QueryHintOption<?>, Object> hints, HintValue overridingValue) {
113 if (!Objects.equals(defaultValue, overridingValue))
114 hints.put(this, overridingValue);
115 }
116
117 @Override
118 public String toString() {
119 return optionQualifiedName;
120 }
121
122}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java
new file mode 100644
index 00000000..6ec6d53e
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java
@@ -0,0 +1,74 @@
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.matchers.backend;
10
11import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess;
12import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference;
13import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
14
15/**
16 * Uniform way of requesting result providers for pattern calls within queries.
17 * Intended users are query backends, for calling other backends to deliver results of dependee queries.
18 *
19 * @author Gabor Bergmann
20 * @since 2.1
21 */
22public class ResultProviderRequestor {
23 IQueryBackend callerBackend;
24 IQueryResultProviderAccess resultProviderAccess;
25 IQueryBackendHintProvider hintProvider;
26 ICallDelegationStrategy delegationStrategy;
27 QueryEvaluationHint callerHint;
28 QueryEvaluationHint universalOverride;
29
30
31 /**
32 * @param callerBackend the actual backend evaluating the calling pattern.
33 * @param resultProviderAccess
34 * @param hintProvider
35 * @param delegationStrategy
36 * @param callerHint a hint under which the calling pattern is evaluated,
37 * @param universalOverride if non-null, overrides the hint with extra options <i>after</i> the {@link ICallDelegationStrategy}
38 */
39 public ResultProviderRequestor(IQueryBackend callerBackend, IQueryResultProviderAccess resultProviderAccess,
40 IQueryBackendHintProvider hintProvider, ICallDelegationStrategy delegationStrategy,
41 QueryEvaluationHint callerHint, QueryEvaluationHint universalOverride) {
42 super();
43 this.callerBackend = callerBackend;
44 this.resultProviderAccess = resultProviderAccess;
45 this.hintProvider = hintProvider;
46 this.delegationStrategy = delegationStrategy;
47 this.callerHint = callerHint;
48 this.universalOverride = universalOverride;
49 }
50
51
52
53
54 /**
55 *
56 * @param call a {@link PConstraint} in a query that calls another query.
57 * @param spotOverride if non-null, overrides the hint with extra options <i>after</i> the {@link ICallDelegationStrategy}
58 * and the universal override specified in the constructor
59 * @return the obtained result provider
60 */
61 public IQueryResultProvider requestResultProvider(IQueryReference call, QueryEvaluationHint spotOverride) {
62 QueryEvaluationHint hints =
63 delegationStrategy.transformHints(call, callerHint, callerBackend, hintProvider);
64
65 if (universalOverride != null)
66 hints = hints.overrideBy(universalOverride);
67
68 if (spotOverride != null)
69 hints = hints.overrideBy(spotOverride);
70
71 return resultProviderAccess.getResultProvider(call.getReferredQuery(), hints);
72 }
73
74}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java
new file mode 100644
index 00000000..99611758
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java
@@ -0,0 +1,69 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.matchers.context;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Comparator;
14import java.util.HashMap;
15import java.util.Map;
16import java.util.Set;
17
18/**
19 * Common abstract class for implementers of {@link IQueryMetaContext}
20 *
21 * @author Grill Balázs
22 * @since 1.3
23 *
24 */
25public abstract class AbstractQueryMetaContext implements IQueryMetaContext {
26
27 /**
28 * @since 2.0
29 */
30 @Override
31 public Map<InputKeyImplication, Set<InputKeyImplication>> getConditionalImplications(IInputKey implyingKey) {
32 return new HashMap<>();
33 }
34
35 /**
36 * @since 1.6
37 */
38 @Override
39 public boolean canLeadOutOfScope(IInputKey key) {
40 return key.getArity() > 1;
41 }
42
43 /**
44 * @since 1.6
45 */
46 @Override
47 public Comparator<IInputKey> getSuggestedEliminationOrdering() {
48 return (o1, o2) -> 0;
49 }
50
51 /**
52 * @since 1.6
53 */
54 @Override
55 public Collection<InputKeyImplication> getWeakenedAlternatives(IInputKey implyingKey) {
56 return Collections.emptySet();
57 }
58
59 @Override
60 public boolean isPosetKey(IInputKey key) {
61 return false;
62 }
63
64 @Override
65 public IPosetComparator getPosetComparator(Iterable<IInputKey> key) {
66 return null;
67 }
68
69}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java
new file mode 100644
index 00000000..c797eff9
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java
@@ -0,0 +1,21 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.matchers.context;
10
11/**
12 * This class is intended to be extended by implementors. The main purpose of this abstract implementation to protect
13 * implementors from future changes in the interface.
14 *
15 * @author Grill Balázs
16 * @since 1.4
17 *
18 */
19public abstract class AbstractQueryRuntimeContext implements IQueryRuntimeContext {
20
21}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java
new file mode 100644
index 00000000..4dbcca88
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java
@@ -0,0 +1,45 @@
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.matchers.context;
10
11/**
12 * An input key identifies an input (extensional) relation, such as the instance set of a given node or edge type, or the direct containment relation.
13 *
14 * <p> The input key, at the very minimum, is associated with an arity (number of columns), a user-friendly name, and a string identifier (for distributive purposes).
15 *
16 * <p> The input key itself must be an immutable data object that properly overrides equals() and hashCode().
17 * It must be instantiable without using the query context object, so that query specifications may construct the appropriate PQueries.
18 *
19 * @author Bergmann Gabor
20 *
21 */
22public interface IInputKey {
23
24 /**
25 * A user-friendly name that can be shown on screen for debug purposes, included in exceptions, etc.
26 */
27 public String getPrettyPrintableName();
28 /**
29 * An internal string identifier that can be used to uniquely identify to input key (relevant for distributed applications).
30 */
31 public String getStringID();
32
33 /**
34 * The width of tuples in this relation.
35 */
36 public int getArity();
37
38 /**
39 * Returns true iff instance tuples of the key can be enumerated.
40 * <p> If false, the runtime can only test tuple membership in the extensional relation identified by the key, but not enumerate member tuples in general.
41 */
42 boolean isEnumerable();
43
44
45}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java
new file mode 100644
index 00000000..e2d5bcee
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java
@@ -0,0 +1,35 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.matchers.context;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12
13/**
14 * Implementations of this interface aid the query engine with the ordering of poset elements. This information is
15 * particularly important in the delete and re-derive evaluation mode because they let the engine identify monotone
16 * change pairs.
17 *
18 * @author Tamas Szabo
19 * @since 1.6
20 */
21public interface IPosetComparator {
22
23 /**
24 * Returns true if the 'left' tuple of poset elements is smaller or equal than the 'right' tuple of poset elements according to
25 * the partial order that this poset comparator employs.
26 *
27 * @param left
28 * the left tuple of poset elements
29 * @param right
30 * the right tuple of poset elements
31 * @return true if left is smaller or equal to right, false otherwise
32 */
33 public boolean isLessOrEqual(final Tuple left, final Tuple right);
34
35}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java
new file mode 100644
index 00000000..04f00aaa
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java
@@ -0,0 +1,49 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.matchers.context;
10
11import org.apache.log4j.Logger;
12import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
13import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
14import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
15import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
16import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
17
18/**
19 * This interface is a collector which holds every API that is provided by the engine to control
20 * the operation of the backends.
21 *
22 * @since 1.5
23 * @noimplement This interface is not intended to be implemented by clients.
24 */
25public interface IQueryBackendContext {
26
27 Logger getLogger();
28
29 IQueryRuntimeContext getRuntimeContext();
30
31 IQueryCacheContext getQueryCacheContext();
32
33 IQueryBackendHintProvider getHintProvider();
34
35 IQueryResultProviderAccess getResultProviderAccess();
36
37 QueryAnalyzer getQueryAnalyzer();
38
39 /**
40 * @since 2.0
41 */
42 IMatcherCapability getRequiredMatcherCapability(PQuery query, QueryEvaluationHint overrideHints);
43
44 /**
45 * @since 1.6
46 */
47 boolean areUpdatesDelayed();
48
49}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java
new file mode 100644
index 00000000..617207f6
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java
@@ -0,0 +1,39 @@
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.matchers.context;
10
11import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
12import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
13import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
14import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
15
16/**
17 * Provides information on already cached queries to query evaluator backends at runtime.
18 *
19 * @author Bergmann Gabor
20 *
21 */
22public interface IQueryCacheContext {
23
24 /**
25 * Checks if there already is a caching result provider for the given query.
26 * <p> Returns false if called while the caching result provider of the given query is being constructed in the first place.
27 */
28 public boolean isResultCached(PQuery query);
29
30 /**
31 * Returns a caching result provider for the given query; it must be constructed if it does not exist yet.
32 * <p> <b>Caution:</b> behavior undefined if called while the caching result provider of the given query is being constructed.
33 * Beware of infinite loops.
34 * <p> <b>Postcondition:</b> {@link IQueryBackend#isCaching()} returns true for the {@link #getQueryBackend()} of the returned provider
35 *
36 * @throws ViatraQueryRuntimeException
37 */
38 public IQueryResultProvider getCachingResultProvider(PQuery query);
39}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java
new file mode 100644
index 00000000..4c22a3cb
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java
@@ -0,0 +1,98 @@
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.matchers.context;
10
11import java.util.Collection;
12import java.util.Comparator;
13import java.util.Map;
14import java.util.Set;
15
16/**
17 * Provides metamodel information (relationship of input keys) to query evaluator backends at runtime and at query planning time.
18 *
19 * @noimplement Implementors should extend {@link AbstractQueryMetaContext} instead of directly implementing this interface.
20 * @author Bergmann Gabor
21 */
22public interface IQueryMetaContext {
23
24 /**
25 * Returns true iff instance tuples of the given key can be enumerated.
26 * <p> If false, the runtime can only test tuple membership in the extensional relation identified by the key, but not enumerate member tuples in general.
27 * <p> Equivalent to {@link IInputKey#isEnumerable()}.
28 */
29 boolean isEnumerable(IInputKey key);
30
31 /**
32 * Returns true iff the set of instance tuples of the given key is immutable.
33 * <p> If false, the runtime provides notifications upon change.
34 */
35 boolean isStateless(IInputKey key);
36
37 /**
38 * Returns a set of implications (weakened alternatives),
39 * with a suggestion for the query planner that satisfying them first may help in satisfying the implying key.
40 * <p> Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys.
41 * <p> Must follow directly or transitively from implications of {@link #getImplications(IInputKey)}.
42 * @since 1.6
43 */
44 Collection<InputKeyImplication> getWeakenedAlternatives(IInputKey implyingKey);
45
46 /**
47 * Returns known direct implications, e.g. edge supertypes, edge opposites, node type constraints, etc.
48 * <p> Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys.
49 */
50 Collection<InputKeyImplication> getImplications(IInputKey implyingKey);
51
52 /**
53 * Returns known "double dispatch" implications, where the given implying key implies other input keys under certain additional conditions (themselves input keys).
54 * For example, a "type x, unscoped" input key may imply the "type x, in scope" input key under the condition of the input key "x is in scope"
55 *
56 * <p> Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys (either as the implying key or as the additional condition).
57 * <p> Note that symmetry is not required, i.e. the additional conditions do not have to list the same conditional implication.
58 * @return multi-map, where the keys are additional conditions and the values are input key implications jointly implied by the condition and the given implying key.
59 * @since 2.0
60 */
61 Map<InputKeyImplication, Set<InputKeyImplication>> getConditionalImplications(IInputKey implyingKey);
62
63 /**
64 * Returns functional dependencies of the input key expressed in terms of column indices.
65 *
66 * <p> Each entry of the map is a functional dependency rule, where the entry key specifies source columns and the entry value specifies target columns.
67 */
68 Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key);
69
70 /**
71 * For query normalizing, this is the order suggested for trying to eliminate input keys.
72 * @since 1.6
73 */
74 Comparator<IInputKey> getSuggestedEliminationOrdering();
75
76 /**
77 * Tells whether the given {@link IInputKey} is an edge and may lead out of scope.
78 *
79 * @since 1.6
80 */
81 boolean canLeadOutOfScope(IInputKey key);
82
83 /**
84 * Returns true if the given {@link IInputKey} represents a poset type.
85 * @since 1.6
86 */
87 boolean isPosetKey(IInputKey key);
88
89 /**
90 * Returns an {@link IPosetComparator} for the given set of {@link IInputKey}s.
91 *
92 * @param keys an iterable collection of input keys
93 * @return the poset comparator
94 * @since 1.6
95 */
96 IPosetComparator getPosetComparator(Iterable<IInputKey> keys);
97
98}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java
new file mode 100644
index 00000000..7fecd01a
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java
@@ -0,0 +1,31 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.matchers.context;
10
11import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
12import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
13import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
14
15/**
16 * This interface exposes API to request {@link IQueryResultProvider} for {@link PQuery} instances.
17 *
18 * @author Grill Balázs
19 * @since 1.5
20 * @noimplement This interface is not intended to be implemented by clients.
21 */
22public interface IQueryResultProviderAccess {
23
24 /**
25 * Get a result provider for the given {@link PQuery}, which conforms the capabilities requested by the
26 * given {@link QueryEvaluationHint} object.
27 * @throws ViatraQueryRuntimeException
28 */
29 public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint overrideHints);
30
31}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java
new file mode 100644
index 00000000..c2e90614
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java
@@ -0,0 +1,281 @@
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.matchers.context;
10
11import java.lang.reflect.InvocationTargetException;
12import java.util.Optional;
13import java.util.concurrent.Callable;
14
15import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper;
16import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.util.Accuracy;
20
21/**
22 * Provides instance model information (relations corresponding to input keys) to query evaluator backends at runtime.
23 * Implementors shall extend {@link AbstractQueryRuntimeContext} instead directly this interface.
24 *
25 * @author Bergmann Gabor
26 * @noimplement This interface is not intended to be implemented by clients. Extend {@link AbstractQueryRuntimeContext} instead.
27 */
28public interface IQueryRuntimeContext {
29 /**
30 * Provides metamodel-specific info independent of the runtime instance model.
31 */
32 public IQueryMetaContext getMetaContext();
33
34
35 /**
36 * The given callable will be executed, and all model traversals will be delayed until the execution is done. If
37 * there are any outstanding information to be read from the model, a single coalesced model traversal will
38 * initialize the caches and deliver the notifications.
39 *
40 * <p> Calls may be nested. A single coalesced traversal will happen at the end of the outermost call.
41 *
42 * <p> <b>Caution: </b> results returned by the runtime context may be incomplete during the coalescing period, to be corrected by notifications sent during the final coalesced traversal.
43 * For example, if a certain input key is not cached yet, an empty relation may be reported during <code>callable.call()</code>; the cache will be constructed after the call terminates and notifications will deliver the entire content of the relation.
44 * Non-incremental query backends should therefore never enumerate input keys while coalesced (verify using {@link #isCoalescing()}).
45 *
46 * @param callable
47 */
48 public abstract <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException;
49 /**
50 * @return true iff currently within a coalescing section (i.e. within the callable of a call to {@link #coalesceTraversals(Callable)}).
51 */
52 public boolean isCoalescing();
53
54 /**
55 * Returns true if index is available for the given key providing the given service.
56 * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
57 * @since 1.4
58 */
59 public boolean isIndexed(IInputKey key, IndexingService service);
60
61 /**
62 * If the given (enumerable) input key is not yet indexed, the model will be traversed
63 * (after the end of the outermost coalescing block, see {@link IQueryRuntimeContext#coalesceTraversals(Callable)})
64 * so that the index can be built. It is possible that the base indexer will select a higher indexing level merging
65 * multiple indexing requests to an appropriate level.
66 *
67 * <p><b>Postcondition:</b> After invoking this method, {@link #getIndexed(IInputKey, IndexingService)} for the same key
68 * and service will be guaranteed to return the requested or a highing indexing level as soon as {@link #isCoalescing()} first returns false.
69 *
70 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
71 * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
72 * @since 1.4
73 */
74 public void ensureIndexed(IInputKey key, IndexingService service);
75
76 /**
77 * Returns the number of tuples in the extensional relation identified by the input key seeded with the given mask and tuple.
78 *
79 * @param key an input key
80 * @param seedMask
81 * a mask that extracts those parameters of the input key (from the entire parameter list) that should be
82 * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most once in seedMask.
83 * @param seed
84 * the tuple of fixed values restricting the match set to be considered, in the same order as given in
85 * parameterSeedMask, so that for each considered match tuple,
86 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null.
87 *
88 * @return the number of tuples in the model for the given key and seed
89 *
90 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
91 * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
92 * @since 1.7
93 */
94 public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed);
95
96
97 /**
98 * Gives an estimate of the number of different groups the tuples of the given relation are projected into by the given mask
99 * (e.g. for an identity mask, this means the full relation size). The estimate must meet the required accuracy.
100 *
101 * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context.
102 * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} is returned.
103 *
104 * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask.
105 *
106 * @return if available, an estimate of the cardinality of the projection of the given extensional relation, with the desired accuracy.
107 *
108 * @since 2.1
109 */
110 public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy);
111
112
113 /**
114 * Gives an estimate of the average size of different groups the tuples of the given relation are projected into by the given mask
115 * (e.g. for an identity mask, this means 1, while for an empty mask, the result is the full relation size).
116 * The estimate must meet the required accuracy.
117 *
118 * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context.
119 * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned.
120 *
121 * <p> For an empty relation, zero is acceptable as an exact answer.
122 *
123 * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask.
124 *
125 * @return if available, an estimate of the average size of each projection group of the given extensional relation, with the desired accuracy.
126 *
127 * @since 2.1
128 */
129 public default Optional<Double> estimateAverageBucketSize(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) {
130 if (key.isEnumerable()) {
131 return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy,
132 (mask, accuracy) -> this.estimateCardinality(key, mask, accuracy));
133 } else return groupMask.isIdentity() ? Optional.of(1.0) : Optional.empty();
134 }
135
136
137 /**
138 * Returns the tuples in the extensional relation identified by the input key, optionally seeded with the given tuple.
139 *
140 * @param key an input key
141 * @param seedMask
142 * a mask that extracts those parameters of the input key (from the entire parameter list) that should be
143 * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most once in seedMask.
144 * @param seed
145 * the tuple of fixed values restricting the match set to be considered, in the same order as given in
146 * parameterSeedMask, so that for each considered match tuple,
147 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null.
148 * @return the tuples in the model for the given key and seed
149 *
150 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
151 * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
152 * @since 1.7
153 */
154 public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed);
155
156 /**
157 * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples
158 * are bound by the seed except for one.
159 *
160 * <p>
161 * Selects the tuples in the extensional relation identified by the input key, optionally seeded with the given
162 * tuple, and then returns the single value from each tuple which is not bound by the ssed mask.
163 *
164 * @param key
165 * an input key
166 * @param seedMask
167 * a mask that extracts those parameters of the input key (from the entire parameter list) that should be
168 * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most
169 * once in seedMask, and seedMask must include all parameters in any arbitrary order except one.
170 * @param seed
171 * the tuple of fixed values restricting the match set to be considered, in the same order as given in
172 * parameterSeedMask, so that for each considered match tuple,
173 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null.
174 * @return the objects in the model for the given key and seed
175 *
176 * <p>
177 * <b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
178 * @throws IllegalArgumentException
179 * if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
180 * @since 1.7
181 */
182 public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed);
183
184 /**
185 * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples
186 * are bound by the seed.
187 *
188 * <p>
189 * Returns whether the given tuple is in the extensional relation identified by the input key.
190 *
191 * <p>
192 * Note: this call works for non-enumerable input keys as well.
193 *
194 * @param key
195 * an input key
196 * @param seed
197 * the tuple of fixed values restricting the match set to be considered, in the same order as given in
198 * parameterSeedMask, so that for each considered match tuple,
199 * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null.
200 * @return true iff there is at least a single tuple contained in the relation that corresponds to the seed tuple
201 * @since 2.0
202 */
203 public boolean containsTuple(IInputKey key, ITuple seed);
204
205
206 /**
207 * Subscribes for updates in the extensional relation identified by the input key, optionally seeded with the given tuple.
208 * <p> This should be called after invoking
209 *
210 * @param key an input key
211 * @param seed can be null or a tuple with matching arity;
212 * if non-null, only those updates in the model are notified about
213 * that match the seed at positions where the seed is non-null.
214 * @param listener will be notified of future changes
215 *
216 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
217 * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
218 */
219 public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener);
220
221 /**
222 * Unsubscribes from updates in the extensional relation identified by the input key, optionally seeded with the given tuple.
223 *
224 * @param key an input key
225 * @param seed can be null or a tuple with matching arity;
226 * if non-null, only those updates in the model are notified about
227 * that match the seed at positions where the seed is non-null.
228 * @param listener will no longer be notified of future changes
229 *
230 * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
231 * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
232 */
233 public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener);
234 /*
235 TODO: uniqueness
236 */
237
238 /**
239 * Wraps the external element into the internal representation that is to be used by the query backend
240 * <p> model element -> internal object.
241 * <p> null must be mapped to null.
242 */
243 public Object wrapElement(Object externalElement);
244
245 /**
246 * Unwraps the internal representation of the element into its original form
247 * <p> internal object -> model element
248 * <p> null must be mapped to null.
249 */
250 public Object unwrapElement(Object internalElement);
251
252 /**
253 * Unwraps the tuple of elements into the internal representation that is to be used by the query backend
254 * <p> model elements -> internal objects
255 * <p> null must be mapped to null.
256 */
257 public Tuple wrapTuple(Tuple externalElements);
258
259 /**
260 * Unwraps the tuple of internal representations of elements into their original forms
261 * <p> internal objects -> model elements
262 * <p> null must be mapped to null.
263 */
264 public Tuple unwrapTuple(Tuple internalElements);
265
266 /**
267 * Starts wildcard indexing for the given service. After this call, no registration is required for this {@link IndexingService}.
268 * a previously set wildcard level cannot be lowered, only extended.
269 * @since 1.4
270 */
271 public void ensureWildcardIndexing(IndexingService service);
272
273 /**
274 * Execute the given runnable after traversal. It is guaranteed that the runnable is executed as soon as
275 * the indexing is finished. The callback is executed only once, then is removed from the callback queue.
276 * @param traversalCallback
277 * @throws InvocationTargetException
278 * @since 1.4
279 */
280 public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException;
281}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java
new file mode 100644
index 00000000..7be27d56
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java
@@ -0,0 +1,27 @@
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.matchers.context;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12
13/**
14 * Listens for changes in the runtime context.
15 * @author Bergmann Gabor
16 *
17 */
18public interface IQueryRuntimeContextListener {
19
20 /**
21 * The given tuple was inserted into or removed from the input relation indicated by the given key.
22 * @param key the key identifying the input relation that was updated
23 * @param updateTuple the tuple that was inserted or removed
24 * @param isInsertion true if it was an insertion, false otherwise.
25 */
26 public void update(IInputKey key, Tuple updateTuple, boolean isInsertion);
27}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java
new file mode 100644
index 00000000..8210765d
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java
@@ -0,0 +1,36 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.matchers.context;
10
11/**
12 * These are the different services which can be provided by an {@link IQueryRuntimeContext} implementation.
13 *
14 * @author Grill Balázs
15 * @since 1.4
16 *
17 */
18public enum IndexingService {
19
20 /**
21 * Cardinality information is available. Makes possible to calculate
22 * unseeded calls of {@link IQueryRuntimeContext#countTuples(IInputKey, tools.refinery.viatra.runtime.matchers.tuple.Tuple)}
23 */
24 STATISTICS,
25
26 /**
27 * The indexer can provide notifications about changes in the model.
28 */
29 NOTIFICATIONS,
30
31 /**
32 * Enables enumeration of instances and reverse-navigation.
33 */
34 INSTANCES
35
36}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java
new file mode 100644
index 00000000..2a403810
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java
@@ -0,0 +1,103 @@
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.matchers.context;
10
11import java.util.ArrayList;
12import java.util.Collections;
13import java.util.List;
14
15/**
16 * Data object representing the implication of an input key, in use cases including edge supertypes, edge opposites, node type constraints, etc.
17 *
18 * <p> Each instance tuple of the <i>implying input key</i> (if given) implies the presence of an instance tuple of the <i>implied input key</i> consisting of elements of the original tuple at given positions.
19 * When the input key is null, it is not an input constraint but some other source that implies input keys.
20 *
21 * <p> The implication is an immutable data object.
22 *
23 * @author Bergmann Gabor
24 *
25 */
26public final class InputKeyImplication {
27 private IInputKey implyingKey;
28 private IInputKey impliedKey;
29 private List<Integer> impliedIndices;
30
31 /**
32 * Optional. Instance tuples of this input key imply an instance tuple of another key.
33 * Sometimes it is not an input key that implies other input keys, so this attribute can be null.
34 */
35 public IInputKey getImplyingKey() {
36 return implyingKey;
37 }
38 /**
39 * An instance tuple of this input key is implied by another key.
40 */
41 public IInputKey getImpliedKey() {
42 return impliedKey;
43 }
44 /**
45 * The implied instance tuple consists of the values in the implying tuple at these indices.
46 */
47 public List<Integer> getImpliedIndices() {
48 return impliedIndices;
49 }
50 /**
51 * @param implyingKey instance tuples of this input key imply an instance tuple of the other key.
52 * @param impliedKey instance tuple of this input key is implied by the other key.
53 * @param implyingIndices the implied instance tuple consists of the values in the implying tuple at these indices.
54 */
55 public InputKeyImplication(IInputKey implyingKey, IInputKey impliedKey,
56 List<Integer> implyingIndices) {
57 super();
58 this.implyingKey = implyingKey;
59 this.impliedKey = impliedKey;
60 this.impliedIndices = Collections.unmodifiableList(new ArrayList<>(implyingIndices));
61 }
62
63 @Override
64 public int hashCode() {
65 final int prime = 31;
66 int result = 1;
67 result = prime * result
68 + ((impliedIndices == null) ? 0 : impliedIndices.hashCode());
69 result = prime * result
70 + ((impliedKey == null) ? 0 : impliedKey.hashCode());
71 result = prime * result
72 + ((implyingKey == null) ? 0 : implyingKey.hashCode());
73 return result;
74 }
75 @Override
76 public boolean equals(Object obj) {
77 if (this == obj)
78 return true;
79 if (obj == null)
80 return false;
81 if (!(obj instanceof InputKeyImplication))
82 return false;
83 InputKeyImplication other = (InputKeyImplication) obj;
84 if (impliedIndices == null) {
85 if (other.impliedIndices != null)
86 return false;
87 } else if (!impliedIndices.equals(other.impliedIndices))
88 return false;
89 if (impliedKey == null) {
90 if (other.impliedKey != null)
91 return false;
92 } else if (!impliedKey.equals(other.impliedKey))
93 return false;
94 if (implyingKey == null) {
95 if (other.implyingKey != null)
96 return false;
97 } else if (!implyingKey.equals(other.implyingKey))
98 return false;
99 return true;
100 }
101
102
103}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java
new file mode 100644
index 00000000..f9b05e5b
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java
@@ -0,0 +1,55 @@
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.matchers.context.common;
10
11import tools.refinery.viatra.runtime.matchers.context.IInputKey;
12
13
14/**
15 * An input key that is identified by a single wrapped object and the class of the wrapper.
16 * @author Bergmann Gabor
17 *
18 */
19public abstract class BaseInputKeyWrapper<Wrapped> implements IInputKey {
20 protected Wrapped wrappedKey;
21
22 public BaseInputKeyWrapper(Wrapped wrappedKey) {
23 super();
24 this.wrappedKey = wrappedKey;
25 }
26
27 public Wrapped getWrappedKey() {
28 return wrappedKey;
29 }
30
31
32 @Override
33 public int hashCode() {
34 return ((wrappedKey == null) ? 0 : wrappedKey.hashCode());
35 }
36
37 @Override
38 public boolean equals(Object obj) {
39 if (this == obj)
40 return true;
41 if (obj == null)
42 return false;
43 if (!(this.getClass().equals(obj.getClass())))
44 return false;
45 BaseInputKeyWrapper other = (BaseInputKeyWrapper) obj;
46 if (wrappedKey == null) {
47 if (other.wrappedKey != null)
48 return false;
49 } else if (!wrappedKey.equals(other.wrappedKey))
50 return false;
51 return true;
52 }
53
54
55}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java
new file mode 100644
index 00000000..eb972c2d
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java
@@ -0,0 +1,165 @@
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.matchers.context.common;
10
11
12
13/**
14 * Instance tuples are of form (x), where object x is an instance of the given Java class or its subclasses.
15 * <p> Fine print 1: classes with the same name are considered equivalent.
16 * Can be instantiated with class name, even if the class itself is not loaded yet; but if the class is available, passing it in the constructor is beneficial to avoid classloading troubles.
17 * <p> Fine print 2: primitive types (char, etc.) are transparently treated as their wrapper class (Character, etc.).
18 * <p> Non-enumerable type, can only be checked.
19 * <p> Stateless type (objects can't change their type)
20 * @author Bergmann Gabor
21 *
22*/
23public class JavaTransitiveInstancesKey extends BaseInputKeyWrapper<String> {
24
25 /**
26 * The actual Class whose (transitive) instances this relation contains. Can be null at compile time, if only the name is available.
27 * Can be a primitive.
28 */
29 private Class<?> cachedOriginalInstanceClass;
30
31 /**
32 * Same as {@link #cachedOriginalInstanceClass}, but primitive classes are replaced with their wrapper classes (e.g. int --> java.lang.Integer).
33 */
34 private Class<?> cachedWrapperInstanceClass;
35
36 /**
37 * Preferred constructor.
38 */
39 public JavaTransitiveInstancesKey(Class<?> instanceClass) {
40 this(primitiveTypeToWrapperClass(instanceClass).getName());
41 this.cachedOriginalInstanceClass = instanceClass;
42 }
43
44 /**
45 * Call this constructor only in contexts where the class itself is not available for loading, e.g. it has not yet been compiled.
46 */
47 public JavaTransitiveInstancesKey(String className) {
48 super(className);
49 }
50
51
52 /**
53 * Returns null if class cannot be loaded.
54 */
55 private Class<?> getOriginalInstanceClass() {
56 if (cachedOriginalInstanceClass == null) {
57 try {
58 resolveClassInternal();
59 } catch (ClassNotFoundException e) {
60 // class not yet available at this point
61 }
62 }
63 return cachedOriginalInstanceClass;
64 }
65
66
67 /**
68 * @return non-null instance class
69 * @throws ClassNotFoundException
70 */
71 private Class<?> forceGetOriginalInstanceClass() throws ClassNotFoundException {
72 if (cachedOriginalInstanceClass == null) {
73 resolveClassInternal();
74 }
75 return cachedOriginalInstanceClass;
76 }
77
78 /**
79 * @return non-null instance class, wrapped if primitive class
80 * @throws ClassNotFoundException
81 */
82 public Class<?> forceGetWrapperInstanceClass() throws ClassNotFoundException {
83 forceGetOriginalInstanceClass();
84 return getWrapperInstanceClass();
85 }
86 /**
87 * @return non-null instance class, wrapped if primitive class
88 * @throws ClassNotFoundException
89 */
90 public Class<?> forceGetInstanceClass() throws ClassNotFoundException {
91 return forceGetWrapperInstanceClass();
92 }
93
94 /**
95 * @return instance class, wrapped if primitive class, null if class cannot be loaded
96 */
97 public Class<?> getWrapperInstanceClass() {
98 if (cachedWrapperInstanceClass == null) {
99 cachedWrapperInstanceClass = primitiveTypeToWrapperClass(getOriginalInstanceClass());
100 }
101 return cachedWrapperInstanceClass;
102 }
103 /**
104 * @return instance class, wrapped if primitive class, null if class cannot be loaded
105 */
106 public Class<?> getInstanceClass() {
107 return getWrapperInstanceClass();
108 }
109
110 private void resolveClassInternal() throws ClassNotFoundException {
111 cachedOriginalInstanceClass = Class.forName(wrappedKey);
112 }
113
114 @Override
115 public String getPrettyPrintableName() {
116 getWrapperInstanceClass();
117 return cachedWrapperInstanceClass == null ? wrappedKey == null ? "<null>" : wrappedKey : cachedWrapperInstanceClass.getName();
118 }
119
120 @Override
121 public String getStringID() {
122 return "javaClass#"+ getPrettyPrintableName();
123 }
124
125 @Override
126 public int getArity() {
127 return 1;
128 }
129
130 @Override
131 public boolean isEnumerable() {
132 return false;
133 }
134
135 @Override
136 public String toString() {
137 return this.getPrettyPrintableName();
138 }
139
140 private static Class<?> primitiveTypeToWrapperClass(Class<?> instanceClass) {
141 if (instanceClass != null && instanceClass.isPrimitive()) {
142 if (Void.TYPE.equals(instanceClass))
143 return Void.class;
144 if (Boolean.TYPE.equals(instanceClass))
145 return Boolean.class;
146 if (Character.TYPE.equals(instanceClass))
147 return Character.class;
148 if (Byte.TYPE.equals(instanceClass))
149 return Byte.class;
150 if (Short.TYPE.equals(instanceClass))
151 return Short.class;
152 if (Integer.TYPE.equals(instanceClass))
153 return Integer.class;
154 if (Long.TYPE.equals(instanceClass))
155 return Long.class;
156 if (Float.TYPE.equals(instanceClass))
157 return Float.class;
158 if (Double.TYPE.equals(instanceClass))
159 return Double.class;
160 }
161 return instanceClass;
162 }
163
164
165}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java
new file mode 100644
index 00000000..bb24ec8c
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java
@@ -0,0 +1,153 @@
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.matchers.context.surrogate;
10
11import java.util.Collections;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.Map;
15import java.util.NoSuchElementException;
16import java.util.Set;
17
18import tools.refinery.viatra.runtime.matchers.context.IInputKey;
19import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
20import tools.refinery.viatra.runtime.matchers.util.IProvider;
21import tools.refinery.viatra.runtime.matchers.util.Preconditions;
22import tools.refinery.viatra.runtime.matchers.util.SingletonInstanceProvider;
23
24/**
25 * @author Abel Hegedus
26 *
27 */
28public class SurrogateQueryRegistry {
29
30 private Map<IInputKey, IProvider<PQuery>> registeredSurrogateQueryMap = new HashMap<>();
31 private Map<IInputKey, IProvider<PQuery>> dynamicSurrogateQueryMap = new HashMap<>();
32
33 /**
34 * Hidden constructor
35 */
36 private SurrogateQueryRegistry() {
37 }
38
39 private static final SurrogateQueryRegistry INSTANCE = new SurrogateQueryRegistry();
40
41 public static SurrogateQueryRegistry instance() {
42 return INSTANCE;
43 }
44
45 /**
46 *
47 * @param feature
48 * @param surrogateQuery
49 * @return the previous surrogate query associated with feature, or null if there was no such query FQN registered
50 * @throws IllegalArgumentException if feature or surrogateQuery is null
51 */
52 public IProvider<PQuery> registerSurrogateQueryForFeature(IInputKey feature, PQuery surrogateQuery) {
53 Preconditions.checkArgument(surrogateQuery != null, "Surrogate query must not be null!");
54 return registerSurrogateQueryForFeature(feature, new SingletonInstanceProvider<PQuery>(surrogateQuery));
55 }
56
57 /**
58 *
59 * @param feature
60 * @param surrogateQuery
61 * @return the previous surrogate query associated with feature, or null
62 * if there was no such query registered
63 * @throws IllegalArgumentException
64 * if feature or surrogateQuery is null
65 */
66 public IProvider<PQuery> registerSurrogateQueryForFeature(IInputKey feature, IProvider<PQuery> surrogateQueryProvider) {
67 Preconditions.checkArgument(feature != null, "Feature must not be null!");
68 Preconditions.checkArgument(surrogateQueryProvider != null, "Surrogate query must not be null!");
69 return registeredSurrogateQueryMap.put(feature, surrogateQueryProvider);
70 }
71
72 public IProvider<PQuery> addDynamicSurrogateQueryForFeature(IInputKey feature, PQuery surrogateQuery) {
73 Preconditions.checkArgument(surrogateQuery != null, "Surrogate query FQN must not be null!");
74 return addDynamicSurrogateQueryForFeature(feature, new SingletonInstanceProvider<PQuery>(surrogateQuery));
75 }
76
77 public IProvider<PQuery> addDynamicSurrogateQueryForFeature(IInputKey feature, IProvider<PQuery> surrogateQuery) {
78 Preconditions.checkArgument(feature != null, "Feature must not be null!");
79 Preconditions.checkArgument(surrogateQuery != null, "Surrogate query FQN must not be null!");
80 return dynamicSurrogateQueryMap.put(feature, surrogateQuery);
81 }
82
83 public IProvider<PQuery> removeDynamicSurrogateQueryForFeature(IInputKey feature) {
84 Preconditions.checkArgument(feature != null, "Feature must not be null!");
85 return dynamicSurrogateQueryMap.remove(feature);
86 }
87
88 /**
89 *
90 * @param feature that may have surrogate query defined, null not allowed
91 * @return true if the feature has a surrogate query defined
92 * @throws IllegalArgumentException if feature is null
93 */
94 public boolean hasSurrogateQueryFQN(IInputKey feature) {
95 Preconditions.checkArgument(feature != null, "Feature must not be null!");
96 boolean surrogateExists = dynamicSurrogateQueryMap.containsKey(feature);
97 if(!surrogateExists){
98 surrogateExists = registeredSurrogateQueryMap.containsKey(feature);
99 }
100 return surrogateExists;
101 }
102
103 /**
104 *
105 * @param feature for which the surrogate query FQN should be returned
106 * @return the surrogate query FQN defined for the feature
107 * @throws IllegalArgumentException if feature is null
108 * @throws NoSuchElementException if the feature has no surrogate query defined, use {@link #hasSurrogateQueryFQN} to check
109 */
110 public PQuery getSurrogateQuery(IInputKey feature) {
111 Preconditions.checkArgument(feature != null, "Feature must not be null!");
112 IProvider<PQuery> surrogate = dynamicSurrogateQueryMap.get(feature);
113 if(surrogate == null) {
114 surrogate = registeredSurrogateQueryMap.get(feature);
115 }
116 if(surrogate != null) {
117 return surrogate.get();
118 } else {
119 throw new NoSuchElementException(String.format("Feature %s has no surrogate query defined! Use #hasSurrogateQueryFQN to check existence.", feature));
120 }
121 }
122
123 /**
124 * @return an unmodifiable set of features with registered surrogate queries
125 */
126 public Set<IInputKey> getRegisteredSurrogateQueries() {
127 return Collections.unmodifiableSet(getRegisteredSurrogateQueriesInternal());
128 }
129
130 private Set<IInputKey> getRegisteredSurrogateQueriesInternal() {
131 return registeredSurrogateQueryMap.keySet();
132 }
133
134 /**
135 * @return an unmodifiable set of features with dynamically added surrogate queries
136 */
137 public Set<IInputKey> getDynamicSurrogateQueries() {
138 return Collections.unmodifiableSet(getDynamicSurrogateQueriesInternal());
139 }
140
141 private Set<IInputKey> getDynamicSurrogateQueriesInternal() {
142 return dynamicSurrogateQueryMap.keySet();
143 }
144
145 /**
146 * @return an unmodifiable set that contains all features with surrogate queries.
147 */
148 public Set<IInputKey> getAllSurrogateQueries() {
149 Set<IInputKey> results = new HashSet<>(getRegisteredSurrogateQueriesInternal());
150 results.addAll(getDynamicSurrogateQueriesInternal());
151 return results;
152 }
153}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java
new file mode 100644
index 00000000..66587f77
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java
@@ -0,0 +1,58 @@
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.matchers.memories;
10
11import java.util.Iterator;
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
19import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
20import tools.refinery.viatra.runtime.matchers.util.IMemory;
21
22/**
23 * Common parts of nullary and identity specializations.
24 *
25 * @noextend This class is not intended to be subclassed by clients.
26 * @author Gabor Bergmann
27 * @since 2.0
28 */
29abstract class AbstractTrivialMaskedMemory<Timestamp extends Comparable<Timestamp>> extends MaskedTupleMemory<Timestamp> {
30
31 protected IMemory<Tuple> tuples;
32
33 protected AbstractTrivialMaskedMemory(TupleMask mask, MemoryType bucketType, Object owner) {
34 super(mask, owner);
35 tuples = CollectionsFactory.createMemory(Object.class, bucketType);
36 }
37
38 @Override
39 public Map<Tuple, Timeline<Timestamp>> getWithTimeline(ITuple signature) {
40 throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!");
41 }
42
43 @Override
44 public void clear() {
45 tuples.clear();
46 }
47
48 @Override
49 public int getTotalSize() {
50 return tuples.size();
51 }
52
53 @Override
54 public Iterator<Tuple> iterator() {
55 return tuples.iterator();
56 }
57
58}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java
new file mode 100644
index 00000000..92081409
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java
@@ -0,0 +1,127 @@
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.matchers.memories;
11
12import java.util.Collection;
13import java.util.Iterator;
14import java.util.Map;
15
16import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
21import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
22import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
23import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
25
26/**
27 * @author Gabor Bergmann
28 *
29 * Default implementation that covers all cases.
30 *
31 * @since 2.0
32 */
33public final class DefaultMaskedTupleMemory<Timestamp extends Comparable<Timestamp>>
34 extends MaskedTupleMemory<Timestamp> {
35 /**
36 * Maps a signature tuple to the bucket of tuples with the given signature.
37 *
38 * @since 2.0
39 */
40 protected IMultiLookup<Tuple, Tuple> signatureToTuples;
41
42 /**
43 * @param mask
44 * The mask used to index the matchings
45 * @param owner
46 * the object "owning" this memory
47 * @param bucketType
48 * the kind of tuple collection maintained for each indexer bucket
49 * @since 2.0
50 */
51 public DefaultMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) {
52 super(mask, owner);
53 signatureToTuples = CollectionsFactory.<Tuple, Tuple> createMultiLookup(Object.class, bucketType, Object.class);
54 }
55
56 @Override
57 public boolean add(Tuple tuple) {
58 Tuple signature = mask.transform(tuple);
59 return add(tuple, signature);
60 }
61
62 @Override
63 public boolean add(Tuple tuple, Tuple signature) {
64 try {
65 return signatureToTuples.addPair(signature, tuple) == ChangeGranularity.KEY;
66 } catch (IllegalStateException ex) { // ignore worthless internal exception details
67 throw raiseDuplicateInsertion(tuple);
68 }
69
70 }
71
72 @Override
73 public boolean remove(Tuple tuple) {
74 Tuple signature = mask.transform(tuple);
75 return remove(tuple, signature);
76 }
77
78 @Override
79 public boolean remove(Tuple tuple, Tuple signature) {
80 try {
81 return signatureToTuples.removePair(signature, tuple) == ChangeGranularity.KEY;
82 } catch (IllegalStateException ex) { // ignore worthless internal exception details
83 throw raiseDuplicateDeletion(tuple);
84 }
85 }
86
87 @Override
88 public Map<Tuple, Timeline<Timestamp>> getWithTimeline(ITuple signature) {
89 throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!");
90 }
91
92 @Override
93 public Collection<Tuple> get(ITuple signature) {
94 IMemoryView<Tuple> bucket = signatureToTuples.lookupUnsafe(signature);
95 return bucket == null ? null : bucket.distinctValues();
96 }
97
98 @Override
99 public void clear() {
100 signatureToTuples.clear();
101 }
102
103 @Override
104 public Iterable<Tuple> getSignatures() {
105 return signatureToTuples.distinctKeys();
106 }
107
108 @Override
109 public Iterator<Tuple> iterator() {
110 return signatureToTuples.distinctValues().iterator();
111 }
112
113 @Override
114 public int getTotalSize() {
115 int i = 0;
116 for (Tuple key : signatureToTuples.distinctKeys()) {
117 i += signatureToTuples.lookup(key).size();
118 }
119 return i;
120 }
121
122 @Override
123 public int getKeysetSize() {
124 return signatureToTuples.countKeys();
125 }
126
127}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java
new file mode 100644
index 00000000..dc59daf5
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java
@@ -0,0 +1,77 @@
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.matchers.memories;
10
11import java.util.Collection;
12import java.util.Collections;
13
14import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
18
19/**
20 * Specialized for identity mask; tuples are stored as a simple set/multiset memory.
21 *
22 * @author Gabor Bergmann
23 * @since 2.0
24 */
25public final class IdentityMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> extends AbstractTrivialMaskedMemory<Timestamp> {
26
27 /**
28 * @param mask
29 * The mask used to index the matchings
30 * @param owner the object "owning" this memory
31 * @param bucketType the kind of tuple collection maintained for each indexer bucket
32 * @since 2.0
33 */
34 public IdentityMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) {
35 super(mask, bucketType, owner);
36 if (!mask.isIdentity()) throw new IllegalArgumentException(mask.toString());
37 }
38
39 @Override
40 public int getKeysetSize() {
41 return tuples.size();
42 }
43
44 @Override
45 public Iterable<Tuple> getSignatures() {
46 return tuples;
47 }
48
49 @Override
50 public Collection<Tuple> get(ITuple signature) {
51 Tuple contained = tuples.theContainedVersionOfUnsafe(signature);
52 return contained != null ?
53 Collections.singleton(contained) :
54 null;
55 }
56
57 @Override
58 public boolean remove(Tuple tuple, Tuple signature) {
59 return tuples.removeOne(tuple);
60 }
61
62 @Override
63 public boolean remove(Tuple tuple) {
64 return tuples.removeOne(tuple);
65 }
66
67 @Override
68 public boolean add(Tuple tuple, Tuple signature) {
69 return tuples.addOne(tuple);
70 }
71
72 @Override
73 public boolean add(Tuple tuple) {
74 return tuples.addOne(tuple);
75 }
76
77}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java
new file mode 100644
index 00000000..62377624
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java
@@ -0,0 +1,385 @@
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.matchers.memories;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Iterator;
14import java.util.Map;
15
16import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyDefaultMaskedTupleMemory;
17import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyIdentityMaskedTupleMemory;
18import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyNullaryMaskedTupleMemory;
19import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyUnaryMaskedTupleMemory;
20import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
22import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
23import tools.refinery.viatra.runtime.matchers.util.Clearable;
24import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
25import tools.refinery.viatra.runtime.matchers.util.resumable.MaskedResumable;
26import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
27import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
28
29/**
30 * Indexes a collection of tuples by their signature (i.e. footprint, projection) obtained according to a mask. May
31 * belong to an "owner" (for documentation / traceability purposes).
32 * <p>
33 * There are timeless and timely versions of the different memories. Timely versions associate {@link Timeline}s with
34 * the stored tuples.
35 *
36 * @noextend This class is not intended to be subclassed by clients.
37 * @author Gabor Bergmann
38 * @author Tamas Szabo
39 * @since 2.0
40 */
41public abstract class MaskedTupleMemory<Timestamp extends Comparable<Timestamp>>
42 implements Clearable, MaskedResumable<Timestamp> {
43
44 /**
45 * Creates a new memory for the given owner that indexes tuples according to the given mask.
46 */
47 public static <T extends Comparable<T>> MaskedTupleMemory<T> create(final TupleMask mask,
48 final MemoryType bucketType, final Object owner) {
49 return create(mask, bucketType, owner, false);
50 }
51
52 /**
53 * Creates a new memory for the given owner that indexes tuples according to the given mask. Clients can specify if
54 * the created memory should be timely or not. <br>
55 * <br>
56 * Timely means that tuples are associated with a timeline.
57 *
58 * @since 2.3
59 */
60 public static <T extends Comparable<T>> MaskedTupleMemory<T> create(final TupleMask mask,
61 final MemoryType bucketType, final Object owner, final boolean isTimely) {
62 return create(mask, bucketType, owner, isTimely, false);
63 }
64
65 /**
66 * Creates a new memory for the given owner that indexes tuples according to the given mask. Clients can specify if
67 * the created memory should be timely or not. In case of timely memory, clients can also specify if the memory is
68 * lazy or not. <br>
69 * <br>
70 * Timely means that tuples are associated with a timeline. <br>
71 * <br>
72 * Lazyness can only be used together with timely memories. It means that the maintenance of the timelines is lazy,
73 * that is, the memory only updates its internal data structures at the timestamp affected by an update, and can be
74 * instructed later to resume the maintenance at higher timestamps, as well.
75 *
76 * @since 2.4
77 */
78 public static <T extends Comparable<T>> MaskedTupleMemory<T> create(final TupleMask mask,
79 final MemoryType bucketType, final Object owner, final boolean isTimely, final boolean isLazy) {
80 if (isTimely) {
81 if (bucketType != MemoryType.SETS) {
82 throw new IllegalArgumentException("Timely memories only support SETS as the bucket type!");
83 }
84 if (mask.isIdentity()) {
85 return new TimelyIdentityMaskedTupleMemory<T>(mask, owner, isLazy);
86 } else if (0 == mask.getSize()) {
87 return new TimelyNullaryMaskedTupleMemory<T>(mask, owner, isLazy);
88 } else if (1 == mask.getSize()) {
89 return new TimelyUnaryMaskedTupleMemory<T>(mask, owner, isLazy);
90 } else {
91 return new TimelyDefaultMaskedTupleMemory<T>(mask, owner, isLazy);
92 }
93 } else {
94 if (isLazy) {
95 throw new IllegalArgumentException("Lazy maintenance is only supported by timely memories!");
96 }
97 if (mask.isIdentity()) {
98 return new IdentityMaskedTupleMemory<T>(mask, bucketType, owner);
99 } else if (0 == mask.getSize()) {
100 return new NullaryMaskedTupleMemory<T>(mask, bucketType, owner);
101 } else if (1 == mask.getSize()) {
102 return new UnaryMaskedTupleMemory<T>(mask, bucketType, owner);
103 } else {
104 return new DefaultMaskedTupleMemory<T>(mask, bucketType, owner);
105 }
106 }
107 }
108
109 @Override
110 public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) {
111 throw new UnsupportedOperationException("This is only supported by lazy timely memory implementations!");
112 }
113
114 @Override
115 public Iterable<Tuple> getResumableSignatures() {
116 throw new UnsupportedOperationException("This is only supported by lazy timely memory implementations!");
117 }
118
119 @Override
120 public Timestamp getResumableTimestamp() {
121 return null;
122 }
123
124 /**
125 * Initializes the contents of this memory based on the contents of another memory. The default value is associated
126 * with each tuple in the timely memories.
127 *
128 * @since 2.3
129 */
130 public void initializeWith(final MaskedTupleMemory<Timestamp> other, final Timestamp defaultValue) {
131 throw new UnsupportedOperationException("This is only supported by timely memory implementations!");
132 }
133
134 /**
135 * Returns true if there is any tuple with the given signature that is present at the timestamp +inf, false
136 * otherwise.
137 * @since 2.4
138 */
139 public boolean isPresentAtInfinity(final ITuple signature) {
140 return get(signature) != null;
141 }
142
143 /**
144 * Returns true of this memory is timely, false otherwise.
145 *
146 * @since 2.3
147 */
148 public boolean isTimely() {
149 return false;
150 }
151
152 /**
153 * The mask by which the tuples are indexed.
154 */
155 protected final TupleMask mask;
156
157 /**
158 * The object "owning" this memory. May be null.
159 *
160 * @since 1.7
161 */
162 protected final Object owner;
163
164 /**
165 * The node owning this memory. May be null.
166 *
167 * @since 2.0
168 */
169 public Object getOwner() {
170 return owner;
171 }
172
173 /**
174 * The mask according to which tuples are projected and indexed.
175 *
176 * @since 2.0
177 */
178 public TupleMask getMask() {
179 return mask;
180 }
181
182 /**
183 * @return the number of distinct signatures of all stored tuples.
184 */
185 public abstract int getKeysetSize();
186
187 /**
188 * @return the total number of distinct tuples stored. Multiple copies of the same tuple, if allowed, are counted as
189 * one.
190 *
191 * <p>
192 * This is currently not cached but computed on demand. It is therefore not efficient, and shall only be
193 * used for debug / profiling purposes.
194 */
195 public abstract int getTotalSize();
196
197 /**
198 * Iterates over distinct tuples stored in the memory, regardless of their signatures.
199 */
200 public abstract Iterator<Tuple> iterator();
201
202 /**
203 * Retrieves a read-only view of exactly those signatures for which at least one tuple is stored
204 *
205 * @since 2.0
206 */
207 public abstract Iterable<Tuple> getSignatures();
208
209 /**
210 * Retrieves tuples that have the specified signature
211 *
212 * @return collection of tuples found, null if none
213 */
214 public abstract Collection<Tuple> get(final ITuple signature);
215
216 /**
217 * Retrieves the tuples and their associated timelines that have the specified signature.
218 *
219 * @return the mappings from tuples to timelines, null if there is no mapping for the signature
220 * @since 2.4
221 */
222 public abstract Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature);
223
224 /**
225 * Retrieves tuples that have the specified signature.
226 *
227 * @return collection of tuples found, never null
228 * @since 2.1
229 */
230 public Collection<Tuple> getOrEmpty(final ITuple signature) {
231 final Collection<Tuple> result = get(signature);
232 return result == null ? Collections.emptySet() : result;
233 }
234
235 /**
236 * Retrieves tuples with their associated timelines that have the specified signature.
237 *
238 * @return map of tuples and timelines found, never null
239 * @since 2.4
240 */
241 public Map<Tuple, Timeline<Timestamp>> getOrEmptyWithTimeline(final ITuple signature) {
242 final Map<Tuple, Timeline<Timestamp>> result = getWithTimeline(signature);
243 return result == null ? Collections.emptyMap() : result;
244 }
245
246 /**
247 * Removes a tuple occurrence from the memory with the given signature.
248 *
249 * @param tuple
250 * the tuple to be removed from the memory
251 * @param signature
252 * precomputed footprint of the tuple according to the mask
253 *
254 * @return true if this was the the last occurrence of the signature (according to the mask)
255 */
256 public boolean remove(final Tuple tuple, final Tuple signature) {
257 throw new UnsupportedOperationException("This is only supported by timeless memory implementations!");
258 }
259
260 /**
261 * Removes a tuple occurrence from the memory with the given signature and timestamp.
262 *
263 * @param tuple
264 * the tuple to be removed from the memory
265 * @param signature
266 * precomputed footprint of the tuple according to the mask
267 * @param timestamp
268 * the timestamp associated with the tuple
269 *
270 * @return A {@link Diff} describing how the timeline of the given tuple changed.
271 *
272 * @since 2.4
273 */
274 public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) {
275 throw new UnsupportedOperationException("This is only supported by timely memory implementations!");
276 }
277
278 /**
279 * Removes a tuple occurrence from the memory.
280 *
281 * @param tuple
282 * the tuple to be removed from the memory
283 *
284 * @return true if this was the the last occurrence of the signature (according to the mask)
285 */
286 public boolean remove(final Tuple tuple) {
287 throw new UnsupportedOperationException("This is only supported by timeless memory implementations!");
288 }
289
290 /**
291 * Removes a tuple occurrence from the memory with the given timestamp.
292 *
293 * @param tuple
294 * the tuple to be removed from the memory
295 * @param timestamp
296 * the timestamp associated with the tuple
297 *
298 * @return A {@link Diff} describing how the timeline of the given tuple changed.
299 *
300 * @since 2.4
301 */
302 public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) {
303 throw new UnsupportedOperationException("This is only supported by timely memory implementations!");
304 }
305
306 /**
307 * Adds a tuple occurrence to the memory with the given signature.
308 *
309 * @param tuple
310 * the tuple to be added to the memory
311 * @param signature
312 * precomputed footprint of the tuple according to the mask
313 *
314 * @return true if new signature encountered (according to the mask)
315 */
316 public boolean add(final Tuple tuple, final Tuple signature) {
317 throw new UnsupportedOperationException("This is only supported by timeless memory implementations!");
318 }
319
320 /**
321 * Adds a tuple occurrence to the memory with the given signature and timestamp.
322 *
323 * @param tuple
324 * the tuple to be added to the memory
325 * @param signature
326 * precomputed footprint of the tuple according to the mask
327 * @param timestamp
328 * the timestamp associated with the tuple
329 *
330 * @return A {@link Diff} describing how the timeline of the given tuple changed.
331 *
332 * @since 2.4
333 */
334 public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) {
335 throw new UnsupportedOperationException("This is only supported by timely memory implementations!");
336 }
337
338 /**
339 * Adds a tuple occurrence to the memory.
340 *
341 * @param tuple
342 * the tuple to be added to the memory
343 *
344 * @return true if new signature encountered (according to the mask)
345 */
346 public boolean add(final Tuple tuple) {
347 throw new UnsupportedOperationException("This is only supported by timeless memory implementations!");
348 }
349
350 /**
351 * Adds a tuple occurrence to the memory with the given timestamp.
352 *
353 * @param tuple
354 * the tuple to be added to the memory
355 * @param timestamp
356 * the timestamp associated with the tuple
357 *
358 * @return A {@link Diff} describing how the timeline of the given tuple changed.
359 *
360 * @since 2.4
361 */
362 public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Timestamp timestamp) {
363 throw new UnsupportedOperationException("This is only supported by timely memory implementations!");
364 }
365
366 protected MaskedTupleMemory(final TupleMask mask, final Object owner) {
367 super();
368 this.mask = mask;
369 this.owner = owner;
370 }
371
372 protected IllegalStateException raiseDuplicateInsertion(final Tuple tuple) {
373 return new IllegalStateException(String.format("Duplicate insertion of tuple %s into %s", tuple, owner));
374 }
375
376 protected IllegalStateException raiseDuplicateDeletion(final Tuple tuple) {
377 return new IllegalStateException(String.format("Duplicate deletion of tuple %s from %s", tuple, owner));
378 }
379
380 @Override
381 public String toString() {
382 return getClass().getSimpleName() + "<" + mask + ">@" + owner;
383 }
384
385} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java
new file mode 100644
index 00000000..7fa9e053
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java
@@ -0,0 +1,85 @@
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.matchers.memories;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
20
21/**
22 * Specialized for nullary mask; tuples are stored as a simple set/multiset memory.
23 *
24 * @author Gabor Bergmann
25 * @since 2.0
26 */
27public final class NullaryMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> extends AbstractTrivialMaskedMemory<Timestamp> {
28
29 protected static final Set<Tuple> UNIT_RELATION =
30 Collections.singleton(Tuples.staticArityFlatTupleOf());
31 protected static final Set<Tuple> EMPTY_RELATION =
32 Collections.emptySet();
33 /**
34 * @param mask
35 * The mask used to index the matchings
36 * @param owner the object "owning" this memory
37 * @param bucketType the kind of tuple collection maintained for each indexer bucket
38 * @since 2.0
39 */
40 public NullaryMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) {
41 super(mask, bucketType, owner);
42 if (0 != mask.getSize()) throw new IllegalArgumentException(mask.toString());
43 }
44
45 @Override
46 public int getKeysetSize() {
47 return tuples.isEmpty() ? 0 : 1;
48 }
49
50 @Override
51 public Iterable<Tuple> getSignatures() {
52 return tuples.isEmpty() ? EMPTY_RELATION : UNIT_RELATION;
53 }
54
55 @Override
56 public Collection<Tuple> get(ITuple signature) {
57 if (0 == signature.getSize())
58 return tuples.distinctValues();
59 else return null;
60 }
61
62 @Override
63 public boolean remove(Tuple tuple, Tuple signature) {
64 tuples.removeOne(tuple);
65 return tuples.isEmpty();
66 }
67
68 @Override
69 public boolean remove(Tuple tuple) {
70 return remove(tuple, null);
71 }
72
73 @Override
74 public boolean add(Tuple tuple, Tuple signature) {
75 boolean wasEmpty = tuples.isEmpty();
76 tuples.addOne(tuple);
77 return wasEmpty;
78 }
79
80 @Override
81 public boolean add(Tuple tuple) {
82 return add(tuple, null);
83 }
84
85}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java
new file mode 100644
index 00000000..f34cc9e3
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java
@@ -0,0 +1,143 @@
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.matchers.memories;
10
11import java.util.Collection;
12import java.util.Iterator;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
21import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
22import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
23import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
25
26/**
27 * Specialized for unary mask; tuples are indexed by a single column as opposed to a projection (signature) tuple.
28 *
29 * @author Gabor Bergmann
30 * @since 2.0
31 */
32public final class UnaryMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> extends MaskedTupleMemory<Timestamp> {
33
34 protected IMultiLookup<Object, Tuple> columnToTuples;
35 protected final int keyPosition;
36
37 /**
38 * @param mask
39 * The mask used to index the matchings
40 * @param owner the object "owning" this memory
41 * @param bucketType the kind of tuple collection maintained for each indexer bucket
42 * @since 2.0
43 */
44 public UnaryMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) {
45 super(mask, owner);
46 if (1 != mask.getSize()) throw new IllegalArgumentException(mask.toString());
47
48 columnToTuples = CollectionsFactory.<Object, Tuple>createMultiLookup(
49 Object.class, bucketType, Object.class);
50 keyPosition = mask.indices[0];
51 }
52
53 @Override
54 public void clear() {
55 columnToTuples.clear();
56 }
57
58 @Override
59 public int getKeysetSize() {
60 return columnToTuples.countKeys();
61 }
62
63 @Override
64 public int getTotalSize() {
65 int i = 0;
66 for (Object key : columnToTuples.distinctKeys()) {
67 i += columnToTuples.lookup(key).size();
68 }
69 return i;
70 }
71
72 @Override
73 public Iterator<Tuple> iterator() {
74 return columnToTuples.distinctValues().iterator();
75 }
76
77 @Override
78 public Iterable<Tuple> getSignatures() {
79 return () -> {
80 Iterator<Object> wrapped = columnToTuples.distinctKeys().iterator();
81 return new Iterator<Tuple>() {
82 @Override
83 public boolean hasNext() {
84 return wrapped.hasNext();
85 }
86 @Override
87 public Tuple next() {
88 Object key = wrapped.next();
89 return Tuples.staticArityFlatTupleOf(key);
90 }
91 };
92 };
93 }
94
95 @Override
96 public Collection<Tuple> get(ITuple signature) {
97 Object key = signature.get(0);
98 IMemoryView<Tuple> bucket = columnToTuples.lookup(key);
99 return bucket == null ? null : bucket.distinctValues();
100 }
101
102 @Override
103 public Map<Tuple, Timeline<Timestamp>> getWithTimeline(ITuple signature) {
104 throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!");
105 }
106
107 @Override
108 public boolean remove(Tuple tuple, Tuple signature) {
109 return removeInternal(tuple, tuple.get(keyPosition));
110 }
111
112 @Override
113 public boolean remove(Tuple tuple) {
114 return removeInternal(tuple, tuple.get(keyPosition));
115 }
116
117 @Override
118 public boolean add(Tuple tuple, Tuple signature) {
119 return addInternal(tuple, tuple.get(keyPosition));
120 }
121
122 @Override
123 public boolean add(Tuple tuple) {
124 return addInternal(tuple, tuple.get(keyPosition));
125 }
126
127 protected boolean addInternal(Tuple tuple, Object key) {
128 try {
129 return columnToTuples.addPair(key, tuple) == ChangeGranularity.KEY;
130 } catch (IllegalStateException ex) { // ignore worthless internal exception details
131 throw raiseDuplicateInsertion(tuple);
132 }
133 }
134
135 protected boolean removeInternal(Tuple tuple, Object key) {
136 try {
137 return columnToTuples.removePair(key, tuple) == ChangeGranularity.KEY;
138 } catch (IllegalStateException ex) { // ignore worthless internal exception details
139 throw raiseDuplicateDeletion(tuple);
140 }
141 }
142
143}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java
new file mode 100644
index 00000000..45ce3a4e
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java
@@ -0,0 +1,228 @@
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.matchers.memories.timely;
10
11import java.util.Collection;
12import java.util.Iterator;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Objects;
16import java.util.Set;
17import java.util.TreeMap;
18
19import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory;
20import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
21import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
22import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
23import tools.refinery.viatra.runtime.matchers.util.Direction;
24import tools.refinery.viatra.runtime.matchers.util.Signed;
25import tools.refinery.viatra.runtime.matchers.util.TimelyMemory;
26import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
27import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
28
29/**
30 * Common parts of timely default and timely unary implementations.
31 *
32 * @noextend This class is not intended to be subclassed by clients.
33 * @author Tamas Szabo
34 * @since 2.3
35 */
36abstract class AbstractTimelyMaskedMemory<Timestamp extends Comparable<Timestamp>, KeyType>
37 extends MaskedTupleMemory<Timestamp> {
38
39 protected final TreeMap<Timestamp, Set<KeyType>> foldingStates;
40 protected final Map<KeyType, TimelyMemory<Timestamp>> memoryMap;
41 protected final boolean isLazy;
42
43 public AbstractTimelyMaskedMemory(final TupleMask mask, final Object owner, final boolean isLazy) {
44 super(mask, owner);
45 this.isLazy = isLazy;
46 this.memoryMap = CollectionsFactory.createMap();
47 this.foldingStates = this.isLazy ? CollectionsFactory.createTreeMap() : null;
48 }
49
50 @Override
51 public void initializeWith(final MaskedTupleMemory<Timestamp> other, final Timestamp defaultValue) {
52 final Iterable<Tuple> signatures = other.getSignatures();
53 for (final Tuple signature : signatures) {
54 if (other.isTimely()) {
55 final Map<Tuple, Timeline<Timestamp>> tupleMap = other.getWithTimeline(signature);
56 for (final Entry<Tuple, Timeline<Timestamp>> entry : tupleMap.entrySet()) {
57 for (final Signed<Timestamp> signed : entry.getValue().asChangeSequence()) {
58 if (signed.getDirection() == Direction.DELETE) {
59 this.removeWithTimestamp(entry.getKey(), signed.getPayload());
60 } else {
61 this.addWithTimestamp(entry.getKey(), signed.getPayload());
62 }
63 }
64 }
65 } else {
66 final Collection<Tuple> tuples = other.get(signature);
67 for (final Tuple tuple : tuples) {
68 this.addWithTimestamp(tuple, defaultValue);
69 }
70 }
71 }
72 }
73
74 public boolean isPresentAtInfinityInteral(KeyType key) {
75 final TimelyMemory<Timestamp> values = this.memoryMap.get(key);
76 if (values == null) {
77 return false;
78 } else {
79 return values.getCountAtInfinity() != 0;
80 }
81 }
82
83 @Override
84 public void clear() {
85 this.memoryMap.clear();
86 }
87
88 @Override
89 public int getKeysetSize() {
90 return this.memoryMap.keySet().size();
91 }
92
93 @Override
94 public int getTotalSize() {
95 int i = 0;
96 for (final Entry<KeyType, TimelyMemory<Timestamp>> entry : this.memoryMap.entrySet()) {
97 i += entry.getValue().size();
98 }
99 return i;
100 }
101
102 @Override
103 public Iterator<Tuple> iterator() {
104 return this.memoryMap.values().stream().flatMap(e -> e.keySet().stream()).iterator();
105 }
106
107 protected Collection<Tuple> getInternal(final KeyType key) {
108 final TimelyMemory<Timestamp> memory = this.memoryMap.get(key);
109 if (memory == null) {
110 return null;
111 } else {
112 return memory.getTuplesAtInfinity();
113 }
114 }
115
116 public Map<Tuple, Timeline<Timestamp>> getWithTimestampInternal(final KeyType key) {
117 final TimelyMemory<Timestamp> memory = this.memoryMap.get(key);
118 if (memory == null) {
119 return null;
120 } else {
121 return memory.asMap();
122 }
123 }
124
125 protected Diff<Timestamp> removeInternal(final KeyType key, final Tuple tuple, final Timestamp timestamp) {
126 Timestamp oldResumableTimestamp = null;
127 Timestamp newResumableTimestamp = null;
128
129 final TimelyMemory<Timestamp> keyMemory = this.memoryMap.get(key);
130 if (keyMemory == null) {
131 throw raiseDuplicateDeletion(tuple);
132 }
133
134 if (this.isLazy) {
135 oldResumableTimestamp = keyMemory.getResumableTimestamp();
136 }
137
138 Diff<Timestamp> diff = null;
139 try {
140 diff = keyMemory.remove(tuple, timestamp);
141 } catch (final IllegalStateException e) {
142 throw raiseDuplicateDeletion(tuple);
143 }
144 if (keyMemory.isEmpty()) {
145 this.memoryMap.remove(key);
146 }
147
148 if (this.isLazy) {
149 newResumableTimestamp = keyMemory.getResumableTimestamp();
150 if (!Objects.equals(oldResumableTimestamp, newResumableTimestamp)) {
151 unregisterFoldingState(oldResumableTimestamp, key);
152 registerFoldingState(newResumableTimestamp, key);
153 }
154 }
155
156 return diff;
157 }
158
159 protected Diff<Timestamp> addInternal(final KeyType key, final Tuple tuple, final Timestamp timestamp) {
160 Timestamp oldResumableTimestamp = null;
161 Timestamp newResumableTimestamp = null;
162
163 final TimelyMemory<Timestamp> keyMemory = this.memoryMap.computeIfAbsent(key,
164 k -> new TimelyMemory<Timestamp>(this.isLazy));
165
166 if (this.isLazy) {
167 oldResumableTimestamp = keyMemory.getResumableTimestamp();
168 }
169
170 final Diff<Timestamp> diff = keyMemory.put(tuple, timestamp);
171
172 if (this.isLazy) {
173 newResumableTimestamp = keyMemory.getResumableTimestamp();
174 if (!Objects.equals(oldResumableTimestamp, newResumableTimestamp)) {
175 unregisterFoldingState(oldResumableTimestamp, key);
176 registerFoldingState(newResumableTimestamp, key);
177 }
178 }
179
180 return diff;
181 }
182
183 @Override
184 public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) {
185 return removeWithTimestamp(tuple, null, timestamp);
186 }
187
188 @Override
189 public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Timestamp timestamp) {
190 return addWithTimestamp(tuple, null, timestamp);
191 }
192
193 @Override
194 public boolean isTimely() {
195 return true;
196 }
197
198 protected void registerFoldingState(final Timestamp timestamp, final KeyType key) {
199 if (timestamp != null) {
200 this.foldingStates.compute(timestamp, (k, v) -> {
201 if (v == null) {
202 v = CollectionsFactory.createSet();
203 }
204 v.add(key);
205 return v;
206 });
207 }
208 }
209
210 protected void unregisterFoldingState(final Timestamp timestamp, final KeyType key) {
211 if (timestamp != null) {
212 this.foldingStates.compute(timestamp, (k, v) -> {
213 v.remove(key);
214 return v.isEmpty() ? null : v;
215 });
216 }
217 }
218
219 @Override
220 public Timestamp getResumableTimestamp() {
221 if (this.foldingStates == null || this.foldingStates.isEmpty()) {
222 return null;
223 } else {
224 return this.foldingStates.firstKey();
225 }
226 }
227
228}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java
new file mode 100644
index 00000000..ca06685a
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java
@@ -0,0 +1,100 @@
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.matchers.memories.timely;
10
11import java.util.Collection;
12import java.util.Iterator;
13import java.util.Map;
14import java.util.Map.Entry;
15
16import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.util.Direction;
20import tools.refinery.viatra.runtime.matchers.util.Signed;
21import tools.refinery.viatra.runtime.matchers.util.TimelyMemory;
22import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
23import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
24
25/**
26 * Common parts of timely nullary and timely identity implementations.
27 *
28 * @noextend This class is not intended to be subclassed by clients.
29 * @author Tamas Szabo
30 * @since 2.3
31 */
32abstract class AbstractTimelyTrivialMaskedMemory<Timestamp extends Comparable<Timestamp>> extends MaskedTupleMemory<Timestamp> {
33
34 protected final TimelyMemory<Timestamp> memory;
35
36 protected AbstractTimelyTrivialMaskedMemory(final TupleMask mask, final Object owner, final boolean isLazy) {
37 super(mask, owner);
38 this.memory = new TimelyMemory<Timestamp>(isLazy);
39 }
40
41 @Override
42 public void initializeWith(final MaskedTupleMemory<Timestamp> other, final Timestamp defaultValue) {
43 final Iterable<Tuple> signatures = other.getSignatures();
44 for (final Tuple signature : signatures) {
45 if (other.isTimely()) {
46 final Map<Tuple, Timeline<Timestamp>> tupleMap = other.getWithTimeline(signature);
47 for (final Entry<Tuple, Timeline<Timestamp>> entry : tupleMap.entrySet()) {
48 for (final Signed<Timestamp> signed : entry.getValue().asChangeSequence()) {
49 if (signed.getDirection() == Direction.DELETE) {
50 this.removeWithTimestamp(entry.getKey(), signed.getPayload());
51 } else {
52 this.addWithTimestamp(entry.getKey(), signed.getPayload());
53 }
54 }
55 }
56 } else {
57 final Collection<Tuple> tuples = other.get(signature);
58 for (final Tuple tuple : tuples) {
59 this.removeWithTimestamp(tuple, defaultValue);
60 }
61 }
62 }
63 }
64
65 @Override
66 public void clear() {
67 this.memory.clear();
68 }
69
70 @Override
71 public int getTotalSize() {
72 return this.memory.size();
73 }
74
75 @Override
76 public Iterator<Tuple> iterator() {
77 return this.memory.keySet().iterator();
78 }
79
80 @Override
81 public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) {
82 return removeWithTimestamp(tuple, null, timestamp);
83 }
84
85 @Override
86 public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Timestamp timestamp) {
87 return addWithTimestamp(tuple, null, timestamp);
88 }
89
90 @Override
91 public boolean isTimely() {
92 return true;
93 }
94
95 @Override
96 public Timestamp getResumableTimestamp() {
97 return this.memory.getResumableTimestamp();
98 }
99
100}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java
new file mode 100644
index 00000000..623d7399
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java
@@ -0,0 +1,98 @@
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.matchers.memories.timely;
11
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Map;
15import java.util.Set;
16
17import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.TimelyMemory;
22import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
23import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
24
25/**
26 * Default timely implementation that covers all cases.
27 *
28 * @author Tamas Szabo
29 * @since 2.3
30 */
31public final class TimelyDefaultMaskedTupleMemory<Timestamp extends Comparable<Timestamp>>
32 extends AbstractTimelyMaskedMemory<Timestamp, Tuple> {
33
34 public TimelyDefaultMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) {
35 super(mask, owner, isLazy);
36 }
37
38 @Override
39 public Iterable<Tuple> getSignatures() {
40 return this.memoryMap.keySet();
41 }
42
43 @Override
44 public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature,
45 final Timestamp timestamp) {
46 final Tuple key = mask.transform(tuple);
47 return removeInternal(key, tuple, timestamp);
48 }
49
50 @Override
51 public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature,
52 final Timestamp timestamp) {
53 final Tuple key = this.mask.transform(tuple);
54 return addInternal(key, tuple, timestamp);
55 }
56
57 @Override
58 public Collection<Tuple> get(final ITuple signature) {
59 return getInternal(signature.toImmutable());
60 }
61
62 @Override
63 public Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature) {
64 return getWithTimestampInternal(signature.toImmutable());
65 }
66
67 @Override
68 public boolean isPresentAtInfinity(final ITuple signature) {
69 return isPresentAtInfinityInteral(signature.toImmutable());
70 }
71
72 @Override
73 public Set<Tuple> getResumableSignatures() {
74 if (this.foldingStates == null || this.foldingStates.isEmpty()) {
75 return Collections.emptySet();
76 } else {
77 return this.foldingStates.firstEntry().getValue();
78 }
79 }
80
81 @Override
82 public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) {
83 final Map<Tuple, Map<Tuple, Diff<Timestamp>>> result = CollectionsFactory.createMap();
84 final Timestamp resumableTimestamp = this.getResumableTimestamp();
85 if (resumableTimestamp == null || resumableTimestamp.compareTo(timestamp) != 0) {
86 throw new IllegalStateException("Expected to continue folding at " + resumableTimestamp + "!");
87 }
88 final Set<Tuple> signatures = this.foldingStates.remove(timestamp);
89 for (final Tuple signature : signatures) {
90 final TimelyMemory<Timestamp> memory = this.memoryMap.get(signature);
91 final Map<Tuple, Diff<Timestamp>> diffMap = memory.resumeAt(resumableTimestamp);
92 result.put(signature, diffMap);
93 registerFoldingState(memory.getResumableTimestamp(), signature);
94 }
95 return result;
96 }
97
98}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java
new file mode 100644
index 00000000..568f274d
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java
@@ -0,0 +1,106 @@
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.matchers.memories.timely;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Set;
16
17import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
22import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
23
24/**
25 * Timely specialization for identity mask.
26 *
27 * @author Tamas Szabo
28 * @since 2.3
29 */
30public final class TimelyIdentityMaskedTupleMemory<Timestamp extends Comparable<Timestamp>>
31 extends AbstractTimelyTrivialMaskedMemory<Timestamp> {
32
33 public TimelyIdentityMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) {
34 super(mask, owner, isLazy);
35 if (!mask.isIdentity())
36 throw new IllegalArgumentException(mask.toString());
37 }
38
39 @Override
40 public int getKeysetSize() {
41 return this.memory.size();
42 }
43
44 @Override
45 public Iterable<Tuple> getSignatures() {
46 return this.memory.keySet();
47 }
48
49 @Override
50 public Collection<Tuple> get(final ITuple signature) {
51 if (this.memory.getTuplesAtInfinity().contains(signature)) {
52 return Collections.singleton(signature.toImmutable());
53 } else {
54 return null;
55 }
56 }
57
58 @Override
59 public Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature) {
60 final Timeline<Timestamp> value = this.memory.get(signature);
61 if (value != null) {
62 return Collections.singletonMap(signature.toImmutable(), value);
63 } else {
64 return null;
65 }
66 }
67
68 @Override
69 public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) {
70 try {
71 return this.memory.remove(tuple, timestamp);
72 } catch (final IllegalStateException e) {
73 throw raiseDuplicateDeletion(tuple);
74 }
75 }
76
77 @Override
78 public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) {
79 return this.memory.put(tuple, timestamp);
80 }
81
82 @Override
83 public boolean isPresentAtInfinity(final ITuple signature) {
84 return this.memory.isPresentAtInfinity(signature.toImmutable());
85 }
86
87 @Override
88 public Set<Tuple> getResumableSignatures() {
89 if (this.memory.getResumableTimestamp() != null) {
90 return this.memory.getResumableTuples();
91 } else {
92 return Collections.emptySet();
93 }
94 }
95
96 @Override
97 public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) {
98 final Map<Tuple, Diff<Timestamp>> diffMap = this.memory.resumeAt(timestamp);
99 final Map<Tuple, Map<Tuple, Diff<Timestamp>>> result = CollectionsFactory.createMap();
100 for (final Entry<Tuple, Diff<Timestamp>> entry : diffMap.entrySet()) {
101 result.put(entry.getKey(), Collections.singletonMap(entry.getKey(), entry.getValue()));
102 }
103 return result;
104 }
105
106}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java
new file mode 100644
index 00000000..75987a89
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java
@@ -0,0 +1,108 @@
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.matchers.memories.timely;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
20import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
21import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
22
23/**
24 * Timely specialization for nullary mask.
25 *
26 * @author Tamas Szabo
27 * @since 2.3
28 */
29public final class TimelyNullaryMaskedTupleMemory<Timestamp extends Comparable<Timestamp>>
30 extends AbstractTimelyTrivialMaskedMemory<Timestamp> {
31
32 protected static final Tuple EMPTY_TUPLE = Tuples.staticArityFlatTupleOf();
33 protected static final Set<Tuple> UNIT_RELATION = Collections.singleton(EMPTY_TUPLE);
34 protected static final Set<Tuple> EMPTY_RELATION = Collections.emptySet();
35
36 public TimelyNullaryMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) {
37 super(mask, owner, isLazy);
38 if (0 != mask.getSize()) {
39 throw new IllegalArgumentException(mask.toString());
40 }
41 }
42
43 @Override
44 public int getKeysetSize() {
45 return this.memory.isEmpty() ? 0 : 1;
46 }
47
48 @Override
49 public Iterable<Tuple> getSignatures() {
50 return this.memory.isEmpty() ? EMPTY_RELATION : UNIT_RELATION;
51 }
52
53 @Override
54 public Collection<Tuple> get(final ITuple signature) {
55 if (0 == signature.getSize()) {
56 return this.memory.getTuplesAtInfinity();
57 } else {
58 return null;
59 }
60 }
61
62 @Override
63 public Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature) {
64 if (0 == signature.getSize()) {
65 return this.memory.asMap();
66 } else {
67 return null;
68 }
69 }
70
71 @Override
72 public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) {
73 try {
74 return this.memory.remove(tuple, timestamp);
75 } catch (final IllegalStateException e) {
76 throw raiseDuplicateDeletion(tuple);
77 }
78 }
79
80 @Override
81 public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) {
82 return this.memory.put(tuple, timestamp);
83 }
84
85 @Override
86 public boolean isPresentAtInfinity(final ITuple signature) {
87 if (0 == signature.getSize()) {
88 return this.memory.getCountAtInfinity() > 0;
89 } else {
90 return false;
91 }
92 }
93
94 @Override
95 public Set<Tuple> getResumableSignatures() {
96 if (this.memory.getResumableTimestamp() != null) {
97 return UNIT_RELATION;
98 } else {
99 return EMPTY_RELATION;
100 }
101 }
102
103 @Override
104 public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) {
105 return Collections.singletonMap(EMPTY_TUPLE, this.memory.resumeAt(timestamp));
106 }
107
108}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java
new file mode 100644
index 00000000..178193af
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java
@@ -0,0 +1,133 @@
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.matchers.memories.timely;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Iterator;
14import java.util.Map;
15import java.util.Set;
16
17import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
21import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
22import tools.refinery.viatra.runtime.matchers.util.TimelyMemory;
23import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
25
26/**
27 * Timely specialization for unary mask.
28 *
29 * @author Tamas Szabo
30 * @since 2.3
31 */
32public final class TimelyUnaryMaskedTupleMemory<Timestamp extends Comparable<Timestamp>>
33 extends AbstractTimelyMaskedMemory<Timestamp, Object> {
34
35 protected final int keyPosition;
36
37 public TimelyUnaryMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) {
38 super(mask, owner, isLazy);
39 if (1 != mask.getSize())
40 throw new IllegalArgumentException(mask.toString());
41 this.keyPosition = mask.indices[0];
42 }
43
44 @Override
45 public Iterable<Tuple> getSignatures() {
46 return () -> {
47 final Iterator<Object> wrapped = this.memoryMap.keySet().iterator();
48 return new Iterator<Tuple>() {
49 @Override
50 public boolean hasNext() {
51 return wrapped.hasNext();
52 }
53
54 @Override
55 public Tuple next() {
56 final Object key = wrapped.next();
57 return Tuples.staticArityFlatTupleOf(key);
58 }
59 };
60 };
61 }
62
63 @Override
64 public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) {
65 final Object key = tuple.get(keyPosition);
66 return removeInternal(key, tuple, timestamp);
67 }
68
69 @Override
70 public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) {
71 final Object key = tuple.get(keyPosition);
72 return addInternal(key, tuple, timestamp);
73 }
74
75 @Override
76 public Collection<Tuple> get(final ITuple signature) {
77 return getInternal(signature.get(0));
78 }
79
80 @Override
81 public Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature) {
82 return getWithTimestampInternal(signature.get(0));
83 }
84
85 @Override
86 public boolean isPresentAtInfinity(ITuple signature) {
87 return isPresentAtInfinityInteral(signature.get(0));
88 }
89
90 @Override
91 public Iterable<Tuple> getResumableSignatures() {
92 if (this.foldingStates == null || this.foldingStates.isEmpty()) {
93 return Collections.emptySet();
94 } else {
95 return () -> {
96 final Iterator<Object> wrapped = this.foldingStates.firstEntry().getValue().iterator();
97 return new Iterator<Tuple>() {
98 @Override
99 public boolean hasNext() {
100 return wrapped.hasNext();
101 }
102
103 @Override
104 public Tuple next() {
105 final Object key = wrapped.next();
106 return Tuples.staticArityFlatTupleOf(key);
107 }
108 };
109 };
110 }
111 }
112
113 @Override
114 public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) {
115 final Map<Tuple, Map<Tuple, Diff<Timestamp>>> result = CollectionsFactory.createMap();
116 final Timestamp resumableTimestamp = this.getResumableTimestamp();
117 if (resumableTimestamp == null) {
118 throw new IllegalStateException("There is nothing to fold!");
119 } else if (resumableTimestamp.compareTo(timestamp) != 0) {
120 throw new IllegalStateException("Expected to continue folding at " + resumableTimestamp + "!");
121 }
122
123 final Set<Object> signatures = this.foldingStates.remove(timestamp);
124 for (final Object signature : signatures) {
125 final TimelyMemory<Timestamp> memory = this.memoryMap.get(signature);
126 final Map<Tuple, Diff<Timestamp>> diffMap = memory.resumeAt(resumableTimestamp);
127 result.put(Tuples.staticArityFlatTupleOf(signature), diffMap);
128 registerFoldingState(memory.getResumableTimestamp(), signature);
129 }
130 return result;
131 }
132
133}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java
new file mode 100644
index 00000000..c9f4b305
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java
@@ -0,0 +1,108 @@
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.matchers.planning;
11
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
15import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator;
16import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19
20/**
21 *
22 * An implicit common parameter is the "effort" PatternDescription. This
23 * indicates that the build request is part of an effort to build the matcher of
24 * the given pattern; it it important to record this during code generation so
25 * that the generated code can be separated according to patterns.
26 *
27 * @param <Collector>
28 * the handle of a receiver-like RETE ending to which plans can be
29 * connected
30 * @author Gabor Bergmann
31 * @noimplement This interface is not intended to be implemented by clients.
32 */
33public interface IOperationCompiler<Collector> {
34
35 /**
36 * @throws ViatraQueryRuntimeException
37 */
38 public Collector patternCollector(PQuery pattern);
39
40 public void buildConnection(SubPlan parentPlan, Collector collector);
41
42 /**
43 * @since 0.9
44 */
45 public void patternFinished(PQuery pattern, Collector collector);
46
47 /**
48 * @throws ViatraQueryRuntimeException
49 */
50 public SubPlan patternCallPlan(Tuple nodes, PQuery supplierKey);
51
52 public SubPlan transitiveInstantiationPlan(Tuple nodes);
53
54 public SubPlan directInstantiationPlan(Tuple nodes);
55
56 public SubPlan transitiveGeneralizationPlan(Tuple nodes);
57
58 public SubPlan directGeneralizationPlan(Tuple nodes);
59
60 public SubPlan transitiveContainmentPlan(Tuple nodes);
61
62 public SubPlan directContainmentPlan(Tuple nodes);
63
64 public SubPlan binaryEdgeTypePlan(Tuple nodes, Object supplierKey);
65
66 public SubPlan ternaryEdgeTypePlan(Tuple nodes, Object supplierKey);
67
68 public SubPlan unaryTypePlan(Tuple nodes, Object supplierKey);
69
70 public SubPlan buildStartingPlan(Object[] constantValues, Object[] constantNames);
71
72 public SubPlan buildEqualityChecker(SubPlan parentPlan, int[] indices);
73
74 public SubPlan buildInjectivityChecker(SubPlan parentPlan, int subject, int[] inequalIndices);
75
76 public SubPlan buildTransitiveClosure(SubPlan parentPlan);
77
78 public SubPlan buildTrimmer(SubPlan parentPlan, TupleMask trimMask, boolean enforceUniqueness);
79
80 public SubPlan buildBetaNode(SubPlan primaryPlan, SubPlan sidePlan,
81 TupleMask primaryMask, TupleMask sideMask, TupleMask complementer, boolean negative);
82
83 public SubPlan buildCounterBetaNode(SubPlan primaryPlan, SubPlan sidePlan,
84 TupleMask primaryMask, TupleMask originalSideMask, TupleMask complementer,
85 Object aggregateResultCalibrationElement);
86
87 public SubPlan buildCountCheckBetaNode(SubPlan primaryPlan, SubPlan sidePlan,
88 TupleMask primaryMask, TupleMask originalSideMask, int resultPositionInSignature);
89
90 public SubPlan buildPredicateChecker(IExpressionEvaluator evaluator, Map<String, Integer> tupleNameMap,
91 SubPlan parentPlan);
92 public SubPlan buildFunctionEvaluator(IExpressionEvaluator evaluator, Map<String, Integer> tupleNameMap,
93 SubPlan parentPlan, Object computedResultCalibrationElement);
94
95 /**
96 * @return an operation compiler that potentially acts on a separate container
97 */
98 public IOperationCompiler<Collector> getNextContainer();
99
100 /**
101 * @return an operation compiler that puts build actions on the tab of the given pattern
102 * @since 0.9
103 */
104 public IOperationCompiler<Collector> putOnTab(PQuery effort /*, IPatternMatcherContext context*/);
105
106 public void reinitialize();
107
108} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java
new file mode 100644
index 00000000..6ce9d91b
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java
@@ -0,0 +1,29 @@
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.matchers.planning;
11
12import org.apache.log4j.Logger;
13import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
14import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
15import tools.refinery.viatra.runtime.matchers.psystem.PBody;
16
17/**
18 * An algorithm that builds a query plan based on a PSystem representation of a body of constraints. This interface is
19 * for internal use of the various query backends.
20 *
21 * @author Gabor Bergmann
22 */
23public interface IQueryPlannerStrategy {
24
25 /**
26 * @throws ViatraQueryRuntimeException
27 */
28 public SubPlan plan(PBody pSystem, Logger logger, IQueryMetaContext context);
29}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java
new file mode 100644
index 00000000..501ddf73
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java
@@ -0,0 +1,102 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.matchers.planning;
10
11import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
12
13/**
14 * @author Zoltan Ujhelyi
15 * @since 0.9
16 */
17public class QueryProcessingException extends ViatraQueryRuntimeException {
18
19 private static final long serialVersionUID = -8272290113656867086L;
20 /**
21 * Binding the '{n}' (n = 1..N) strings to contextual conditions in 'context'
22 *
23 * @param context
24 * : array of context-sensitive Strings
25 */
26 protected static String bind(String message, String[] context) {
27 if (context == null)
28 return message;
29
30 String internal = message;
31 for (int i = 0; i < context.length; i++) {
32 internal = internal.replace("{" + (i + 1) + "}", context[i] != null ? context[i] : "<<null>>");
33 }
34 return internal;
35 }
36
37 private Object patternDescription;
38 private String shortMessage;
39
40 /**
41 * @param message
42 * The template of the exception message
43 * @param context
44 * The data elements to be used to instantiate the template. Can be null if no context parameter is
45 * defined
46 * @param patternDescription
47 * the PatternDescription where the exception occurred
48 * @since 2.0
49 */
50 public QueryProcessingException(String message, Object patternDescription) {
51 super(message);
52 initializeFields(message, patternDescription);
53 }
54
55 /**
56 * @param message
57 * The template of the exception message
58 * @param context
59 * The data elements to be used to instantiate the template. Can be null if no context parameter is
60 * defined
61 * @param patternDescription
62 * the PatternDescription where the exception occurred
63 */
64 public QueryProcessingException(String message, String[] context, String shortMessage, Object patternDescription) {
65 super(bind(message, context));
66 initializeFields(shortMessage, patternDescription);
67 }
68
69 /**
70 * @param message
71 * The template of the exception message
72 * @param context
73 * The data elements to be used to instantiate the template. Can be null if no context parameter is
74 * defined
75 * @param patternDescription
76 * the PatternDescription where the exception occurred
77 */
78 public QueryProcessingException(String message, String[] context, String shortMessage, Object patternDescription,
79 Throwable cause) {
80 super(bind(message, context), cause);
81 initializeFields(shortMessage, patternDescription);
82 }
83
84 public Object getPatternDescription() {
85 return patternDescription;
86 }
87
88 public String getShortMessage() {
89 return shortMessage;
90 }
91
92 private void initializeFields(String shortMessage, Object patternDescription) {
93 this.patternDescription = patternDescription;
94 this.shortMessage = shortMessage;
95 }
96
97
98 public void setPatternDescription(Object patternDescription) {
99 this.patternDescription = patternDescription;
100 }
101
102} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java
new file mode 100644
index 00000000..1998df9d
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java
@@ -0,0 +1,240 @@
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.matchers.planning;
11
12import java.util.Arrays;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Set;
16import java.util.WeakHashMap;
17import java.util.stream.Collectors;
18
19import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
20import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper;
21import tools.refinery.viatra.runtime.matchers.planning.operations.POperation;
22import tools.refinery.viatra.runtime.matchers.planning.operations.PProject;
23import tools.refinery.viatra.runtime.matchers.planning.operations.PStart;
24import tools.refinery.viatra.runtime.matchers.psystem.PBody;
25import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
26import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
27import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement;
28
29/**
30 * A plan representing a subset of (or possibly all the) constraints evaluated. A SubPlan instance is responsible for
31 * representing a state of the plan; but after it is initialized it is expected be immutable
32 * (exception: inferred constraints, see {@link #inferConstraint(PConstraint)}).
33 *
34 * <p> A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans.
35 * Important maintained information: <ul>
36 * <li>set of <b>variables</b> whose values are known when the runtime evaluation is at this stage,
37 * <li>set of <b>constraints</b> that are known to hold true at this point.
38 * </ul>
39 *
40 * <p> Recommended to instantiate via a {@link SubPlanFactory} or subclasses,
41 * so that query planners can subclass SubPlan if needed.
42 *
43 * @author Gabor Bergmann
44 *
45 */
46public class SubPlan {
47 private PBody body;
48 private List<? extends SubPlan> parentPlans;
49 private POperation operation;
50
51 private final Set<PVariable> visibleVariables;
52 private final Set<PVariable> allVariables;
53 private final Set<PVariable> introducedVariables; // delta compared to first parent
54 private Set<PConstraint> allConstraints;
55 private Set<PConstraint> deltaConstraints; // delta compared to all parents
56 private Set<PConstraint> externallyInferredConstraints; // inferred in addition to direct consequences of the operation and parents
57
58
59
60
61
62 /**
63 * A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans.
64 */
65 public SubPlan(PBody body, POperation operation, SubPlan... parentPlans) {
66 this(body, operation, Arrays.asList(parentPlans));
67 }
68 /**
69 * A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans.
70 */
71 public SubPlan(PBody body, POperation operation, List<? extends SubPlan> parentPlans) {
72 super();
73 this.body = body;
74 this.parentPlans = parentPlans;
75 this.operation = operation;
76
77 this.externallyInferredConstraints = new HashSet<PConstraint>();
78 this.deltaConstraints = new HashSet<PConstraint>(operation.getDeltaConstraints());
79
80 this.allVariables = new HashSet<PVariable>();
81 for (PConstraint constraint: deltaConstraints) {
82 this.allVariables.addAll(constraint.getDeducedVariables());
83 }
84 this.allConstraints = new HashSet<PConstraint>(deltaConstraints);
85 for (SubPlan parentPlan: parentPlans) {
86 this.allConstraints.addAll(parentPlan.getAllEnforcedConstraints());
87 this.allVariables.addAll(parentPlan.getAllDeducedVariables());
88 }
89
90 // TODO this is ugly a bit
91 if (operation instanceof PStart) {
92 this.visibleVariables = new HashSet<PVariable>(((PStart) operation).getAPrioriVariables());
93 this.allVariables.addAll(visibleVariables);
94 } else if (operation instanceof PProject) {
95 this.visibleVariables = new HashSet<PVariable>(((PProject) operation).getToVariables());
96 } else {
97 this.visibleVariables = new HashSet<PVariable>();
98 for (SubPlan parentPlan: parentPlans)
99 this.visibleVariables.addAll(parentPlan.getVisibleVariables());
100 for (PConstraint constraint: deltaConstraints)
101 this.visibleVariables.addAll(constraint.getDeducedVariables());
102 }
103
104 this.introducedVariables = new HashSet<PVariable>(this.visibleVariables);
105 if (!parentPlans.isEmpty())
106 introducedVariables.removeAll(parentPlans.get(0).getVisibleVariables());
107
108 operation.checkConsistency(this);
109 }
110
111
112 @Override
113 public String toString() {
114 return toLongString();
115 }
116 public String toShortString() {
117 return String.format("Plan{%s}:%s",
118 visibleVariables.stream().map(PVariable::getName).collect(Collectors.joining(",")),
119 operation.getShortName());
120 }
121 public String toLongString() {
122 return String.format("%s<%s>",
123 toShortString(),
124 parentPlans.stream().map(Object::toString).collect(Collectors.joining("; ")));
125 }
126
127
128 /**
129 * All constraints that are known to hold at this point
130 */
131 public Set<PConstraint> getAllEnforcedConstraints() {
132 return allConstraints;
133 }
134
135 /**
136 * The new constraints enforced at this stage of plan, that aren't yet enforced at parents
137 * (results are also included in {@link SubPlan#getAllEnforcedConstraints()})
138 */
139 public Set<PConstraint> getDeltaEnforcedConstraints() {
140 return deltaConstraints;
141 }
142
143 /**
144 * Indicate that a given constraint was found to be automatically satisfied at this point
145 * without additional operations.
146 * (results are also included in {@link SubPlan#getDeltaEnforcedConstraints()})
147 *
148 * <p>Warning: not propagated automatically to child plans,
149 * so best to invoke before constructing further SubPlans. </p>
150 */
151 public void inferConstraint(PConstraint constraint) {
152 externallyInferredConstraints.add(constraint);
153 deltaConstraints.add(constraint);
154 allConstraints.add(constraint);
155 }
156
157 public PBody getBody() {
158 return body;
159 }
160
161 /**
162 * Variables which are assigned a value at this point
163 * (results are also included in {@link SubPlan#getAllDeducedVariables()})
164 */
165 public Set<PVariable> getVisibleVariables() {
166 return visibleVariables;
167 }
168 /**
169 * Variables which have been assigned a value;
170 * includes visible variables (see {@link #getVisibleVariables()})
171 * and additionally any variables hidden by a projection (see {@link PProject}).
172 */
173 public Set<PVariable> getAllDeducedVariables() {
174 return allVariables;
175 }
176 /**
177 * Delta compared to first parent: variables that are visible here but were not visible at the first parent.
178 */
179 public Set<PVariable> getIntroducedVariables() {
180 return introducedVariables;
181 }
182 public List<? extends SubPlan> getParentPlans() {
183 return parentPlans;
184 }
185 public POperation getOperation() {
186 return operation;
187 }
188
189
190 /**
191 * The closure of all type judgments of enforced constraints at this point.
192 * <p> No subsumption applied.
193 */
194 public Set<TypeJudgement> getAllImpliedTypeJudgements(IQueryMetaContext context) {
195 Set<TypeJudgement> impliedJudgements = allImpliedTypeJudgements.get(context);
196 if (impliedJudgements == null) {
197 Set<TypeJudgement> equivalentJudgements = TypeHelper.getDirectJudgements(getAllEnforcedConstraints(), context);
198 impliedJudgements = TypeHelper.typeClosure(equivalentJudgements, context);
199
200 allImpliedTypeJudgements.put(context, impliedJudgements);
201 }
202 return impliedJudgements;
203 }
204 private WeakHashMap<IQueryMetaContext, Set<TypeJudgement>> allImpliedTypeJudgements = new WeakHashMap<IQueryMetaContext, Set<TypeJudgement>>();
205
206
207 @Override
208 public int hashCode() {
209 final int prime = 31;
210 int result = 1;
211 result = prime * result
212 + ((operation == null) ? 0 : operation.hashCode());
213 result = prime * result
214 + ((parentPlans == null) ? 0 : parentPlans.hashCode());
215 return result;
216 }
217 @Override
218 public boolean equals(Object obj) {
219 if (this == obj)
220 return true;
221 if (obj == null)
222 return false;
223 if (!(obj instanceof SubPlan))
224 return false;
225 SubPlan other = (SubPlan) obj;
226 if (operation == null) {
227 if (other.operation != null)
228 return false;
229 } else if (!operation.equals(other.operation))
230 return false;
231 if (parentPlans == null) {
232 if (other.parentPlans != null)
233 return false;
234 } else if (!parentPlans.equals(other.parentPlans))
235 return false;
236 return true;
237 }
238
239
240}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java
new file mode 100644
index 00000000..d0df5fac
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.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.matchers.planning;
10
11import tools.refinery.viatra.runtime.matchers.planning.operations.POperation;
12import tools.refinery.viatra.runtime.matchers.psystem.PBody;
13
14/**
15 * Single entry point for creating subplans.
16 * Can be subclassed by query planner to provide specialized SubPlans.
17 * @author Bergmann Gabor
18 *
19 */
20public class SubPlanFactory {
21
22 protected PBody body;
23
24 public SubPlanFactory(PBody body) {
25 super();
26 this.body = body;
27 }
28
29 public SubPlan createSubPlan(POperation operation, SubPlan... parentPlans) {
30 return new SubPlan(body, operation, parentPlans);
31 }
32
33}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java
new file mode 100644
index 00000000..ed5d1cbb
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java
@@ -0,0 +1,165 @@
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.matchers.planning.helpers;
11
12import java.util.Collection;
13import java.util.HashSet;
14import java.util.Map;
15import java.util.Set;
16
17import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
18import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
19import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
20import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
21import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory;
22import tools.refinery.viatra.runtime.matchers.planning.operations.PProject;
23import tools.refinery.viatra.runtime.matchers.psystem.PBody;
24import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
25import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
26import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
27import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
28
29/**
30 * @author Gabor Bergmann
31 *
32 */
33public class BuildHelper {
34
35 private BuildHelper() {
36 // Hiding constructor for utility class
37 }
38
39// public static SubPlan naturalJoin(IOperationCompiler buildable,
40// SubPlan primaryPlan, SubPlan secondaryPlan) {
41// JoinHelper joinHelper = new JoinHelper(primaryPlan, secondaryPlan);
42// return buildable.buildBetaNode(primaryPlan, secondaryPlan, joinHelper.getPrimaryMask(),
43// joinHelper.getSecondaryMask(), joinHelper.getComplementerMask(), false);
44// }
45
46
47 /**
48 * Reduces the number of tuples by trimming (existentially quantifying) the set of variables that <ul>
49 * <li> are visible in the subplan,
50 * <li> are not exported parameters,
51 * <li> have all their constraints already enforced in the subplan,
52 * </ul> and thus will not be needed anymore.
53 *
54 * @param onlyIfNotDetermined if true, no trimming performed unless there is at least one variable that is not functionally determined
55 * @return the plan after the trimming (possibly the original)
56 * @since 1.5
57 */
58 public static SubPlan trimUnneccessaryVariables(SubPlanFactory planFactory, /*IOperationCompiler buildable,*/
59 SubPlan plan, boolean onlyIfNotDetermined, QueryAnalyzer analyzer) {
60 Set<PVariable> canBeTrimmed = new HashSet<PVariable>();
61 Set<PVariable> variablesInPlan = plan.getVisibleVariables();
62 for (PVariable trimCandidate : variablesInPlan) {
63 if (trimCandidate.getReferringConstraintsOfType(ExportedParameter.class).isEmpty()) {
64 if (plan.getAllEnforcedConstraints().containsAll(trimCandidate.getReferringConstraints()))
65 canBeTrimmed.add(trimCandidate);
66 }
67 }
68 final Set<PVariable> retainedVars = setMinus(variablesInPlan, canBeTrimmed);
69 if (!canBeTrimmed.isEmpty() && !(onlyIfNotDetermined && areVariablesDetermined(plan, retainedVars, canBeTrimmed, analyzer, false))) {
70 // TODO add smart ordering?
71 plan = planFactory.createSubPlan(new PProject(retainedVars), plan);
72 }
73 return plan;
74 }
75
76 /**
77 * @return true iff a set of given variables functionally determine all visible variables in the subplan according to the subplan's constraints
78 * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation;
79 * if false, user-provided soft dependencies are included as well, that are anticipated but not guaranteed by the storage mechanism;
80 * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance
81 * @since 1.5
82 */
83 public static boolean areAllVariablesDetermined(SubPlan plan, Collection<PVariable> determining, QueryAnalyzer analyzer, boolean strict) {
84 return areVariablesDetermined(plan, determining, plan.getVisibleVariables(), analyzer, strict);
85 }
86
87 /**
88 * @return true iff one set of given variables functionally determine the other set according to the subplan's constraints
89 * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation;
90 * if false, user-provided soft dependencies are included as well, that are anticipated but not guaranteed by the storage mechanism;
91 * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance
92 * @since 1.5
93 */
94 public static boolean areVariablesDetermined(SubPlan plan, Collection<PVariable> determining, Collection<PVariable> determined,
95 QueryAnalyzer analyzer, boolean strict) {
96 Map<Set<PVariable>, Set<PVariable>> dependencies = analyzer.getFunctionalDependencies(plan.getAllEnforcedConstraints(), strict);
97 final Set<PVariable> closure = FunctionalDependencyHelper.closureOf(determining, dependencies);
98 final boolean isDetermined = closure.containsAll(determined);
99 return isDetermined;
100 }
101
102 private static <T> Set<T> setMinus(Set<T> a, Set<T> b) {
103 Set<T> difference = new HashSet<T>(a);
104 difference.removeAll(b);
105 return difference;
106 }
107
108 /**
109 * Finds an arbitrary constraint that is not enforced at the given plan.
110 *
111 * @param pSystem
112 * @param plan
113 * @return a PConstraint that is not enforced, if any, or null if all are enforced
114 */
115 public static PConstraint getAnyUnenforcedConstraint(PBody pSystem,
116 SubPlan plan) {
117 Set<PConstraint> allEnforcedConstraints = plan.getAllEnforcedConstraints();
118 Set<PConstraint> constraints = pSystem.getConstraints();
119 for (PConstraint pConstraint : constraints) {
120 if (!allEnforcedConstraints.contains(pConstraint))
121 return pConstraint;
122 }
123 return null;
124 }
125
126 /**
127 * Skips the last few steps, if any, that are projections, so that a custom projection can be added instead.
128 * Useful for connecting body final plans into the production node.
129 *
130 * @since 2.1
131 */
132 public static SubPlan eliminateTrailingProjections(SubPlan plan) {
133 while (plan.getOperation() instanceof PProject)
134 plan = plan.getParentPlans().get(0);
135 return plan;
136 }
137
138 /**
139 * Verifies whether all constraints are enforced and exported parameters are present.
140 *
141 * @param pSystem
142 * @param plan
143 * @throws ViatraQueryRuntimeException
144 */
145 public static void finalCheck(final PBody pSystem, SubPlan plan, IQueryMetaContext context) {
146 PConstraint unenforcedConstraint = getAnyUnenforcedConstraint(pSystem, plan);
147 if (unenforcedConstraint != null) {
148 throw new QueryProcessingException(
149 "Pattern matcher construction terminated without successfully enforcing constraint {1}."
150 + " Could be caused if the value of some variables can not be deduced, e.g. by circularity of pattern constraints.",
151 new String[] { unenforcedConstraint.toString() }, "Could not enforce a pattern constraint", null);
152 }
153 for (ExportedParameter export : pSystem
154 .getConstraintsOfType(ExportedParameter.class)) {
155 if (!export.isReadyAt(plan, context)) {
156 throw new QueryProcessingException(
157 "Exported pattern parameter {1} could not be deduced during pattern matcher construction."
158 + " A pattern constraint is required to positively deduce its value.",
159 new String[] { export.getParameterName() }, "Could not calculate pattern parameter",
160 null);
161 }
162 }
163 }
164
165}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java
new file mode 100644
index 00000000..40835f52
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java
@@ -0,0 +1,143 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Adam Dudas, 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.matchers.planning.helpers;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.HashSet;
15import java.util.Map;
16import java.util.Map.Entry;
17import java.util.Set;
18
19import tools.refinery.viatra.runtime.matchers.util.Sets;
20
21/**
22 * Helper utility class for functional dependency analysis.
23 *
24 * Throughout this class attribute sets are represented as generic sets and functional dependencies as maps from
25 * attribute set (generic sets) to attribute set (generic sets)
26 *
27 * @author Adam Dudas
28 *
29 */
30public class FunctionalDependencyHelper {
31
32 private FunctionalDependencyHelper() {
33 // Hiding constructor for utility class
34 }
35
36 /**
37 * Get the closure of the specified attribute set relative to the specified functional dependencies.
38 *
39 * @param attributes
40 * The attributes to get the closure of.
41 * @param dependencies
42 * The functional dependencies of which the closure operation is relative to.
43 * @return The closure of the specified attribute set relative to the specified functional dependencies.
44 */
45 public static <A> Set<A> closureOf(Collection<A> attributes, Map<Set<A>, Set<A>> dependencies) {
46 Set<A> closureSet = new HashSet<A>();
47
48 for (Set<A> closureSet1 = new HashSet<A>(attributes); closureSet.addAll(closureSet1);) {
49 closureSet1 = new HashSet<A>();
50 for (Entry<Set<A>, Set<A>> dependency : dependencies.entrySet()) {
51 if (closureSet.containsAll(dependency.getKey()))
52 closureSet1.addAll(dependency.getValue());
53 }
54 }
55
56 return closureSet;
57 }
58
59 /**
60 * @return true if the dependency from the left set to the right set is trivial
61 * @since 1.5
62 */
63 public static <A> boolean isTrivial(Set<A> left, Set<A> right) {
64 return left.containsAll(right);
65 }
66
67 /***
68 * Returns the dependency set over attributes in {@link targetAttributes} that are implied by a given source dependency set.
69 * <p> Note: exponential in the size of the target attribute set.
70 * <p> Note: minimality of the returned dependency set is currently not guaranteed.
71 * @param originalDependencies all dependencies that are known to hold on a wider set of attributes
72 * @param targetAttributes the set of attributes we are interested in
73 * @since 1.5
74 */
75 public static <A> Map<Set<A>, Set<A>> projectDependencies(Map<Set<A>, Set<A>> originalDependencies, Set<A> targetAttributes) {
76 // only those attributes are considered as left-hand-side candidates that occur at least once in dependencies
77 Set<A> leftCandidates = new HashSet<A>();
78 for (Entry<Set<A>, Set<A>> dependency : originalDependencies.entrySet()) {
79 if (!isTrivial(dependency.getKey(), dependency.getValue())) // only if non-trivial
80 leftCandidates.addAll(Sets.intersection(dependency.getKey(), targetAttributes));
81 }
82
83 // Compute an initial list of nontrivial projected dependencies - it does not have to be minimal yet
84 Map<Set<A>, Set<A>> initialDependencies = new HashMap<Set<A>, Set<A>>();
85 for (Set<A> leftSet : Sets.powerSet(leftCandidates)) {
86 Set<A> rightSet = Sets.intersection(closureOf(leftSet, originalDependencies), targetAttributes);
87 if (!isTrivial(leftSet, rightSet)) {
88 initialDependencies.put(leftSet, rightSet);
89 }
90 }
91 // Don't forget to include constants!
92 Set<A> constants = Sets.intersection(closureOf(Collections.<A>emptySet(), originalDependencies), targetAttributes);
93 if (! constants.isEmpty()) {
94 initialDependencies.put(Collections.<A>emptySet(), constants);
95 }
96
97 // Omit those dependencies where the LHS has superfluous attributes
98 Map<Set<A>, Set<A>> solidDependencies = new HashMap<Set<A>, Set<A>>();
99 for (Entry<Set<A>, Set<A>> dependency : initialDependencies.entrySet()) {
100 Set<A> leftSet = dependency.getKey();
101 Set<A> rightSet = dependency.getValue();
102 boolean solid = true;
103 for (A skipped : leftSet) { // what if we skip one attribute from the left set?
104 Set<A> singleton = Collections.singleton(skipped);
105 Set<A> candidate = Sets.difference(leftSet, singleton);
106 Set<A> rightCandidate = initialDependencies.get(candidate);
107 if (rightCandidate != null) {
108 if (Sets.union(rightCandidate, singleton).containsAll(rightSet)) {
109 solid = false;
110 break;
111 }
112 }
113 }
114 if (solid) {
115 solidDependencies.put(leftSet, rightSet);
116 }
117 }
118
119 // TODO perform proper minimization,
120 // see e.g. page 45 in http://www.cs.ubc.ca/~hkhosrav/db/slides/03.design%20theory.pdf
121
122 return Collections.unmodifiableMap(solidDependencies);
123 }
124
125 /**
126 * Adds a given dependency to a mutable accumulator.
127 * @since 1.5
128 */
129 public static <A> void includeDependency(Map<Set<A>, Set<A>> accumulator, Set<A> left, Set<A> right) {
130 Set<A> accumulatorRights = accumulator.computeIfAbsent(left, l -> new HashSet<>());
131 accumulatorRights.addAll(right);
132 }
133
134 /**
135 * Adds all given dependencies to a mutable accumulator.
136 * @since 1.5
137 */
138 public static <A> void includeDependencies(Map<Set<A>, Set<A>> accumulator, Map<Set<A>, Set<A>> additionalDependencies) {
139 for (Entry<Set<A>, Set<A>> entry : additionalDependencies.entrySet()) {
140 includeDependency(accumulator, entry.getKey(), entry.getValue());
141 }
142 }
143}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java
new file mode 100644
index 00000000..b4f848a7
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java
@@ -0,0 +1,62 @@
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.matchers.planning.helpers;
10
11import java.util.Optional;
12import java.util.function.BiFunction;
13
14import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
15import tools.refinery.viatra.runtime.matchers.util.Accuracy;
16
17/**
18 * Helpers dealing with optionally present statistics information
19 *
20 * @author Gabor Bergmann
21 * @since 2.1
22 *
23 */
24public class StatisticsHelper {
25
26 private StatisticsHelper() {
27 // Hidden utility class constructor
28 }
29
30 public static Optional<Double> estimateAverageBucketSize(TupleMask groupMask, Accuracy requiredAccuracy,
31 BiFunction<TupleMask, Accuracy, Optional<Long>> estimateCardinality)
32 {
33 if (groupMask.isIdentity()) return Optional.of(1.0);
34
35 Accuracy numeratorAccuracy = requiredAccuracy;
36 Accuracy denominatorAccuracy = requiredAccuracy.reciprocal();
37 TupleMask identityMask = TupleMask.identity(groupMask.sourceWidth);
38
39 Optional<Long> totalCountEstimate = estimateCardinality.apply(identityMask, numeratorAccuracy);
40 Optional<Long> bucketCountEstimate = estimateCardinality.apply(groupMask, denominatorAccuracy);
41
42 return totalCountEstimate.flatMap(matchCount ->
43 bucketCountEstimate.map(bucketCount ->
44 bucketCount == 0L ? 0L : ((double) matchCount) / bucketCount
45 ));
46 }
47
48 public static Optional<Double> min(Optional<Double> a, Optional<Double> b) {
49 if (b.isPresent()) {
50 if (a.isPresent()) {
51 return Optional.of(Math.min(a.get(), b.get()));
52 } else return b;
53 } else return a;
54 }
55 public static Optional<Double> min(Optional<Double> a, double b) {
56 if (a.isPresent()) {
57 return Optional.of(Math.min(a.get(), b));
58 } else return Optional.of(b);
59 }
60
61
62}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java
new file mode 100644
index 00000000..926a591f
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java
@@ -0,0 +1,217 @@
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.matchers.planning.helpers;
11
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.HashSet;
15import java.util.LinkedList;
16import java.util.Map;
17import java.util.Map.Entry;
18import java.util.Queue;
19import java.util.Set;
20import java.util.stream.Collectors;
21
22import tools.refinery.viatra.runtime.matchers.context.IInputKey;
23import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
24import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint;
25import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
26import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
27import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement;
28
29/**
30 * @author Gabor Bergmann
31 * @author Tamas Szabo
32 */
33public class TypeHelper {
34
35 private TypeHelper() {
36 // Hiding constructor for utility class
37 }
38
39 /**
40 * Collects the type constraints for the specified collection of variables. The type constraints consist of the
41 * constraints directly enforced on the variable itself, plus all of those that the given variable is unified with
42 * through equalities.
43 *
44 * @param variables
45 * the variables in question
46 * @param constraints
47 * the constraints in the pattern body
48 * @param context
49 * the query meta context
50 * @return the mapping from variable to set of type constraints
51 * @since 1.6
52 */
53 public static Map<PVariable, Set<IInputKey>> inferUnaryTypesFor(Iterable<PVariable> variables,
54 Set<PConstraint> constraints, IQueryMetaContext context) {
55 Map<PVariable, Set<TypeJudgement>> typeMap = TypeHelper.inferUnaryTypes(constraints, context);
56 return inferUnaryTypesFor(variables, typeMap);
57 }
58
59 /**
60 * Collects the type constraints for the specified collection of variables. The type constraints consist of the
61 * constraints directly enforced on the variable itself, plus all of those that the given variable is unified with
62 * through equalities.
63 *
64 * The method accepts a type map which is the result of the basic type inference from the
65 * {@link TypeHelper.inferUnaryTypes} method. The purpose of this method is that the type map can be reused across
66 * several calls to this method.
67 *
68 * @param variables
69 * the variables in question
70 * @param typeMap
71 * the type map of inference results
72 * @return the mapping from variable to set of type constraints
73 * @since 1.6
74 */
75 public static Map<PVariable, Set<IInputKey>> inferUnaryTypesFor(Iterable<PVariable> variables,
76 Map<PVariable, Set<TypeJudgement>> typeMap) {
77 Map<PVariable, Set<IInputKey>> result = new HashMap<PVariable, Set<IInputKey>>();
78
79 for (PVariable original : variables) {
80 // it can happen that the variable was unified into an other one due to equalities
81 Set<IInputKey> keys = new HashSet<IInputKey>();
82 PVariable current = original;
83
84 while (current != null) {
85 Set<TypeJudgement> judgements = typeMap.get(current);
86 if (judgements != null) {
87 for (TypeJudgement judgement : judgements) {
88 keys.add(judgement.getInputKey());
89 }
90 }
91 current = current.getDirectUnifiedInto();
92 }
93
94 result.put(original, keys);
95 }
96
97 return result;
98 }
99
100 /**
101 * Infers unary type information for variables, based on the given constraints.
102 *
103 * Subsumptions are not taken into account.
104 *
105 * @param constraints
106 * the set of constraints to extract type info from
107 */
108 public static Map<PVariable, Set<TypeJudgement>> inferUnaryTypes(Set<PConstraint> constraints,
109 IQueryMetaContext context) {
110 Set<TypeJudgement> equivalentJudgements = getDirectJudgements(constraints, context);
111 Set<TypeJudgement> impliedJudgements = typeClosure(equivalentJudgements, context);
112
113 Map<PVariable, Set<TypeJudgement>> results = new HashMap<PVariable, Set<TypeJudgement>>();
114 for (TypeJudgement typeJudgement : impliedJudgements) {
115 final IInputKey inputKey = typeJudgement.getInputKey();
116 if (inputKey.getArity() == 1) {
117 PVariable variable = (PVariable) typeJudgement.getVariablesTuple().get(0);
118 Set<TypeJudgement> inferredTypes = results.computeIfAbsent(variable, v -> new HashSet<>());
119 inferredTypes.add(typeJudgement);
120 }
121 }
122 return results;
123 }
124
125 /**
126 * Gets direct judgements reported by constraints. No closure is applied yet.
127 */
128 public static Set<TypeJudgement> getDirectJudgements(Set<PConstraint> constraints, IQueryMetaContext context) {
129 Set<TypeJudgement> equivalentJudgements = new HashSet<TypeJudgement>();
130 for (PConstraint pConstraint : constraints) {
131 if (pConstraint instanceof ITypeInfoProviderConstraint) {
132 equivalentJudgements.addAll(((ITypeInfoProviderConstraint) pConstraint).getImpliedJudgements(context));
133 }
134 }
135 return equivalentJudgements;
136 }
137
138 /**
139 * Calculates the closure of a set of type judgements, with respect to supertyping.
140 *
141 * @return the set of all type judgements in typesToClose and all their direct and indirect supertypes
142 */
143 public static Set<TypeJudgement> typeClosure(Set<TypeJudgement> typesToClose, IQueryMetaContext context) {
144 return typeClosure(Collections.<TypeJudgement> emptySet(), typesToClose, context);
145 }
146
147 /**
148 * Calculates the closure of a set of type judgements (with respect to supertyping), where the closure has been
149 * calculated before for a given base set, but not for a separate delta set.
150 * <p>
151 * Precondition: the set (typesToClose MINUS delta) is already closed w.r.t. supertyping.
152 *
153 * @return the set of all type judgements in typesToClose and all their direct and indirect supertypes
154 * @since 1.6
155 */
156 public static Set<TypeJudgement> typeClosure(Set<TypeJudgement> preclosedBaseSet, Set<TypeJudgement> delta,
157 IQueryMetaContext context) {
158 Queue<TypeJudgement> queue = delta.stream().filter(input -> !preclosedBaseSet.contains(input)).collect(Collectors.toCollection(LinkedList::new));
159 if (queue.isEmpty())
160 return preclosedBaseSet;
161
162 Set<TypeJudgement> closure = new HashSet<TypeJudgement>(preclosedBaseSet);
163
164 Map<TypeJudgement, Set<TypeJudgement>> conditionalImplications = new HashMap<>();
165 for (TypeJudgement typeJudgement : closure) {
166 conditionalImplications.putAll(typeJudgement.getConditionalImpliedJudgements(context));
167 }
168
169 do {
170 TypeJudgement deltaType = queue.poll();
171 if (closure.add(deltaType)) {
172 // direct implications
173 queue.addAll(deltaType.getDirectlyImpliedJudgements(context));
174
175 // conditional implications, source key processed before, this is the condition key
176 final Set<TypeJudgement> implicationSet = conditionalImplications.get(deltaType);
177 if (implicationSet != null) {
178 queue.addAll(implicationSet);
179 }
180
181 // conditional implications, this is the source key
182 Map<TypeJudgement, Set<TypeJudgement>> deltaConditionalImplications = deltaType
183 .getConditionalImpliedJudgements(context);
184 for (Entry<TypeJudgement, Set<TypeJudgement>> entry : deltaConditionalImplications.entrySet()) {
185 if (closure.contains(entry.getKey())) {
186 // condition processed before
187 queue.addAll(entry.getValue());
188 } else {
189 // condition not processed yet
190 conditionalImplications.computeIfAbsent(entry.getKey(), key -> new HashSet<>())
191 .addAll(entry.getValue());
192 }
193 }
194 }
195 } while (!queue.isEmpty());
196
197 return closure;
198 }
199
200 /**
201 * Calculates a remainder set of types from a larger set, that are not subsumed by a given set of subsuming types.
202 *
203 * @param subsumableTypes
204 * a set of types from which some may be implied by the subsuming types
205 * @param subsumingTypes
206 * a set of types that may imply some of the subsuming types
207 * @return the collection of types in subsumableTypes that are NOT identical to or supertypes of any type in
208 * subsumingTypes.
209 */
210 public static Set<TypeJudgement> subsumeTypes(Set<TypeJudgement> subsumableTypes, Set<TypeJudgement> subsumingTypes,
211 IQueryMetaContext context) {
212 Set<TypeJudgement> closure = typeClosure(subsumingTypes, context);
213 Set<TypeJudgement> subsumed = new HashSet<TypeJudgement>(subsumableTypes);
214 subsumed.removeAll(closure);
215 return subsumed;
216 }
217}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java
new file mode 100644
index 00000000..2c285b54
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java
@@ -0,0 +1,94 @@
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.matchers.planning.operations;
10
11import java.util.Collections;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
15import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
16import tools.refinery.viatra.runtime.matchers.util.Preconditions;
17
18/**
19 * Represents a constraint application on a single parent SubPlan.
20 * <p> Either a "selection" filter operation according to a deferred PConstraint (or transform in case of eval/aggregate), or
21 * alternatively a shorthand for PJoin + a PEnumerate on the right input for an enumerable PConstraint.
22 *
23 * <p> <b>WARNING</b>: if there are coinciding variables in the variable tuple of the enumerable constraint,
24 * it is the responsibility of the compiler to check them for equality.
25 *
26 * @author Bergmann Gabor
27 *
28 */
29public class PApply extends POperation {
30
31 private PConstraint pConstraint;
32
33 public PApply(PConstraint pConstraint) {
34 super();
35 this.pConstraint = pConstraint;
36 }
37 public PConstraint getPConstraint() {
38 return pConstraint;
39 }
40
41 @Override
42 public String getShortName() {
43 return String.format("APPLY_%s", pConstraint.toString());
44 }
45
46 @Override
47 public Set<? extends PConstraint> getDeltaConstraints() {
48 return Collections.singleton(pConstraint);
49 }
50
51 @Override
52 public int numParentSubPlans() {
53 return 1;
54 }
55
56 @Override
57 public void checkConsistency(SubPlan subPlan) {
58 super.checkConsistency(subPlan);
59 for (SubPlan parentPlan : subPlan.getParentPlans())
60 Preconditions.checkArgument(!parentPlan.getAllEnforcedConstraints().contains(pConstraint),
61 "Double-checking constraint %s", pConstraint);
62 // TODO obtain context?
63 //if (pConstraint instanceof DeferredPConstraint)
64 // Preconditions.checkArgument(((DeferredPConstraint) pConstraint).isReadyAt(subPlan, context))
65 }
66
67 @Override
68 public int hashCode() {
69 final int prime = 31;
70 int result = 1;
71 result = prime
72 * result
73 + ((pConstraint == null) ? 0 : pConstraint
74 .hashCode());
75 return result;
76 }
77 @Override
78 public boolean equals(Object obj) {
79 if (this == obj)
80 return true;
81 if (obj == null)
82 return false;
83 if (!(obj instanceof PApply))
84 return false;
85 PApply other = (PApply) obj;
86 if (pConstraint == null) {
87 if (other.pConstraint != null)
88 return false;
89 } else if (!pConstraint.equals(other.pConstraint))
90 return false;
91 return true;
92 }
93
94}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java
new file mode 100644
index 00000000..a975d50c
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.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.matchers.planning.operations;
10
11import java.util.Collections;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint;
15import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
16
17/**
18 * Represents a base relation defined by the instance set of an enumerable PConstraint; there are no parent SubPlans.
19 *
20 * <p> <b>WARNING</b>: if there are coinciding variables in the variable tuple of the enumerable constraint,
21 * it is the responsibility of the compiler to check them for equality.
22 * @author Bergmann Gabor
23 *
24 */
25public class PEnumerate extends POperation {
26
27 EnumerablePConstraint enumerablePConstraint;
28
29 public PEnumerate(EnumerablePConstraint enumerablePConstraint) {
30 super();
31 this.enumerablePConstraint = enumerablePConstraint;
32 }
33 public EnumerablePConstraint getEnumerablePConstraint() {
34 return enumerablePConstraint;
35 }
36
37 @Override
38 public Set<? extends PConstraint> getDeltaConstraints() {
39 return Collections.singleton(enumerablePConstraint);
40 }
41 @Override
42 public int numParentSubPlans() {
43 return 0;
44 }
45 @Override
46 public String getShortName() {
47 return enumerablePConstraint.toString();
48 }
49 @Override
50 public int hashCode() {
51 final int prime = 31;
52 int result = 1;
53 result = prime
54 * result
55 + ((enumerablePConstraint == null) ? 0 : enumerablePConstraint
56 .hashCode());
57 return result;
58 }
59 @Override
60 public boolean equals(Object obj) {
61 if (this == obj)
62 return true;
63 if (obj == null)
64 return false;
65 if (!(obj instanceof PEnumerate))
66 return false;
67 PEnumerate other = (PEnumerate) obj;
68 if (enumerablePConstraint == null) {
69 if (other.enumerablePConstraint != null)
70 return false;
71 } else if (!enumerablePConstraint.equals(other.enumerablePConstraint))
72 return false;
73 return true;
74 }
75
76}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java
new file mode 100644
index 00000000..10e0a85a
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java
@@ -0,0 +1,64 @@
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.matchers.planning.operations;
10
11import java.util.Collections;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
15
16/**
17 * Represents a natural join of two parent SubPlans.
18 * @author Bergmann Gabor
19 *
20 */
21public class PJoin extends POperation {
22
23// // TODO leave here? is this a problem in equivalnece checking?
24// private Set<PVariable> onVariables;
25
26 public PJoin(/*Set<PVariable> onVariables*/) {
27 super();
28 //this.onVariables = new HashSet<PVariable>(onVariables);
29 }
30// public Set<PVariable> getOnVariables() {
31// return onVariables;
32// }
33
34 @Override
35 public Set<? extends PConstraint> getDeltaConstraints() {
36 return Collections.emptySet();
37 }
38 @Override
39 public int numParentSubPlans() {
40 return 2;
41 }
42
43 @Override
44 public String getShortName() {
45 return "JOIN"; //String.format("JOIN_{%s}", Joiner.on(",").join(onVariables));
46 }
47
48 @Override
49 public int hashCode() {
50 return getClass().hashCode();
51 }
52 @Override
53 public boolean equals(Object obj) {
54 if (this == obj)
55 return true;
56 if (obj == null)
57 return false;
58 if (!(obj instanceof PJoin))
59 return false;
60 return true;
61 }
62
63
64}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java
new file mode 100644
index 00000000..e71cf217
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java
@@ -0,0 +1,52 @@
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.matchers.planning.operations;
10
11import java.util.Set;
12
13import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
14import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
15import tools.refinery.viatra.runtime.matchers.util.Preconditions;
16
17/**
18 * Abstract superclass for representing a high-level query evaluation operation.
19 *
20 * <p> Subclasses correspond to various POperations modeled after relational algebra.
21 *
22 * @author Bergmann Gabor
23 *
24 */
25public abstract class POperation {
26
27 /**
28 * Newly enforced constraints
29 */
30 public abstract Set<? extends PConstraint> getDeltaConstraints();
31
32 public abstract String getShortName();
33
34 /**
35 * @return the number of SubPlans that must be specified as parents
36 */
37 public abstract int numParentSubPlans();
38
39 /**
40 * Checks whether this constraint can be properly applied at the given SubPlan.
41 */
42 public void checkConsistency(SubPlan subPlan) {
43 Preconditions.checkArgument(this == subPlan.getOperation(), "POperation misalignment");
44 Preconditions.checkArgument(subPlan.getParentPlans().size() == numParentSubPlans(), "Incorrect number of parent SubPlans");
45 }
46
47 @Override
48 public String toString() {
49 return getShortName();
50 }
51
52}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java
new file mode 100644
index 00000000..d0539b2c
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java
@@ -0,0 +1,109 @@
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.matchers.planning.operations;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.List;
14import java.util.Set;
15import java.util.stream.Collectors;
16
17import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
18import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
19import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
20import tools.refinery.viatra.runtime.matchers.util.Preconditions;
21
22/**
23 * Represents a projection of a single parent SubPlan onto a limited set of variables.
24 * <p> May optionally prescribe an ordering of variables (List, as opposed to Set).
25 *
26 * @author Bergmann Gabor
27 *
28 */
29public class PProject extends POperation {
30
31 private Collection<PVariable> toVariables;
32 private boolean ordered;
33
34
35 public PProject(Set<PVariable> toVariables) {
36 super();
37 this.toVariables = toVariables;
38 this.ordered = false;
39 }
40 public PProject(List<PVariable> toVariables) {
41 super();
42 this.toVariables = toVariables;
43 this.ordered = true;
44 }
45
46 public Collection<PVariable> getToVariables() {
47 return toVariables;
48 }
49 public boolean isOrdered() {
50 return ordered;
51 }
52
53 @Override
54 public Set<? extends PConstraint> getDeltaConstraints() {
55 return Collections.emptySet();
56 }
57 @Override
58 public int numParentSubPlans() {
59 return 1;
60 }
61 @Override
62 public void checkConsistency(SubPlan subPlan) {
63 super.checkConsistency(subPlan);
64 final SubPlan parentPlan = subPlan.getParentPlans().get(0);
65
66 Preconditions.checkArgument(parentPlan.getVisibleVariables().containsAll(toVariables),
67 () -> toVariables.stream()
68 .filter(input -> !parentPlan.getVisibleVariables().contains(input)).map(PVariable::getName)
69 .collect(Collectors.joining(",", "Variables missing from project: ", "")));
70 }
71
72 @Override
73 public String getShortName() {
74 return String.format("PROJECT%s_{%s}", ordered? "!" : "",
75 toVariables.stream().map(PVariable::getName).collect(Collectors.joining(",")));
76 }
77
78 @Override
79 public int hashCode() {
80 final int prime = 31;
81 int result = 1;
82 result = prime * result + (ordered ? 1231 : 1237);
83 result = prime * result
84 + ((toVariables == null) ? 0 : toVariables.hashCode());
85 return result;
86 }
87 @Override
88 public boolean equals(Object obj) {
89 if (this == obj)
90 return true;
91 if (obj == null)
92 return false;
93 if (!(obj instanceof PProject))
94 return false;
95 PProject other = (PProject) obj;
96 if (ordered != other.ordered)
97 return false;
98 if (toVariables == null) {
99 if (other.toVariables != null)
100 return false;
101 } else if (!toVariables.equals(other.toVariables))
102 return false;
103 return true;
104 }
105
106
107
108
109}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java
new file mode 100644
index 00000000..9e6ea10e
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java
@@ -0,0 +1,90 @@
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.matchers.planning.operations;
10
11import java.util.Arrays;
12import java.util.Collections;
13import java.util.HashSet;
14import java.util.Set;
15import java.util.stream.Collectors;
16
17import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
18import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
19
20/**
21 * No constraints, and no parent SubPlan, just a (possibly empty) set of a priori known (input) variables. Satisfied by a single tuple.
22 *
23 * <p> Can also be used without a priori variables,
24 * e.g. as a "virtual parent" in extreme cases,
25 * such as <code>pattern foo(Bar) = {Bar = eval (3*4)} </code>
26 *
27 * @author Bergmann Gabor
28 *
29 */
30public class PStart extends POperation {
31
32 private Set<PVariable> aPrioriVariables;
33
34
35 public PStart(Set<PVariable> aPrioriVariables) {
36 super();
37 this.aPrioriVariables = aPrioriVariables;
38 }
39 public PStart(PVariable... aPrioriVariables) {
40 this(new HashSet<PVariable>(Arrays.asList(aPrioriVariables)));
41 }
42 public Set<PVariable> getAPrioriVariables() {
43 return aPrioriVariables;
44 }
45
46 @Override
47 public String getShortName() {
48 return aPrioriVariables.stream().map(PVariable::getName).collect(Collectors.joining(",", "START_{", "}"));
49 }
50 @Override
51 public int numParentSubPlans() {
52 return 0;
53 }
54
55 @Override
56 public Set<? extends PConstraint> getDeltaConstraints() {
57 return Collections.emptySet();
58 }
59
60 @Override
61 public int hashCode() {
62 final int prime = 31;
63 int result = 1;
64 result = prime
65 * result
66 + ((aPrioriVariables == null) ? 0 : aPrioriVariables.hashCode());
67 return result;
68 }
69
70 @Override
71 public boolean equals(Object obj) {
72 if (this == obj)
73 return true;
74 if (obj == null)
75 return false;
76 if (!(obj instanceof PStart))
77 return false;
78 PStart other = (PStart) obj;
79 if (aPrioriVariables == null) {
80 if (other.aPrioriVariables != null)
81 return false;
82 } else if (!aPrioriVariables.equals(other.aPrioriVariables))
83 return false;
84 return true;
85 }
86
87
88
89
90}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java
new file mode 100644
index 00000000..eda4aa25
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java
@@ -0,0 +1,108 @@
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.matchers.psystem;
11
12import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
13
14import java.util.Collections;
15import java.util.HashSet;
16import java.util.Map;
17import java.util.Set;
18import java.util.concurrent.atomic.AtomicInteger;
19
20/**
21 * @author Gabor Bergmann
22 *
23 */
24public abstract class BasePConstraint implements PConstraint {
25
26
27 protected PBody pBody;
28 private final Set<PVariable> affectedVariables;
29
30
31 private final int sequentialID = nextID.getAndIncrement();
32
33 // Use a static atomic integer to avoid race conditions when creating new constraints.
34 private static AtomicInteger nextID = new AtomicInteger(0);
35
36 public BasePConstraint(PBody pBody, Set<PVariable> affectedVariables) {
37 super();
38 this.pBody = pBody;
39 this.affectedVariables = new HashSet<PVariable>(affectedVariables);
40
41 for (PVariable pVariable : affectedVariables) {
42 pVariable.refer(this);
43 }
44 pBody.registerConstraint(this);
45 }
46
47 @Override
48 public String toString() {
49 return "PC[" + getClass().getSimpleName() + ":" + toStringRest() + "]";
50 }
51
52 protected abstract String toStringRest();
53
54 @Override
55 public Set<PVariable> getAffectedVariables() {
56 return affectedVariables;
57 }
58
59 @Override
60 public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) {
61 return Collections.emptyMap();
62 }
63
64 @Override
65 public void replaceVariable(PVariable obsolete, PVariable replacement) {
66 pBody.checkMutability();
67 if (affectedVariables.remove(obsolete)) {
68 affectedVariables.add(replacement);
69 obsolete.unrefer(this);
70 replacement.refer(this);
71 doReplaceVariable(obsolete, replacement);
72 }
73 }
74
75 protected abstract void doReplaceVariable(PVariable obsolete, PVariable replacement);
76
77 @Override
78 public void delete() {
79 pBody.checkMutability();
80 for (PVariable pVariable : affectedVariables) {
81 pVariable.unrefer(this);
82 }
83 pBody.unregisterConstraint(this);
84 }
85
86 @Override
87 public void checkSanity() {
88 }
89
90 /**
91 * For backwards compatibility. Equivalent to {@link #getBody()}
92 */
93 public PBody getPSystem() {
94 return pBody;
95 }
96 /**
97 * @since 2.1
98 */
99 @Override
100 public PBody getBody() {
101 return pBody;
102 }
103
104 @Override
105 public int getMonotonousID() {
106 return sequentialID;
107 }
108}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java
new file mode 100644
index 00000000..d2bf088c
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java
@@ -0,0 +1,31 @@
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.matchers.psystem;
11
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
15import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
16
17/**
18 * Any constraint that can only be checked on certain SubPlans (e.g. those plans that already contain some variables).
19 *
20 * @author Gabor Bergmann
21 *
22 */
23public abstract class DeferredPConstraint extends BasePConstraint {
24
25 public DeferredPConstraint(PBody pBody, Set<PVariable> affectedVariables) {
26 super(pBody, affectedVariables);
27 }
28
29 public abstract boolean isReadyAt(SubPlan plan, IQueryMetaContext context);
30
31}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java
new file mode 100644
index 00000000..9129aa47
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java
@@ -0,0 +1,59 @@
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.matchers.psystem;
11
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15
16/**
17 * A constraint for which all satisfying tuples of variable values can be enumerated at any point during run-time.
18 *
19 * @author Gabor Bergmann
20 *
21 */
22public abstract class EnumerablePConstraint extends BasePConstraint {
23 protected Tuple variablesTuple;
24
25 protected EnumerablePConstraint(PBody pBody, Tuple variablesTuple) {
26 super(pBody, variablesTuple.<PVariable> getDistinctElements());
27 this.variablesTuple = variablesTuple;
28 }
29
30 @Override
31 public void doReplaceVariable(PVariable obsolete, PVariable replacement) {
32 variablesTuple = variablesTuple.replaceAll(obsolete, replacement);
33 }
34
35 @Override
36 protected String toStringRest() {
37 String stringRestRest = toStringRestRest();
38 String tupleString = "@" + variablesTuple.toString();
39 return stringRestRest == null ? tupleString : ":" + stringRestRest + tupleString;
40 }
41
42 protected String toStringRestRest() {
43 return null;
44 }
45
46 public Tuple getVariablesTuple() {
47 return variablesTuple;
48 }
49
50 @Override
51 public Set<PVariable> getDeducedVariables() {
52 return getAffectedVariables();
53 }
54
55 public PVariable getVariableInTuple(int index) {
56 return (PVariable) this.variablesTuple.get(index);
57 }
58
59}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java
new file mode 100644
index 00000000..686999f7
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java
@@ -0,0 +1,42 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.matchers.psystem;
10
11/**
12 * An expression evaluator is used to execute arbitrary Java code during pattern matching. In order to include the
13 * evaluation in the planning seemlessly it is expected from the evaluator implementors to report all used PVariables by
14 * name.
15 *
16 * @author Zoltan Ujhelyi
17 *
18 */
19public interface IExpressionEvaluator {
20
21 /**
22 * A textual description of the expression. Used only for debug purposes, but must not be null.
23 */
24 String getShortDescription();
25
26 /**
27 * All input parameter names should be reported correctly.
28 */
29 Iterable<String> getInputParameterNames();
30
31 /**
32 * The expression evaluator code
33 *
34 * @param provider
35 * the value provider is an engine-specific way of reading internal variable tuples to evaluate the
36 * expression with
37 * @return the result of the expression: in case of predicate evaluation the return value must be true or false;
38 * otherwise the result can be an arbitrary object. No null values should be returned.
39 * @throws Exception
40 */
41 Object evaluateExpression(IValueProvider provider) throws Exception;
42}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java
new file mode 100644
index 00000000..8f647c64
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java
@@ -0,0 +1,26 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2022, Tamas Szabo, GitHub
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.matchers.psystem;
10
11import java.util.Collection;
12
13import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
14
15/**
16 * A {@link PConstraint} that implements this interface refers to a list of PQueries.
17 *
18 * @author Tamas Szabo
19 * @since 2.8
20 *
21 */
22public interface IMultiQueryReference {
23
24 Collection<PQuery> getReferredQueries();
25
26}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java
new file mode 100644
index 00000000..9ee05b39
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java
@@ -0,0 +1,33 @@
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.matchers.psystem;
10
11import java.util.Collections;
12import java.util.List;
13
14import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
15
16/**
17 * A {@link PConstraint} that implements this interface refers to a {@link PQuery}.
18 *
19 * @author Zoltan Ujhelyi
20 *
21 */
22public interface IQueryReference extends IMultiQueryReference {
23
24 PQuery getReferredQuery();
25
26 /**
27 * @since 2.8
28 */
29 @Override
30 default List<PQuery> getReferredQueries() {
31 return Collections.singletonList(getReferredQuery());
32 }
33}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java
new file mode 100644
index 00000000..e4c396d8
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java
@@ -0,0 +1,47 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2022, Tamas Szabo, GitHub
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.matchers.psystem;
10
11import java.util.List;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15
16/**
17 * Implementations of this interface take an arbitrary number of input relations with their contents and compute the
18 * tuples of a single output relation.
19 *
20 * @author Tamas Szabo
21 * @since 2.8
22 *
23 */
24public interface IRelationEvaluator {
25
26 /**
27 * A textual description of the evaluator. Used only for debug purposes, but must not be null.
28 */
29 String getShortDescription();
30
31 /**
32 * The relation evaluator code. For performance reasons, it is expected that the returned set is a mutable
33 * collection, and the caller must be allowed to actually perform mutations!
34 */
35 Set<Tuple> evaluateRelation(List<Set<Tuple>> inputs) throws Exception;
36
37 /**
38 * For each input relation that this evaluator requires, this method returns the expected arities of the relations in order.
39 */
40 List<Integer> getInputArities();
41
42 /**
43 * Returns the arity of the output relation that this evaluator computes.
44 */
45 int getOutputArity();
46
47}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java
new file mode 100644
index 00000000..b72035a8
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java
@@ -0,0 +1,65 @@
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.matchers.psystem;
10
11import java.util.HashMap;
12import java.util.HashSet;
13import java.util.Map;
14import java.util.Map.Entry;
15
16import tools.refinery.viatra.runtime.matchers.context.IInputKey;
17import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19
20import java.util.Set;
21
22/**
23 * Common superinterface of enumerable and deferred type constraints.
24 * @author Bergmann Gabor
25 *
26 */
27public interface ITypeConstraint extends ITypeInfoProviderConstraint {
28
29 public abstract TypeJudgement getEquivalentJudgement();
30
31 /**
32 * Static internal utility class for implementations of {@link ITypeConstraint}s.
33 * @author Bergmann Gabor
34 */
35 public static class TypeConstraintUtil {
36
37 private TypeConstraintUtil() {
38 // Hiding constructor for utility class
39 }
40
41 public static Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context, IInputKey inputKey, Tuple variablesTuple) {
42 final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>();
43
44 Set<Entry<Set<Integer>, Set<Integer>>> dependencies = context.getFunctionalDependencies(inputKey).entrySet();
45 for (Entry<Set<Integer>, Set<Integer>> dependency : dependencies) {
46 result.put(
47 transcribeVariables(dependency.getKey(), variablesTuple),
48 transcribeVariables(dependency.getValue(), variablesTuple)
49 );
50 }
51
52 return result;
53 }
54
55 private static Set<PVariable> transcribeVariables(Set<Integer> indices, Tuple variablesTuple) {
56 Set<PVariable> result = new HashSet<PVariable>();
57 for (Integer index : indices) {
58 result.add((PVariable) variablesTuple.get(index));
59 }
60 return result;
61 }
62
63 }
64
65}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java
new file mode 100644
index 00000000..ff127d38
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java
@@ -0,0 +1,28 @@
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.matchers.psystem;
11
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
15
16/**
17 * @author Gabor Bergmann
18 *
19 */
20public interface ITypeInfoProviderConstraint extends PConstraint {
21
22 /**
23 * Returns type information implied by this constraint.
24 *
25 */
26 public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context);
27
28}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java
new file mode 100644
index 00000000..d959adc4
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java
@@ -0,0 +1,27 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.matchers.psystem;
10
11/**
12 * Helper interface to get values from a tuple of variables. All pattern matching engines are expected to implement this
13 * to handle their internal structures.
14 *
15 * @author Zoltan Ujhelyi
16 *
17 */
18public interface IValueProvider {
19
20 /**
21 * Returns the value of the selected variable
22 * @param variableName
23 * @return the value of the variable; never null
24 * @throws IllegalArgumentException if the variable is not defined
25 */
26 Object getValue(String variableName);
27}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java
new file mode 100644
index 00000000..a82d12ec
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java
@@ -0,0 +1,56 @@
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.matchers.psystem;
10
11import java.util.Set;
12
13import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
14import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation;
15import tools.refinery.viatra.runtime.matchers.psystem.queries.PProblem;
16import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
17
18/**
19 * Adds extra methods to the PQuery interface to initialize its contents.
20 *
21 * @author Zoltan Ujhelyi
22 *
23 */
24public interface InitializablePQuery extends PQuery {
25
26 /**
27 * Sets the query status. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED uninitialized}.
28 *
29 * @param status the new status
30 */
31 void setStatus(PQueryStatus status);
32
33 /**
34 * Adds a detected error. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED uninitialized}.
35 *
36 * @param problem the new problem
37 */
38 void addError(PProblem problem);
39
40 /**
41 * Sets up the bodies of the pattern. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED
42 * uninitialized}.
43 *
44 * @param bodies
45 * @throws ViatraQueryRuntimeException
46 */
47 void initializeBodies(Set<PBody> bodies);
48
49 /**
50 * Adds an annotation to the specification. Only applicable if the pattern is still
51 * {@link PQueryStatus#UNINITIALIZED uninitialized}.
52 *
53 * @param annotation
54 */
55 void addAnnotation(PAnnotation annotation);
56}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java
new file mode 100644
index 00000000..91eea817
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java
@@ -0,0 +1,39 @@
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.matchers.psystem;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13
14/**
15 * @author Gabor Bergmann
16 *
17 */
18public abstract class KeyedEnumerablePConstraint<KeyType> extends EnumerablePConstraint {
19
20 protected KeyType supplierKey;
21
22 public KeyedEnumerablePConstraint(PBody pBody, Tuple variablesTuple,
23 KeyType supplierKey) {
24 super(pBody, variablesTuple);
25 this.supplierKey = supplierKey;
26 }
27
28 @Override
29 protected String toStringRestRest() {
30 return supplierKey == null ? "$any(null)" : keyToString();
31 }
32
33 protected abstract String keyToString();
34
35 public KeyType getSupplierKey() {
36 return supplierKey;
37 }
38
39}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java
new file mode 100644
index 00000000..c38dc23a
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java
@@ -0,0 +1,289 @@
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.matchers.psystem;
11
12import java.util.ArrayList;
13import java.util.HashMap;
14import java.util.HashSet;
15import java.util.LinkedHashSet;
16import java.util.List;
17import java.util.Map;
18import java.util.Set;
19import java.util.WeakHashMap;
20import java.util.stream.Collectors;
21
22import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
23import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper;
24import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
25import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
26import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction;
27import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
28import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus;
29import tools.refinery.viatra.runtime.matchers.util.Preconditions;
30
31/**
32 * A set of constraints representing a pattern body
33 *
34 * @author Gabor Bergmann
35 *
36 */
37public class PBody implements PTraceable {
38
39 public static final String VIRTUAL_VARIABLE_PREFIX = ".virtual";
40 private static final String VIRTUAL_VARIABLE_PATTERN = VIRTUAL_VARIABLE_PREFIX + "{%d}";
41
42 private PQuery query;
43
44 /**
45 * If null, then parent query status is reused
46 */
47 private PQueryStatus status = PQueryStatus.UNINITIALIZED;
48
49 private Set<PVariable> allVariables;
50 private Set<PVariable> uniqueVariables;
51 private List<ExportedParameter> symbolicParameters;
52 private Map<Object, PVariable> variablesByName;
53 private Set<PConstraint> constraints;
54 private int nextVirtualNodeID;
55 private PDisjunction containerDisjunction;
56
57 public PBody(PQuery query) {
58 super();
59 this.query = query;
60 allVariables = new LinkedHashSet<>();
61 uniqueVariables = new LinkedHashSet<>();
62 variablesByName = new HashMap<>();
63 constraints = new LinkedHashSet<>();
64 }
65
66 /**
67 * @return whether the submission of the new variable was successful
68 */
69 private boolean addVariable(PVariable var) {
70 checkMutability();
71 Object name = var.getName();
72 if (!variablesByName.containsKey(name)) {
73 allVariables.add(var);
74 if (var.isUnique())
75 uniqueVariables.add(var);
76 variablesByName.put(name, var);
77 return true;
78 } else {
79 return false;
80 }
81 }
82
83 /**
84 * Use this method to add a newly created constraint to the pSystem.
85 *
86 * @return whether the submission of the new constraint was successful
87 */
88 boolean registerConstraint(PConstraint constraint) {
89 checkMutability();
90 return constraints.add(constraint);
91 }
92
93 /**
94 * Use this method to remove an obsolete constraint from the pSystem.
95 *
96 * @return whether the removal of the constraint was successful
97 */
98 boolean unregisterConstraint(PConstraint constraint) {
99 checkMutability();
100 return constraints.remove(constraint);
101 }
102
103 @SuppressWarnings("unchecked")
104 public <ConstraintType> Set<ConstraintType> getConstraintsOfType(Class<ConstraintType> constraintClass) {
105 Set<ConstraintType> result = new HashSet<ConstraintType>();
106 for (PConstraint pConstraint : constraints) {
107 if (constraintClass.isInstance(pConstraint))
108 result.add((ConstraintType) pConstraint);
109 }
110 return result;
111 }
112
113 public PVariable newVirtualVariable() {
114 checkMutability();
115 String name;
116 do {
117
118 name = String.format(VIRTUAL_VARIABLE_PATTERN, nextVirtualNodeID++);
119 } while (variablesByName.containsKey(name));
120 PVariable var = new PVariable(this, name, true);
121 addVariable(var);
122 return var;
123 }
124
125 public PVariable newVirtualVariable(String name) {
126 checkMutability();
127 Preconditions.checkArgument(!variablesByName.containsKey(name), "ID %s already used for a virtual variable", name);
128 PVariable var = new PVariable(this, name, true);
129 addVariable(var);
130 return var;
131 }
132
133 public PVariable newConstantVariable(Object value) {
134 checkMutability();
135 PVariable virtual = newVirtualVariable();
136 new ConstantValue(this, virtual, value);
137 return virtual;
138 }
139
140 public Set<PVariable> getAllVariables() {
141 return allVariables;
142 }
143
144 public Set<PVariable> getUniqueVariables() {
145 return uniqueVariables;
146 }
147
148 private PVariable getVariableByName(Object name) {
149 return variablesByName.get(name).getUnifiedIntoRoot();
150 }
151
152 /**
153 * Find a PVariable by name
154 *
155 * @param name
156 * @return the found variable
157 * @throws IllegalArgumentException
158 * if no PVariable is found with the selected name
159 */
160 public PVariable getVariableByNameChecked(Object name) {
161 if (!variablesByName.containsKey(name))
162 throw new IllegalArgumentException(String.format("Cannot find PVariable %s", name));
163 return getVariableByName(name);
164 }
165
166 /**
167 * Finds and returns a PVariable by name. If no PVariable exists with the name in the body, a new one is created. If
168 * the name of the variable starts with {@value #VIRTUAL_VARIABLE_PREFIX}, the created variable will be considered
169 * virtual.
170 *
171 * @param name
172 * @return a PVariable with the selected name; never null
173 */
174 public PVariable getOrCreateVariableByName(String name) {
175 checkMutability();
176 if (!variablesByName.containsKey(name)) {
177 addVariable(new PVariable(this, name, name.startsWith(VIRTUAL_VARIABLE_PREFIX)));
178 }
179 return getVariableByName(name);
180 }
181
182 public Set<PConstraint> getConstraints() {
183 return constraints;
184 }
185
186 public PQuery getPattern() {
187 return query;
188 }
189
190 void noLongerUnique(PVariable pVariable) {
191 assert (!pVariable.isUnique());
192 uniqueVariables.remove(pVariable);
193 }
194
195 /**
196 * Returns the symbolic parameters of the body. </p>
197 *
198 * <p>
199 * <strong>Warning</strong>: if two PVariables are unified, the returned list changes. If you want to have a stable
200 * version, consider using {@link #getSymbolicParameters()}.
201 *
202 * @return a non-null, but possibly empty list
203 */
204 public List<PVariable> getSymbolicParameterVariables() {
205 return getSymbolicParameters().stream().map(ExportedParameter::getParameterVariable)
206 .collect(Collectors.toList());
207 }
208
209 /**
210 * Returns the exported parameter constraints of the body.
211 *
212 * @return a non-null, but possibly empty list
213 */
214 public List<ExportedParameter> getSymbolicParameters() {
215 if (symbolicParameters == null)
216 symbolicParameters = new ArrayList<>();
217 return symbolicParameters;
218 }
219
220 /**
221 * Sets the exported parameter constraints of the body, if this instance is mutable.
222 * @param symbolicParameters the new value
223 */
224 public void setSymbolicParameters(List<ExportedParameter> symbolicParameters) {
225 checkMutability();
226 this.symbolicParameters = new ArrayList<>(symbolicParameters);
227 }
228
229 /**
230 * Sets a specific status for the body. If set, the parent PQuery status will not be checked; if set to null, its corresponding PQuery
231 * status is checked for mutability.
232 *
233 * @param status
234 * the status to set
235 */
236 public void setStatus(PQueryStatus status) {
237 this.status = status;
238 }
239
240 public boolean isMutable() {
241 if (status == null) {
242 return query.isMutable();
243 } else {
244 return status.equals(PQueryStatus.UNINITIALIZED);
245 }
246 }
247
248 void checkMutability() {
249 if (status == null) {
250 query.checkMutability();
251 } else {
252 Preconditions.checkState(status.equals(PQueryStatus.UNINITIALIZED), "Initialized queries are not mutable");
253 }
254 }
255
256 /**
257 * Returns the disjunction the body is contained with. This disjunction may either be the
258 * {@link PQuery#getDisjunctBodies() canonical disjunction of the corresponding query} or something equivalent.
259 *
260 * @return the container disjunction of the body. Can be null if body is not in a disjunction yet.
261 */
262 public PDisjunction getContainerDisjunction() {
263 return containerDisjunction;
264 }
265
266 /**
267 * @param containerDisjunction the containerDisjunction to set
268 */
269 public void setContainerDisjunction(PDisjunction containerDisjunction) {
270 Preconditions.checkArgument(query.equals(containerDisjunction.getQuery()), "Disjunction of pattern %s incompatible with body %s", containerDisjunction.getQuery().getFullyQualifiedName(), query.getFullyQualifiedName());
271 Preconditions.checkState(this.containerDisjunction == null, "Disjunction is already set.");
272 this.containerDisjunction = containerDisjunction;
273 }
274
275 /**
276 * All unary input keys directly prescribed by constraints, grouped by variable.
277 * <p> to supertype inference or subsumption applied at this point.
278 */
279 public Map<PVariable, Set<TypeJudgement>> getAllUnaryTypeRestrictions(IQueryMetaContext context) {
280 Map<PVariable, Set<TypeJudgement>> currentRestrictions = allUnaryTypeRestrictions.get(context);
281 if (currentRestrictions == null) {
282 currentRestrictions = TypeHelper.inferUnaryTypes(getConstraints(), context);
283 allUnaryTypeRestrictions.put(context, currentRestrictions);
284 }
285 return currentRestrictions;
286 }
287 private WeakHashMap<IQueryMetaContext, Map<PVariable, Set<TypeJudgement>>> allUnaryTypeRestrictions = new WeakHashMap<IQueryMetaContext, Map<PVariable,Set<TypeJudgement>>>();
288
289}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java
new file mode 100644
index 00000000..ae2c4632
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java
@@ -0,0 +1,70 @@
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.matchers.psystem;
11
12import java.util.Comparator;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
17import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
18
19/**
20 * @author Gabor Bergmann
21 *
22 */
23public interface PConstraint extends PTraceable {
24
25 /**
26 * @since 2.1
27 * @return the query body this constraint belongs to
28 */
29 public PBody getBody();
30
31 /**
32 * All variables affected by this constraint.
33 */
34 public Set<PVariable> getAffectedVariables();
35
36 /**
37 * The set of variables whose potential values can be enumerated (once all non-deduced variables have known values).
38 */
39 public Set<PVariable> getDeducedVariables();
40
41 /**
42 * A (preferably minimal) cover of known functional dependencies between variables.
43 * @noreference Use {@link QueryAnalyzer} instead to properly handle dependencies of pattern calls.
44 * @return non-trivial functional dependencies in the form of {variables} --> {variables}, where dependencies with the same lhs are unified.
45 */
46 public Map<Set<PVariable>,Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context);
47
48 public void replaceVariable(PVariable obsolete, PVariable replacement);
49
50 public void delete();
51
52 public void checkSanity();
53
54 /**
55 * Returns an integer ID that is guaranteed to increase strictly monotonously for constraints within a pBody.
56 */
57 public abstract int getMonotonousID();
58
59
60 /**
61 * A comparator that orders constraints by their {@link #getMonotonousID() monotonous identifiers}. Should only used
62 * for tiebreaking in other comparators.
63 *
64 * @since 2.0
65 */
66 public static final Comparator<PConstraint> COMPARE_BY_MONOTONOUS_ID = (arg0, arg1) -> arg0.getMonotonousID() - arg1.getMonotonousID();
67
68
69
70}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java
new file mode 100644
index 00000000..f0241a9c
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java
@@ -0,0 +1,16 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Dénes Harmath, 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.matchers.psystem;
10
11/**
12 * Marker interface for PSystem elements that can be traced.
13 * @since 1.6
14 */
15public interface PTraceable {
16}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java
new file mode 100644
index 00000000..b6ea4861
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java
@@ -0,0 +1,203 @@
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.matchers.psystem;
11
12import java.util.HashSet;
13import java.util.Set;
14
15/**
16 * @author Gabor Bergmann
17 *
18 */
19public class PVariable {
20 private PBody pBody;
21 /**
22 * The name of the pattern variable. This is the unique key of the pattern node.
23 */
24 private String name;
25 /**
26 * virtual pVariables are nodes that do not correspond to actual pattern variables; they represent constants or Term
27 * substitutes
28 */
29 private boolean virtual;
30
31 /**
32 * Set of constraints that mention this variable
33 */
34 private Set<PConstraint> referringConstraints;
35
36 /**
37 * Determines whether there are any constraints that can deduce this variable
38 */
39 private Boolean deducable;
40
41 /**
42 * Another PVariable this variable has been unified into. Please use the other variable instead of this. Null iff
43 * this is still a first-class variable.
44 */
45 private PVariable unifiedInto;
46
47 PVariable(PBody pBody, String name) {
48 this(pBody, name, false);
49 }
50
51 PVariable(PBody pBody, String name, boolean virtual) {
52 super();
53 this.pBody = pBody;
54 this.name = name;
55 this.virtual = virtual;
56 // this.exportedParameter = false;
57 this.referringConstraints = new HashSet<PConstraint>();
58 this.unifiedInto = null;
59 this.deducable = false;
60 }
61
62 /**
63 * Replaces this variable with a given other, resulting in their unification. This variable will no longer be
64 * unique.
65 *
66 * @param replacement
67 */
68 public void unifyInto(PVariable replacement) {
69 pBody.checkMutability();
70 replacementCheck();
71 replacement = replacement.getUnifiedIntoRoot();
72
73 if (this.equals(replacement))
74 return;
75
76 if (!this.isVirtual() && replacement.isVirtual()) {
77 replacement.unifyInto(this);
78 } else {
79 // replacement.referringConstraints.addAll(this.referringConstraints);
80 // replacement.exportedParameter |= this.exportedParameter;
81 replacement.virtual &= this.virtual;
82 if (replacement.deducable != null && this.deducable != null)
83 replacement.deducable |= this.deducable;
84 else
85 replacement.deducable = null;
86 Set<PConstraint> snapshotConstraints = // avoid ConcurrentModificationX
87 new HashSet<PConstraint>(this.referringConstraints);
88 for (PConstraint constraint : snapshotConstraints) {
89 constraint.replaceVariable(this, replacement);
90 }
91 // replacementCheck() will fail from this point
92 this.unifiedInto = replacement;
93 pBody.noLongerUnique(this);
94 }
95 }
96
97 /**
98 * Determines whether there are any constraints that can deduce this variable
99 */
100 public boolean isDeducable() {
101 replacementCheck();
102 if (deducable == null) {
103 deducable = false;
104 for (PConstraint pConstraint : getReferringConstraints()) {
105 if (pConstraint.getDeducedVariables().contains(this)) {
106 deducable = true;
107 break;
108 }
109 }
110 }
111 return deducable;
112 }
113
114 /**
115 * Register that this variable is referred by the given constraint.
116 *
117 * @param constraint
118 */
119 public void refer(PConstraint constraint) {
120 pBody.checkMutability();
121 replacementCheck();
122 deducable = null;
123 referringConstraints.add(constraint);
124 }
125
126 /**
127 * Register that this variable is no longer referred by the given constraint.
128 *
129 * @param constraint
130 */
131 public void unrefer(PConstraint constraint) {
132 pBody.checkMutability();
133 replacementCheck();
134 deducable = null;
135 referringConstraints.remove(constraint);
136 }
137
138 /**
139 * @return the name of the pattern variable. This is the unique key of the pattern node.
140 */
141 public String getName() {
142 replacementCheck();
143 return name;
144 }
145
146 /**
147 * @return the virtual
148 */
149 public boolean isVirtual() {
150 replacementCheck();
151 return virtual;
152 }
153
154 /**
155 * @return the referringConstraints
156 */
157 public Set<PConstraint> getReferringConstraints() {
158 replacementCheck();
159 return referringConstraints;
160 }
161
162 @SuppressWarnings("unchecked")
163 public <ConstraintType> Set<ConstraintType> getReferringConstraintsOfType(Class<ConstraintType> constraintClass) {
164 replacementCheck();
165 Set<ConstraintType> result = new HashSet<ConstraintType>();
166 for (PConstraint pConstraint : referringConstraints) {
167 if (constraintClass.isInstance(pConstraint))
168 result.add((ConstraintType) pConstraint);
169 }
170 return result;
171 }
172
173 @Override
174 public String toString() {
175 // replacementCheck();
176 return name;// + ":PatternNode";
177 }
178
179 public PVariable getDirectUnifiedInto() {
180 return unifiedInto;
181 }
182
183 public PVariable getUnifiedIntoRoot() {
184 PVariable nextUnified = unifiedInto;
185 PVariable oldUnifiedInto = this;
186 while (nextUnified != null) {
187 oldUnifiedInto = nextUnified;
188 nextUnified = oldUnifiedInto.getDirectUnifiedInto();
189 }
190 return oldUnifiedInto; // unifiedInto;
191 }
192
193 public boolean isUnique() {
194 return unifiedInto == null;
195 }
196
197 private void replacementCheck() {
198 if (unifiedInto != null)
199 throw new IllegalStateException("Illegal usage of variable " + name + " which has been replaced with "
200 + unifiedInto.name);
201 }
202
203}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java
new file mode 100644
index 00000000..4447b225
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java
@@ -0,0 +1,153 @@
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.matchers.psystem;
10
11import java.util.Collection;
12import java.util.HashSet;
13import java.util.List;
14import java.util.Map;
15import java.util.stream.Collectors;
16import java.util.Set;
17
18import tools.refinery.viatra.runtime.matchers.context.IInputKey;
19import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
20import tools.refinery.viatra.runtime.matchers.context.InputKeyImplication;
21import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint;
22import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
23import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
24import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
25
26/**
27 * A judgement that means that the given tuple of variables will represent a tuple of values that is a member of the extensional relation identified by the given input key.
28 * @author Bergmann Gabor
29 *
30 */
31public class TypeJudgement {
32
33 private IInputKey inputKey;
34 private Tuple variablesTuple;
35 /**
36 * @param inputKey
37 * @param variablesTuple
38 */
39 public TypeJudgement(IInputKey inputKey, Tuple variablesTuple) {
40 super();
41 this.inputKey = inputKey;
42 this.variablesTuple = variablesTuple;
43 }
44 public IInputKey getInputKey() {
45 return inputKey;
46 }
47 public Tuple getVariablesTuple() {
48 return variablesTuple;
49 }
50 @Override
51 public int hashCode() {
52 final int prime = 31;
53 int result = 1;
54 result = prime * result
55 + ((inputKey == null) ? 0 : inputKey.hashCode());
56 result = prime * result
57 + ((variablesTuple == null) ? 0 : variablesTuple.hashCode());
58 return result;
59 }
60 @Override
61 public boolean equals(Object obj) {
62 if (this == obj)
63 return true;
64 if (obj == null)
65 return false;
66 if (!(obj instanceof TypeJudgement))
67 return false;
68 TypeJudgement other = (TypeJudgement) obj;
69 if (inputKey == null) {
70 if (other.inputKey != null)
71 return false;
72 } else if (!inputKey.equals(other.inputKey))
73 return false;
74 if (variablesTuple == null) {
75 if (other.variablesTuple != null)
76 return false;
77 } else if (!variablesTuple.equals(other.variablesTuple))
78 return false;
79 return true;
80 }
81
82 public Set<TypeJudgement> getDirectlyImpliedJudgements(IQueryMetaContext context) {
83 Set<TypeJudgement> results = new HashSet<TypeJudgement>();
84 results.add(this);
85
86 Collection<InputKeyImplication> implications = context.getImplications(this.inputKey);
87 for (InputKeyImplication inputKeyImplication : implications) {
88 results.add(
89 transcribeImplication(inputKeyImplication)
90 );
91 }
92
93 return results;
94 }
95
96 /**
97 * @since 1.6
98 */
99 public Set<TypeJudgement> getWeakenedAlternativeJudgements(IQueryMetaContext context) {
100 Set<TypeJudgement> results = new HashSet<TypeJudgement>();
101
102 Collection<InputKeyImplication> implications = context.getWeakenedAlternatives(this.inputKey);
103 for (InputKeyImplication inputKeyImplication : implications) {
104 results.add(
105 transcribeImplication(inputKeyImplication)
106 );
107 }
108
109 return results;
110 }
111
112 /**
113 * @since 2.0
114 */
115 public Map<TypeJudgement, Set<TypeJudgement>> getConditionalImpliedJudgements(IQueryMetaContext context) {
116 return context.getConditionalImplications(this.inputKey).entrySet().stream().collect(Collectors.toMap(
117 entry -> transcribeImplication(entry.getKey()),
118 entry -> entry.getValue().stream().map(this::transcribeImplication).collect(Collectors.toSet())));
119 }
120
121
122
123 private TypeJudgement transcribeImplication(InputKeyImplication inputKeyImplication) {
124 return new TypeJudgement(
125 inputKeyImplication.getImpliedKey(),
126 transcribeVariablesToTuple(inputKeyImplication.getImpliedIndices())
127 );
128 }
129 private Tuple transcribeVariablesToTuple(List<Integer> indices) {
130 Object[] elements = new Object[indices.size()];
131 for (int i = 0; i < indices.size(); ++i)
132 elements[i] = variablesTuple.get(indices.get(i));
133 return Tuples.flatTupleOf(elements);
134 }
135
136 @Override
137 public String toString() {
138 return "TypeJudgement:" + inputKey.getPrettyPrintableName() + "@" + variablesTuple.toString();
139 }
140
141 /**
142 * Creates this judgement as a direct type constraint in the given PBody under construction.
143 * <p> pre: the variables tuple must be formed of variables of that PBody.
144 * @since 1.6
145 */
146 public PConstraint createConstraintFor(PBody pBody) {
147 if (inputKey.isEnumerable()) {
148 return new TypeConstraint(pBody, variablesTuple, inputKey);
149 } else {
150 return new TypeFilterConstraint(pBody, variablesTuple, inputKey);
151 }
152 }
153}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java
new file mode 100644
index 00000000..8ea6bb93
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.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.matchers.psystem;
11
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
15import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
16
17/**
18 * A kind of deferred constraint that can only be checked when a set of deferring variables are all present in a plan.
19 *
20 * @author Gabor Bergmann
21 *
22 */
23public abstract class VariableDeferredPConstraint extends DeferredPConstraint {
24
25 public VariableDeferredPConstraint(PBody pBody,
26 Set<PVariable> affectedVariables) {
27 super(pBody, affectedVariables);
28 }
29
30 public abstract Set<PVariable> getDeferringVariables();
31
32 /**
33 * Refine further if needed
34 */
35 @Override
36 public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) {
37 return plan.getVisibleVariables().containsAll(getDeferringVariables());
38 }
39
40}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java
new file mode 100644
index 00000000..63a37bbe
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java
@@ -0,0 +1,31 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.matchers.psystem.aggregations;
10
11/**
12 *
13 * An aggregation operator that does not store interim results beyond the final aggregated value.
14 * @author Gabor Bergmann
15 * @since 1.4
16 */
17public abstract class AbstractMemorylessAggregationOperator<Domain, AggregateResult>
18 implements IMultisetAggregationOperator<Domain, AggregateResult, AggregateResult>
19{
20
21 @Override
22 public AggregateResult getAggregate(AggregateResult result) {
23 return result;
24 }
25
26 @Override
27 public AggregateResult clone(AggregateResult original) {
28 return original;
29 }
30
31}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java
new file mode 100644
index 00000000..4cc40a2b
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java
@@ -0,0 +1,49 @@
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.matchers.psystem.aggregations;
10
11import java.lang.annotation.ElementType;
12import java.lang.annotation.Inherited;
13import java.lang.annotation.Retention;
14import java.lang.annotation.RetentionPolicy;
15import java.lang.annotation.Target;
16
17import tools.refinery.viatra.runtime.matchers.aggregators.count;
18
19/**
20 * The aggregator type annotation describes the type constraints for the selected aggregator. In version 1.4, two kinds of
21 * aggregators are supported:
22 *
23 * <ol>
24 * <li>An aggregator that does not consider any parameter value from the call ({@link count}), just calculates the
25 * number of matches. This is represented by a single {@link Void} and a single corresponding return type.</li>
26 * <li>An aggregator that considers a single parameter from the call, and executes some aggregate operations over it.
27 * Such an aggregate operation can be defined over multiple types, where each possible parameter type has a corresponding return type declared.</li>
28 * </ol>
29 *
30 * <strong>Important!</strong> The parameterTypes and returnTypes arrays must have
31 * <ul>
32 * <li>The same number of classes defined each.</li>
33 * <li>Items are corresponded by index.</li>
34 * <li>Items should represent data types</li>
35 * </ul>
36 *
37 * @author Zoltan Ujhelyi
38 * @since 1.4
39 *
40 */
41@Target({ ElementType.TYPE })
42@Retention(RetentionPolicy.RUNTIME)
43@Inherited
44public @interface AggregatorType {
45
46 Class<?>[] parameterTypes();
47
48 Class<?>[] returnTypes();
49}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java
new file mode 100644
index 00000000..e6972544
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java
@@ -0,0 +1,61 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.matchers.psystem.aggregations;
10
11import tools.refinery.viatra.runtime.matchers.context.IInputKey;
12import tools.refinery.viatra.runtime.matchers.context.common.JavaTransitiveInstancesKey;
13
14/**
15 * Augments an aggregator operator with type bindings for the type of values being aggregated and the aggregate result.
16 * <p> In case of <em>count</em>, the operator should be null.
17 * @author Gabor Bergmann
18 * @since 1.4
19 */
20public class BoundAggregator {
21 private final IMultisetAggregationOperator<?, ?, ?> operator;
22 private final Class<?> domainType;
23 private final Class<?> aggregateResultType;
24
25 public BoundAggregator(IMultisetAggregationOperator<?, ?, ?> operator,
26 Class<?> domainType,
27 Class<?> aggregateResultType) {
28 super();
29 this.operator = operator;
30 this.domainType = domainType;
31 this.aggregateResultType = aggregateResultType;
32 }
33
34 public IMultisetAggregationOperator<?, ?, ?> getOperator() {
35 return operator;
36 }
37
38 public Class<?> getDomainType() {
39 return domainType;
40 }
41
42 public Class<?> getAggregateResultType() {
43 return aggregateResultType;
44 }
45
46 public IInputKey getDomainTypeAsInputKey() {
47 return toJavaInputKey(domainType);
48 }
49
50 public IInputKey getAggregateResultTypeAsInputKey() {
51 return toJavaInputKey(aggregateResultType);
52 }
53
54 private static IInputKey toJavaInputKey(Class<?> type) {
55 if (type==null) {
56 return null;
57 } else {
58 return new JavaTransitiveInstancesKey(type);
59 }
60 }
61}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java
new file mode 100644
index 00000000..c970bd6a
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java
@@ -0,0 +1,40 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Zoltan Ujhelyi, Tamas Szabo, 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.matchers.psystem.aggregations;
10
11/**
12 *
13 * Describes an aggregation operator keyword, potentially with type polymorphism. The actual runtime
14 * {@link IMultisetAggregationOperator} that implements the aggregation logic may depend on the type context.
15 *
16 * <p>
17 * Implementors are suggested to use lower-case classnames (as it will end up in the language) and are required use the
18 * annotation {@link AggregatorType} to indicate type inference rules.
19 *
20 * <p>
21 * <strong>Important!</strong> Implemented aggregators must be (1) deterministic (2) pure and (3)support incremental
22 * value updates in the internal operation.
23 *
24 * @author Zoltan Ujhelyi
25 * @since 1.4
26 */
27
28public interface IAggregatorFactory {
29
30 /**
31 * Given type parameters selected from {@link AggregatorType} annotations, returns a run-time aggregator operator
32 * that is bound to the actual types.
33 *
34 * @param domainClass
35 * Java type of the values that are being aggregated
36 * @return the actual run-time aggregator logic, with type bindings
37 */
38 public BoundAggregator getAggregatorLogic(Class<?> domainClass);
39
40}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java
new file mode 100644
index 00000000..3bc22274
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java
@@ -0,0 +1,106 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.matchers.psystem.aggregations;
10
11import java.util.Collection;
12import java.util.stream.Stream;
13
14import tools.refinery.viatra.runtime.matchers.aggregators.ExtremumOperator;
15
16/**
17 * A single column aggregator is used to incrementally compute the aggregate of a multiset of values according to an aggregator operator.
18 *
19 * <p> The operator provides two methods of computation: <ul>
20 * <li>Stateless aggregation of an explicit multiset, provided by {@link #aggregateStatelessly(Collection)}.</li>
21 * <li>Incremental aggregation, provided by {@link #createNeutral()}, {@link #update(Object, Object, boolean)}, {@link #isNeutral(Object)}, {@link #getAggregate(Object)}.
22 * </ul>
23 *
24 * <p> In case of incremental computation, the aggregable multiset is conceptual; it is not represented by an explicit Collection<Domain> object, but its update operations are tracked.
25 *
26 * <p> In case of incremental computation, internal results, potentially distinct from the final aggregate result, may be stored in a helper data structure called <b>accumulator</b>.
27 * The goal of this distinction is that the final result may not be sufficient for incremental updates (see e.g. {@link ExtremumOperator}).
28 *
29 * @author Gabor Bergmann
30 *
31 * @param <Domain> the type of elements to be aggregated.
32 * @param <Accumulator> the type used to store the interim results of the aggregate computation,
33 * that may be incrementally refreshed upon updates to the multiset, and that can easily yield the final result.
34 * @param <AggregateResult> the type of the final result of the aggregation to be output.
35 *
36 * @since 1.4
37 */
38public interface IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> {
39
40 /**
41 * A textual description of the operator.
42 */
43 String getShortDescription();
44
45 /**
46 * A name or identifier of the operator.
47 */
48 String getName();
49
50 /**
51 * @return the neutral element, i.e. the interim result of aggregating an empty multiset.
52 */
53 Accumulator createNeutral();
54
55 /**
56 * @return true if the interim result is equivalent to the neutral element, as if there are no values in the multiset.
57 * Must return true if the multiset is empty.
58 */
59 boolean isNeutral(Accumulator result);
60
61 /**
62 * @return an updated intermediate result,
63 * changed to reflect that a given object was added to / removed from the multiset
64 * (as indicated by the parameter isInsertion)
65 */
66 Accumulator update(Accumulator oldResult, Domain updateValue, boolean isInsertion);
67
68 /**
69 * @return the aggregate result obtained from the given intermediate result.
70 * May be null to indicate that the current multiset cannot be aggregated (e.g. 0 elements have no minimum).
71 */
72 AggregateResult getAggregate(Accumulator result);
73
74 /**
75 * Calculates the aggregate results from a given stream of values; all values are considered as inserted
76 * @return the aggregate result, or null if no result can be calculated (e.g. because of an empty stream)
77 * @since 2.0
78 */
79 AggregateResult aggregateStream(Stream<Domain> stream);
80
81 /**
82 * Clones the given accumulator (with all its internal contents).
83 */
84 default Accumulator clone(Accumulator original) {
85 throw new UnsupportedOperationException();
86 }
87
88 /**
89 * Combines the given aggregate result and accumulator into a single aggregate result.
90 */
91 default AggregateResult combine(AggregateResult left, Accumulator right) {
92 throw new UnsupportedOperationException();
93 }
94
95 default boolean contains(Domain value, Accumulator accumulator) {
96 throw new UnsupportedOperationException();
97 }
98
99 /**
100 * Pretty prints the contents of the given accumulator.
101 */
102 default String prettyPrint(final Accumulator accumulator) {
103 return accumulator.toString();
104 }
105
106}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java
new file mode 100644
index 00000000..e3f28cff
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java
@@ -0,0 +1,194 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.matchers.psystem.analysis;
10
11import java.util.HashMap;
12import java.util.HashSet;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Set;
16import java.util.stream.Collectors;
17
18import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
19import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
20import tools.refinery.viatra.runtime.matchers.planning.helpers.FunctionalDependencyHelper;
21import tools.refinery.viatra.runtime.matchers.psystem.PBody;
22import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
23import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
24import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation;
25import tools.refinery.viatra.runtime.matchers.psystem.annotations.ParameterReference;
26import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
27import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
28import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
29import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
30
31/**
32 * Object responsible for computing and caching static query analysis results.
33 * <p> Any client can instantiate this to statically analyze queries.
34 * Query backends should share an instance obtained via {@link IQueryBackendContext} to save resources.
35 * <p> Precondition: all involved queries must be initialized.
36 * @noinstantiate Considered unstable API; subject to change in future versions.
37 * Either use the analyzer provided by {@link IQueryBackendContext}, or anticipate
38 * potential future breakage when instantiating your own analyzer.
39 * @author Gabor Bergmann
40 * @since 1.5
41 */
42public final class QueryAnalyzer {
43
44 private IQueryMetaContext metaContext;
45
46 public QueryAnalyzer(IQueryMetaContext metaContext) {
47 this.metaContext = metaContext;
48 }
49
50 // Functional dependencies
51
52 /**
53 * Maps query and strictness to functional dependencies
54 */
55 private Map<PQuery, Map<Set<Integer>, Set<Integer>>> strictFunctionalDependencyGuarantees =
56 new HashMap<>();
57 private Map<PQuery, Map<Set<Integer>, Set<Integer>>> softFunctionalDependencyGuarantees =
58 new HashMap<>();
59
60 /**
61 * Functional dependency information, expressed on query parameters, that the match set of the query is guaranteed to respect.
62 * <p> The type dependencies shall be expressed on the <i>parameter index</i> integers, NOT the {@link PParameter} object.
63 * @return a non-null map of functional dependencies on parameters that can be processed by {@link FunctionalDependencyHelper}
64 * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation;
65 * if false, user-provided soft dependencies (@FunctionalDependency) are included as well, that are anticipated but not guaranteed by the storage mechanism;
66 * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance
67 * @since 1.5
68 */
69 public Map<Set<Integer>, Set<Integer>> getProjectedFunctionalDependencies(PQuery query, boolean strict) {
70 Map<PQuery, Map<Set<Integer>, Set<Integer>>> guaranteeStore = strict ? strictFunctionalDependencyGuarantees : softFunctionalDependencyGuarantees;
71 Map<Set<Integer>, Set<Integer>> dependencies = guaranteeStore.get(query);
72 // Why not computeIfAbsent? See Bug 532507
73 // Invoked method #computeFunctionalDependencies may trigger functional dependency computation for called queries;
74 // and may thus recurs back into #getProjectedFunctionalDependencies, causing a ConcurrentModificationException
75 // if the called query has not been previously analyzed.
76 //
77 // Note: if patterns are recursive, the empty accumulator will be found in the store
78 // (this yields a safe lower estimate and guarantees termination for #getProjectedFunctionalDependencies)
79 // But this case probably will not occur due to recursive queries having a disjunction at some point,
80 // and thus ignored by #computeFunctionalDependencies
81 if (dependencies == null) {
82 dependencies = new HashMap<>(); // accumulator
83 guaranteeStore.put(query, dependencies);
84 computeFunctionalDependencies(dependencies, query, strict);
85 }
86 return dependencies;
87 }
88
89 private void computeFunctionalDependencies(Map<Set<Integer>, Set<Integer>> accumulator, PQuery query, boolean strict) {
90 Set<PBody> bodies = query.getDisjunctBodies().getBodies();
91 if (bodies.size() == 1) { // no support for recursion or disjunction
92
93 PBody body = bodies.iterator().next();
94
95 // collect parameter variables
96 Map<PVariable, Integer> parameters = body.getSymbolicParameters().stream()
97 .collect(Collectors.toMap(ExportedParameter::getParameterVariable,
98 param -> query.getParameters().indexOf(param.getPatternParameter())));
99
100 // collect all internal dependencies
101 Map<Set<PVariable>, Set<PVariable>> internalDependencies =
102 getFunctionalDependencies(body.getConstraints(), strict);
103
104 // project onto parameter variables
105 Map<Set<PVariable>, Set<PVariable>> projectedDeps =
106 FunctionalDependencyHelper.projectDependencies(internalDependencies, parameters.keySet());
107
108 // translate into indices
109 for (Entry<Set<PVariable>, Set<PVariable>> entry : projectedDeps.entrySet()) {
110 Set<Integer> left = new HashSet<Integer>();
111 Set<Integer> right = new HashSet<Integer>();
112 for (PVariable pVariable : entry.getKey()) {
113 left.add(parameters.get(pVariable));
114 }
115 for (PVariable pVariable : entry.getValue()) {
116 right.add(parameters.get(pVariable));
117 }
118 accumulator.put(left, right);
119 }
120
121 } else {
122 // Disjunctive case, no dependencies are inferred
123 // TODO: we can still salvage the intersection of dependencies IF
124 // - all bodies have disjoint match sets
125 // - and we avoid recursion
126 }
127
128 // add annotation-based soft dependencies (regardless of number of bodies)
129 if (!strict) {
130 outer:
131 for (PAnnotation annotation : query.getAnnotationsByName("FunctionalDependency")) {
132 Set<Integer> lefts = new HashSet<Integer>();
133 Set<Integer> rights = new HashSet<Integer>();
134
135 for (Object object : annotation.getAllValues("forEach")) {
136 ParameterReference parameter = (ParameterReference) object;
137 Integer position = query.getPositionOfParameter(parameter.getName());
138 if (position == null) continue outer;
139 lefts.add(position);
140 }
141 for (Object object : annotation.getAllValues("unique")) {
142 ParameterReference parameter = (ParameterReference) object;
143 Integer position = query.getPositionOfParameter(parameter.getName());
144 if (position == null) continue outer;
145 rights.add(position);
146 }
147
148 FunctionalDependencyHelper.includeDependency(accumulator, lefts, rights);
149 }
150 }
151 }
152
153 /**
154 * Functional dependency information, expressed on PVariables within a body, that the selected constraints imply.
155 * @return a non-null map of functional dependencies on PVariables that can be processed by {@link FunctionalDependencyHelper}
156 * @param constraints the set of constraints whose consequences will be analyzed
157 * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation;
158 * if false, user-provided soft dependencies (@FunctionalDependency) are included as well, that are anticipated but not guaranteed by the storage mechanism;
159 * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance
160 * @since 1.5
161 */
162 public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(Set<? extends PConstraint> constraints, boolean strict) {
163 Map<Set<PVariable>, Set<PVariable>> accumulator = new HashMap<Set<PVariable>, Set<PVariable>>();
164 for (PConstraint pConstraint : constraints){
165 if (pConstraint instanceof PositivePatternCall) {
166 // use query analysis results instead
167 PositivePatternCall call = (PositivePatternCall) pConstraint;
168 PQuery query = call.getSupplierKey();
169 Map<Set<Integer>, Set<Integer>> paramDependencies = getProjectedFunctionalDependencies(query, strict);
170 for (Entry<Set<Integer>, Set<Integer>> entry : paramDependencies.entrySet()) {
171 Set<PVariable> lefts = new HashSet<PVariable>();
172 Set<PVariable> rights = new HashSet<PVariable>();
173
174 for (Integer index : entry.getKey()) {
175 lefts.add(call.getVariableInTuple(index));
176 }
177 for (Integer index : entry.getValue()) {
178 rights.add(call.getVariableInTuple(index));
179 }
180
181 FunctionalDependencyHelper.includeDependency(accumulator,
182 lefts, rights);
183 }
184 } else {
185 // delegate to PConstraint
186 FunctionalDependencyHelper.includeDependencies(accumulator,
187 pConstraint.getFunctionalDependencies(metaContext));
188 }
189 }
190 return accumulator;
191 }
192
193
194}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java
new file mode 100644
index 00000000..c4fbe0e9
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java
@@ -0,0 +1,94 @@
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.matchers.psystem.annotations;
10
11import java.util.List;
12import java.util.Optional;
13import java.util.Set;
14import java.util.function.BiConsumer;
15
16import org.eclipse.collections.api.multimap.MutableMultimap;
17import org.eclipse.collections.impl.multimap.list.FastListMultimap;
18
19/**
20 * A container describing query annotations
21 * @author Zoltan Ujhelyi
22 *
23 */
24public class PAnnotation {
25
26 private final String name;
27 private MutableMultimap<String, Object> attributes = FastListMultimap.newMultimap();
28
29 public PAnnotation(String name) {
30 this.name = name;
31
32 }
33
34 /**
35 * Adds an attribute to the annotation
36 * @param attributeName
37 * @param value
38 */
39 public void addAttribute(String attributeName, Object value) {
40 attributes.put(attributeName, value);
41 }
42
43 /**
44 * Return the name of the annotation
45 */
46 public String getName() {
47 return name;
48 }
49
50 /**
51 * Returns the value of the first occurrence of an attribute
52 * @param attributeName
53 * @return the attribute value, or null, if attribute is not available
54 * @since 2.0
55 */
56 public Optional<Object> getFirstValue(String attributeName) {
57 return getAllValues(attributeName).stream().findFirst();
58 }
59
60 /**
61 * Returns the value of the first occurrence of an attribute
62 * @param attributeName
63 * @return the attribute value, or null, if attribute is not available
64 * @since 2.0
65 */
66 public <T> Optional<T> getFirstValue(String attributeName, Class<T> clazz) {
67 return getAllValues(attributeName).stream().filter(clazz::isInstance).map(clazz::cast).findFirst();
68 }
69
70 /**
71 * Returns all values of a selected attribute
72 * @param attributeName
73 * @return a non-null, but possibly empty list of attributes
74 */
75 public List<Object> getAllValues(String attributeName) {
76 return attributes.get(attributeName).toList();
77 }
78
79 /**
80 * Executes a consumer over all attributes. A selected attribute name (key) can appear (and thus consumed) multiple times.
81 * @since 2.0
82 */
83 public void forEachValue(BiConsumer<String, Object> consumer) {
84 attributes.forEachKeyValue(consumer::accept);
85 }
86
87 /**
88 * Returns a set of all attribute names used in this annotation
89 * @since 2.1
90 */
91 public Set<String> getAllAttributeNames() {
92 return attributes.keySet().toSet();
93 }
94}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java
new file mode 100644
index 00000000..c67e9046
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java
@@ -0,0 +1,30 @@
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.matchers.psystem.annotations;
10
11/**
12 * An annotation parameter referencing a query parameter by name. Does not check whether the parameter exists.
13 *
14 * @author Zoltan Ujhelyi
15 *
16 */
17public class ParameterReference {
18
19 final String name;
20
21 public ParameterReference(String name) {
22 super();
23 this.name = name;
24 }
25
26 public String getName() {
27 return name;
28 }
29
30}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java
new file mode 100644
index 00000000..56f86e89
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java
@@ -0,0 +1,98 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.matchers.psystem.basicdeferred;
10
11import java.util.Collections;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.Map;
15import java.util.Set;
16
17import tools.refinery.viatra.runtime.matchers.context.IInputKey;
18import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
19import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint;
20import tools.refinery.viatra.runtime.matchers.psystem.PBody;
21import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
22import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement;
23import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator;
24import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
25import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
26import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
27
28/**
29 * The PSystem representation of an aggregation.
30 *
31 * @author Tamas Szabo
32 * @since 1.4
33 */
34public class AggregatorConstraint extends PatternCallBasedDeferred implements ITypeInfoProviderConstraint {
35
36 protected PVariable resultVariable;
37 private BoundAggregator aggregator;
38 protected int aggregatedColumn;
39
40 public AggregatorConstraint(BoundAggregator aggregator, PBody pBody, Tuple actualParametersTuple, PQuery query,
41 PVariable resultVariable, int aggregatedColumn) {
42 super(pBody, actualParametersTuple, query, Collections.singleton(resultVariable));
43 this.resultVariable = resultVariable;
44 this.aggregatedColumn = aggregatedColumn;
45 this.aggregator = aggregator;
46 }
47
48 public int getAggregatedColumn() {
49 return this.aggregatedColumn;
50 }
51
52 public BoundAggregator getAggregator() {
53 return this.aggregator;
54 }
55
56 @Override
57 public Set<PVariable> getDeducedVariables() {
58 return Collections.singleton(resultVariable);
59 }
60
61 @Override
62 public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) {
63 final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>();
64 result.put(getDeferringVariables(), getDeducedVariables());
65 return result;
66 }
67
68 @Override
69 protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) {
70 if (resultVariable.equals(obsolete))
71 resultVariable = replacement;
72 }
73
74 @Override
75 protected Set<PVariable> getCandidateQuantifiedVariables() {
76 return actualParametersTuple.<PVariable> getDistinctElements();
77 }
78
79 @Override
80 protected String toStringRest() {
81 return query.getFullyQualifiedName() + "@" + actualParametersTuple.toString() + "->"
82 + resultVariable.toString();
83 }
84
85 public PVariable getResultVariable() {
86 return resultVariable;
87 }
88
89 @Override
90 public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) {
91 Set<TypeJudgement> result = new HashSet<TypeJudgement>();
92 IInputKey aggregateResultType = aggregator.getAggregateResultTypeAsInputKey();
93 if (aggregateResultType != null) {
94 result.add(new TypeJudgement(aggregateResultType, Tuples.staticArityFlatTupleOf(resultVariable)));
95 }
96 return result;
97 }
98}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java
new file mode 100644
index 00000000..7bc949a8
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java
@@ -0,0 +1,99 @@
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.matchers.psystem.basicdeferred;
11
12import java.util.Collections;
13import java.util.Set;
14import java.util.stream.Collectors;
15import java.util.stream.Stream;
16
17import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
18import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
19import tools.refinery.viatra.runtime.matchers.psystem.PBody;
20import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
21import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement;
22import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint;
23
24/**
25 * @author Gabor Bergmann
26 *
27 */
28public abstract class BaseTypeSafeConstraint extends
29 VariableDeferredPConstraint {
30
31 protected Set<PVariable> inputVariables;
32 protected PVariable outputVariable;
33
34 public PVariable getOutputVariable() {
35 return outputVariable;
36 }
37
38 /**
39 * @param pBody
40 * @param inputVariables
41 * @param outputVariable null iff no output (check-only)
42 */
43 public BaseTypeSafeConstraint(PBody pBody,
44 Set<PVariable> inputVariables, final PVariable outputVariable) {
45 super(pBody,
46 (outputVariable == null) ?
47 inputVariables :
48 Stream.concat(inputVariables.stream(), Stream.of(outputVariable)).collect(Collectors.toSet())
49 );
50 this.inputVariables = inputVariables;
51 this.outputVariable = outputVariable;
52 }
53
54 @Override
55 public Set<PVariable> getDeducedVariables() {
56 if (outputVariable == null)
57 return Collections.emptySet();
58 else
59 return Collections.singleton(outputVariable);
60 }
61
62 @Override
63 public Set<PVariable> getDeferringVariables() {
64 return inputVariables;
65 }
66
67 @Override
68 public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) {
69 if (super.isReadyAt(plan, context)) {
70 return checkTypeSafety(plan, context) == null;
71 }
72 return false;
73 }
74
75 /**
76 * Checks whether all type restrictions are already enforced on affected variables.
77 *
78 * @param plan
79 * @return a variable whose type safety is not enforced yet, or null if the plan is typesafe
80 */
81 public PVariable checkTypeSafety(SubPlan plan, IQueryMetaContext context) {
82 Set<TypeJudgement> impliedJudgements = plan.getAllImpliedTypeJudgements(context);
83
84 for (PVariable pVariable : inputVariables) {
85 Set<TypeJudgement> allTypeRestrictionsForVariable = pBody.getAllUnaryTypeRestrictions(context).get(pVariable);
86 if (allTypeRestrictionsForVariable != null && !impliedJudgements.containsAll(allTypeRestrictionsForVariable))
87 return pVariable;
88 }
89 return null;
90 }
91
92 @Override
93 protected void doReplaceVariable(PVariable obsolete, PVariable replacement) {
94 if (inputVariables.remove(obsolete))
95 inputVariables.add(replacement);
96 if (outputVariable == obsolete)
97 outputVariable = replacement;
98 }
99}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java
new file mode 100644
index 00000000..b978b62c
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java
@@ -0,0 +1,96 @@
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.matchers.psystem.basicdeferred;
11
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.HashSet;
15import java.util.Map;
16import java.util.Set;
17
18import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
19import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
20import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint;
21import tools.refinery.viatra.runtime.matchers.psystem.PBody;
22import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
23
24/**
25 * @author Gabor Bergmann
26 *
27 */
28public class Equality extends DeferredPConstraint {
29
30 private PVariable who;
31 private PVariable withWhom;
32
33 public Equality(PBody pBody, PVariable who, PVariable withWhom) {
34 super(pBody, buildSet(who, withWhom));
35 this.who = who;
36 this.withWhom = withWhom;
37 }
38
39 private static Set<PVariable> buildSet(PVariable who, PVariable withWhom) {
40 Set<PVariable> set = new HashSet<PVariable>();
41 set.add(who);
42 set.add(withWhom);
43 return set;
44 }
45
46 /**
47 * An equality is moot if it compares the a variable with itself.
48 *
49 * @return true, if the equality is moot
50 */
51 public boolean isMoot() {
52 return who.equals(withWhom);
53 }
54
55 @Override
56 public void doReplaceVariable(PVariable obsolete, PVariable replacement) {
57 if (obsolete.equals(who))
58 who = replacement;
59 if (obsolete.equals(withWhom))
60 withWhom = replacement;
61 }
62
63 @Override
64 protected String toStringRest() {
65 return who.getName() + "=" + withWhom.getName();
66 }
67
68 public PVariable getWho() {
69 return who;
70 }
71
72 public PVariable getWithWhom() {
73 return withWhom;
74 }
75
76 @Override
77 public Set<PVariable> getDeducedVariables() {
78 return Collections.emptySet();
79 }
80
81 @Override
82 public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) {
83 final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>();
84 result.put(Collections.singleton(who), Collections.singleton(withWhom));
85 result.put(Collections.singleton(withWhom), Collections.singleton(who));
86 return result;
87 }
88
89 @Override
90 public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) {
91 return plan.getVisibleVariables().contains(who) && plan.getVisibleVariables().contains(withWhom);
92 // will be replaced by || if copierNode is available;
93 // until then, LayoutHelper.unifyVariablesAlongEqualities(PSystem<PatternDescription, StubHandle, Collector>) is
94 // recommended.
95 }
96}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java
new file mode 100644
index 00000000..80706792
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java
@@ -0,0 +1,108 @@
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.matchers.psystem.basicdeferred;
11
12import java.util.Collections;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
16import tools.refinery.viatra.runtime.matchers.psystem.PBody;
17import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
18import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint;
19import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
20import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
21
22/**
23 * @author Gabor Bergmann
24 *
25 */
26public class ExportedParameter extends VariableDeferredPConstraint {
27 PVariable parameterVariable;
28 final String parameterName;
29 final PParameter patternParameter;
30
31 /**
32 * @since 1.4
33 */
34 public ExportedParameter(PBody pBody, PVariable parameterVariable, PParameter patternParameter) {
35 super(pBody, Collections.singleton(parameterVariable));
36 this.parameterVariable = parameterVariable;
37 this.patternParameter = patternParameter;
38 parameterName = patternParameter.getName();
39 }
40
41 @Override
42 public void doReplaceVariable(PVariable obsolete, PVariable replacement) {
43 if (obsolete.equals(parameterVariable))
44 parameterVariable = replacement;
45 }
46
47 @Override
48 protected String toStringRest() {
49 Object varName = parameterVariable.getName();
50 return parameterName.equals(varName) ? parameterName : parameterName + "(" + varName + ")";
51 }
52
53 @Override
54 public Set<PVariable> getDeducedVariables() {
55 return Collections.emptySet();
56 }
57
58 /**
59 * The name of the parameter; usually, it is expected that {@link #getParameterVariable()} is more useful, except
60 * maybe for debugging purposes.
61 *
62 * @return a non-null name of the parameter
63 */
64 public String getParameterName() {
65 return parameterName;
66 }
67
68 public PVariable getParameterVariable() {
69 return parameterVariable;
70 }
71
72 /**
73 * @since 1.4
74 */
75 public PParameter getPatternParameter() {
76 if (patternParameter == null) {
77 PQuery query = pBody.getPattern();
78 Integer index = query.getPositionOfParameter(parameterName);
79 if (index == null) {
80 throw new IllegalStateException(String.format("Pattern %s does not have a parameter named %s",
81 query.getFullyQualifiedName(), parameterName));
82 }
83 return query.getParameters().get(index);
84 } else {
85 return patternParameter;
86 }
87 }
88
89 @Override
90 public Set<PVariable> getDeferringVariables() {
91 return Collections.singleton(parameterVariable);
92 }
93
94 @Override
95 public void checkSanity() {
96 super.checkSanity();
97 if (!parameterVariable.isDeducable()) {
98 String[] args = { parameterName };
99 String msg = "Impossible to match pattern: "
100 + "exported pattern variable {1} can not be determined based on the pattern constraints. "
101 + "HINT: certain constructs (e.g. negative patterns or check expressions) cannot output symbolic parameters.";
102 String shortMsg = "Could not deduce value of parameter";
103 throw new QueryProcessingException(msg, args, shortMsg, null);
104 }
105
106 }
107
108}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java
new file mode 100644
index 00000000..06688c36
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java
@@ -0,0 +1,80 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.matchers.psystem.basicdeferred;
10
11import java.util.ArrayList;
12import java.util.Collections;
13import java.util.LinkedHashSet;
14import java.util.Map;
15import java.util.Set;
16
17import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
18import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator;
19import tools.refinery.viatra.runtime.matchers.psystem.PBody;
20import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
22
23/**
24 * @author Zoltan Ujhelyi
25 *
26 */
27public class ExpressionEvaluation extends BaseTypeSafeConstraint {
28
29 private IExpressionEvaluator evaluator;
30 private boolean isUnwinding;
31
32 public ExpressionEvaluation(PBody pBody, IExpressionEvaluator evaluator, PVariable outputVariable) {
33 this(pBody, evaluator, outputVariable, false);
34 }
35
36 /**
37 * @since 2.4
38 */
39 public ExpressionEvaluation(PBody pBody, IExpressionEvaluator evaluator, PVariable outputVariable,
40 boolean isUnwinding) {
41 super(pBody, getPVariablesOfExpression(pBody, evaluator), outputVariable);
42 this.evaluator = evaluator;
43 this.isUnwinding = isUnwinding;
44 }
45
46 /**
47 * @since 2.4
48 */
49 public boolean isUnwinding() {
50 return isUnwinding;
51 }
52
53 public IExpressionEvaluator getEvaluator() {
54 return evaluator;
55 }
56
57 @Override
58 protected String toStringRest() {
59 return Tuples.flatTupleOf(new ArrayList<PVariable>(inputVariables).toArray()).toString() + "|="
60 + evaluator.getShortDescription();
61 }
62
63 @Override
64 public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) {
65 if (outputVariable == null)
66 return Collections.emptyMap();
67 else
68 return Collections.singletonMap(inputVariables, Collections.singleton(outputVariable));
69 }
70
71 private static Set<PVariable> getPVariablesOfExpression(PBody pBody, IExpressionEvaluator evaluator) {
72 // use a linked set, so that the variables will come in the order of the parameters
73 Set<PVariable> result = new LinkedHashSet<PVariable>();
74 for (String name : evaluator.getInputParameterNames()) {
75 PVariable variable = pBody.getOrCreateVariableByName(name);
76 result.add(variable);
77 }
78 return result;
79 }
80}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java
new file mode 100644
index 00000000..5cac33dc
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java
@@ -0,0 +1,151 @@
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.matchers.psystem.basicdeferred;
11
12import java.util.Arrays;
13import java.util.Collections;
14import java.util.HashSet;
15import java.util.Set;
16
17import tools.refinery.viatra.runtime.matchers.psystem.PBody;
18import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
19import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint;
20
21/**
22 * @author Gabor Bergmann
23 *
24 */
25public class Inequality extends VariableDeferredPConstraint {
26
27 private PVariable who;
28 private PVariable withWhom;
29
30 /**
31 * The inequality constraint is weak if it can be ignored when who is the same as withWhom, or if any if them is
32 * undeducible.
33 */
34 private boolean weak;
35
36 public Inequality(PBody pBody, PVariable who, PVariable withWhom) {
37 this(pBody, who, withWhom, false);
38 }
39
40 public Inequality(PBody pBody, PVariable who, PVariable withWhom,
41 boolean weak) {
42 super(pBody, new HashSet<>(Arrays.asList(who, withWhom) ));
43 this.who = who;
44 this.withWhom = withWhom;
45 this.weak = weak;
46 }
47
48 // private Inequality(
49 // PSystem<PatternDescription, StubHandle, ?> pSystem,
50 // PVariable subject, Set<PVariable> inequals)
51 // {
52 // super(pSystem, include(inequals, subject));
53 // this.subject = subject;
54 // this.inequals = inequals;
55 // }
56
57 // private static HashSet<PVariable> include(Set<PVariable> inequals, PVariable subject) {
58 // HashSet<PVariable> hashSet = new HashSet<PVariable>(inequals);
59 // hashSet.add(subject);
60 // return hashSet;
61 // }
62
63 @Override
64 public Set<PVariable> getDeferringVariables() {
65 return getAffectedVariables();
66 }
67
68 // private static int[] mapIndices(Map<Object, Integer> variablesIndex, Set<PVariable> keys) {
69 // int[] result = new int[keys.size()];
70 // int k = 0;
71 // for (PVariable key : keys) {
72 // result[k++] = variablesIndex.get(key);
73 // }
74 // return result;
75 // }
76
77 // @Override
78 // public IFoldablePConstraint getIncorporator() {
79 // return incorporator;
80 // }
81 //
82 // @Override
83 // public void registerIncorporatationInto(IFoldablePConstraint incorporator) {
84 // this.incorporator = incorporator;
85 // }
86 //
87 // @Override
88 // public boolean incorporate(IFoldablePConstraint other) {
89 // if (other instanceof Inequality<?, ?>) {
90 // Inequality other2 = (Inequality) other;
91 // if (subject.equals(other2.subject)) {
92 // Set<PVariable> newInequals = new HashSet<PVariable>(inequals);
93 // newInequals.addAll(other2.inequals);
94 // return new Inequality<PatternDescription, StubHandle>(buildable, subject, newInequals);
95 // }
96 // } else return false;
97 // }
98
99 @Override
100 protected String toStringRest() {
101 return who.toString() + (isWeak() ? "!=?" : "!=") + withWhom.toString();
102 }
103
104 @Override
105 public void doReplaceVariable(PVariable obsolete, PVariable replacement) {
106 if (obsolete.equals(who))
107 who = replacement;
108 if (obsolete.equals(withWhom))
109 withWhom = replacement;
110 }
111
112 @Override
113 public Set<PVariable> getDeducedVariables() {
114 return Collections.emptySet();
115 }
116
117 /**
118 * The inequality constraint is weak if it can be ignored when who is the same as withWhom, or if any if them is
119 * undeducible.
120 *
121 * @return the weak
122 */
123 public boolean isWeak() {
124 return weak;
125 }
126
127 /**
128 * A weak inequality constraint is eliminable if who is the same as withWhom, or if any if them is undeducible.
129 */
130 public boolean isEliminable() {
131 return isWeak() && (who.equals(withWhom) || !who.isDeducable() || !withWhom.isDeducable());
132 }
133
134 /**
135 * Eliminates a weak inequality constraint if it can be ignored when who is the same as withWhom, or if any if them
136 * is undeducible.
137 */
138 public void eliminateWeak() {
139 if (isEliminable())
140 delete();
141 }
142
143 public PVariable getWho() {
144 return who;
145 }
146
147 public PVariable getWithWhom() {
148 return withWhom;
149 }
150
151}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java
new file mode 100644
index 00000000..87d9d9fc
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java
@@ -0,0 +1,52 @@
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.matchers.psystem.basicdeferred;
11
12import java.util.Collections;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.matchers.psystem.PBody;
16import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
17import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19
20/**
21 * @author Gabor Bergmann
22 *
23 */
24public class NegativePatternCall extends PatternCallBasedDeferred {
25
26 public NegativePatternCall(PBody pBody, Tuple actualParametersTuple, PQuery query) {
27 super(pBody, actualParametersTuple, query);
28 }
29
30 @Override
31 public Set<PVariable> getDeducedVariables() {
32 return Collections.emptySet();
33 }
34
35 /**
36 * @return all variables that may potentially be quantified they are not used anywhere else
37 */
38 @Override
39 protected Set<PVariable> getCandidateQuantifiedVariables() {
40 return getAffectedVariables();
41 }
42
43 @Override
44 protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) {
45 }
46
47 @Override
48 protected String toStringRest() {
49 return "!" + query.getFullyQualifiedName() + "@" + actualParametersTuple.toString();
50 }
51
52}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java
new file mode 100644
index 00000000..93eeffec
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java
@@ -0,0 +1,118 @@
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.matchers.psystem.basicdeferred;
11
12import java.util.Collections;
13import java.util.HashSet;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
17import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference;
18import tools.refinery.viatra.runtime.matchers.psystem.PBody;
19import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
20import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
21import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint;
22import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
23import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
24
25/**
26 * @author Gabor Bergmann
27 *
28 */
29public abstract class PatternCallBasedDeferred extends VariableDeferredPConstraint implements IQueryReference {
30
31 protected Tuple actualParametersTuple;
32
33 protected abstract void doDoReplaceVariables(PVariable obsolete, PVariable replacement);
34
35 protected abstract Set<PVariable> getCandidateQuantifiedVariables();
36
37 protected PQuery query;
38 private Set<PVariable> deferringVariables;
39
40 public PatternCallBasedDeferred(PBody pBody, Tuple actualParametersTuple,
41 PQuery pattern, Set<PVariable> additionalAffectedVariables) {
42 super(pBody, union(actualParametersTuple.<PVariable> getDistinctElements(), additionalAffectedVariables));
43 this.actualParametersTuple = actualParametersTuple;
44 this.query = pattern;
45 }
46
47 public PatternCallBasedDeferred(PBody pBody, Tuple actualParametersTuple,
48 PQuery pattern) {
49 this(pBody, actualParametersTuple, pattern, Collections.<PVariable> emptySet());
50 }
51
52 private static Set<PVariable> union(Set<PVariable> a, Set<PVariable> b) {
53 Set<PVariable> result = new HashSet<PVariable>();
54 result.addAll(a);
55 result.addAll(b);
56 return result;
57 }
58
59 @Override
60 public Set<PVariable> getDeferringVariables() {
61 if (deferringVariables == null) {
62 deferringVariables = new HashSet<PVariable>();
63 for (PVariable var : getCandidateQuantifiedVariables()) {
64 if (var.isDeducable())
65 deferringVariables.add(var);
66 }
67 }
68 return deferringVariables;
69 }
70
71 @Override
72 public void checkSanity() {
73 super.checkSanity();
74 for (Object obj : this.actualParametersTuple.getDistinctElements()) {
75 PVariable var = (PVariable) obj;
76 if (!getDeferringVariables().contains(var)) {
77 // so this is a free variable of the NAC / aggregation?
78 for (PConstraint pConstraint : var.getReferringConstraints()) {
79 if (pConstraint != this
80 && !(pConstraint instanceof Equality && ((Equality) pConstraint).isMoot()))
81 throw new QueryProcessingException (
82 "Variable {1} of constraint {2} is not a positively determined part of the pattern, yet it is also affected by {3}.",
83 new String[] { var.toString(), this.toString(), pConstraint.toString() },
84 "Read-only variable can not be deduced", null);
85 }
86 }
87 }
88
89 }
90
91// public SubPlan getSidePlan(IOperationCompiler compiler) throws QueryPlannerException {
92// SubPlan sidePlan = compiler.patternCallPlan(actualParametersTuple, query);
93// sidePlan = BuildHelper.enforceVariableCoincidences(compiler, sidePlan);
94// return sidePlan;
95// }
96
97 @Override
98 protected void doReplaceVariable(PVariable obsolete, PVariable replacement) {
99 if (deferringVariables != null) {
100 // FAIL instead of hopeless attempt to fix
101 // if (deferringVariables.remove(obsolete)) deferringVariables.add(replacement);
102 throw new IllegalStateException("Cannot replace variables on " + this
103 + " when deferring variables have already been identified.");
104 }
105 actualParametersTuple = actualParametersTuple.replaceAll(obsolete, replacement);
106 doDoReplaceVariables(obsolete, replacement);
107 }
108
109 public Tuple getActualParametersTuple() {
110 return actualParametersTuple;
111 }
112
113 @Override
114 public PQuery getReferredQuery() {
115 return query;
116 }
117
118}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java
new file mode 100644
index 00000000..0c40d91e
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java
@@ -0,0 +1,70 @@
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.matchers.psystem.basicdeferred;
11
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.Map;
15import java.util.Set;
16
17import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
18import tools.refinery.viatra.runtime.matchers.psystem.PBody;
19import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
20import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
22
23/**
24 * @author Gabor Bergmann
25 */
26public class PatternMatchCounter extends PatternCallBasedDeferred {
27
28 private PVariable resultVariable;
29
30 public PatternMatchCounter(PBody pBody, Tuple actualParametersTuple,
31 PQuery query, PVariable resultVariable) {
32 super(pBody, actualParametersTuple, query, Collections.singleton(resultVariable));
33 this.resultVariable = resultVariable;
34 }
35
36 @Override
37 public Set<PVariable> getDeducedVariables() {
38 return Collections.singleton(resultVariable);
39 }
40
41 @Override
42 public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) {
43 final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>();
44 result.put(getDeferringVariables(), getDeducedVariables());
45 return result;
46 }
47
48 @Override
49 protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) {
50 if (resultVariable.equals(obsolete))
51 resultVariable = replacement;
52 }
53
54 @Override
55 protected Set<PVariable> getCandidateQuantifiedVariables() {
56 return actualParametersTuple.<PVariable> getDistinctElements();
57 }
58
59
60 @Override
61 protected String toStringRest() {
62 return query.getFullyQualifiedName() + "@" + actualParametersTuple.toString() + "->"
63 + resultVariable.toString();
64 }
65
66 public PVariable getResultVariable() {
67 return resultVariable;
68 }
69
70} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java
new file mode 100644
index 00000000..336a83fb
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java
@@ -0,0 +1,57 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2022, Tamas Szabo, GitHub
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.matchers.psystem.basicdeferred;
10
11import java.util.List;
12
13import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint;
14import tools.refinery.viatra.runtime.matchers.psystem.IMultiQueryReference;
15import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator;
16import tools.refinery.viatra.runtime.matchers.psystem.PBody;
17import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19
20/**
21 * A constraint which prescribes the evaluation of custom Java logic that takes an arbitrary number of input relations
22 * and produces one output relation. Contrast this to {@link ExpressionEvaluation}, which produces a single output value
23 * given an input tuple.
24 *
25 * The assumption is that the relation evaluation logic is not incremental, that is, it can only perform from-scratch
26 * computation of the output relation given the complete input relations. To this end, the relation evaluator always
27 * receives the complete input relations with all their contents as input. However, the evaluator engine makes sure that
28 * the output of the relation evaluation is at least "seemingly" incremental. This means that the underlying computation
29 * network computes the delta on the output compared to the previous output and only propagates the delta further.
30 *
31 * @author Tamas Szabo
32 *
33 * @since 2.8
34 *
35 */
36public class RelationEvaluation extends EnumerablePConstraint implements IMultiQueryReference {
37
38 private final IRelationEvaluator evaluator;
39 private final List<PQuery> inputQueries;
40
41 public RelationEvaluation(final PBody body, final Tuple variablesTuple, final List<PQuery> inputQueries,
42 final IRelationEvaluator evaluator) {
43 super(body, variablesTuple);
44 this.evaluator = evaluator;
45 this.inputQueries = inputQueries;
46 }
47
48 public IRelationEvaluator getEvaluator() {
49 return this.evaluator;
50 }
51
52 @Override
53 public List<PQuery> getReferredQueries() {
54 return this.inputQueries;
55 }
56
57}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java
new file mode 100644
index 00000000..8b6e29ef
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java
@@ -0,0 +1,105 @@
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.matchers.psystem.basicdeferred;
10
11import java.util.Collections;
12import java.util.Map;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.matchers.context.IInputKey;
16import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
17import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint;
18import tools.refinery.viatra.runtime.matchers.psystem.PBody;
19import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
20import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement;
21import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint;
22import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
23import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
24
25/**
26 * Represents a non-enumerable type constraint that asserts that values substituted for the given tuple of variables
27 * form a tuple that belongs to a (typically non-enumerable) extensional relation identified by an {@link IInputKey}.
28 *
29 * <p> The InputKey is typically not enumerable. If it is enumerable, use {@link TypeConstraint} instead, so that the PConstraint carries over the property of enumerability.
30 *
31 * @author Bergmann Gabor
32 *
33 */
34public class TypeFilterConstraint extends VariableDeferredPConstraint implements
35 ITypeConstraint {
36
37 private Tuple variablesTuple;
38 private IInputKey inputKey;
39
40 private TypeJudgement equivalentJudgement;
41
42
43 public TypeFilterConstraint(PBody pBody, Tuple variablesTuple, IInputKey inputKey) {
44 super(pBody, variablesTuple.<PVariable> getDistinctElements());
45 this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple);
46
47 this.variablesTuple = variablesTuple;
48 this.inputKey = inputKey;
49
50 if (variablesTuple.getSize() != inputKey.getArity())
51 throw new IllegalArgumentException(
52 this.getClass().getSimpleName() +
53 " applied for variable tuple " + variablesTuple + " having wrong arity for input key " +
54 inputKey);
55 }
56
57
58
59 public Tuple getVariablesTuple() {
60 return variablesTuple;
61 }
62
63 public IInputKey getInputKey() {
64 return inputKey;
65 }
66
67 @Override
68 public TypeJudgement getEquivalentJudgement() {
69 return equivalentJudgement;
70 }
71
72 @Override
73 protected void doReplaceVariable(PVariable obsolete, PVariable replacement) {
74 variablesTuple = variablesTuple.replaceAll(obsolete, replacement);
75 this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple);
76 }
77
78 @Override
79 public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) {
80 return Collections.singleton(equivalentJudgement);
81 }
82
83 @Override
84 public Set<PVariable> getDeducedVariables() {
85 return Collections.emptySet();
86 }
87
88 @Override
89 public Set<PVariable> getDeferringVariables() {
90 return getAffectedVariables();
91 }
92
93 @Override
94 protected String toStringRest() {
95 return inputKey.getPrettyPrintableName() + "@" + variablesTuple;
96 }
97
98 @Override
99 public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) {
100 return TypeConstraintUtil.getFunctionalDependencies(context, inputKey, variablesTuple);
101 }
102
103
104
105}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java
new file mode 100644
index 00000000..7bbf7118
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java
@@ -0,0 +1,44 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Gabor Bergmann, 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.matchers.psystem.basicenumerables;
10
11import java.util.Set;
12
13import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
14import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference;
15import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint;
16import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint;
17import tools.refinery.viatra.runtime.matchers.psystem.PBody;
18import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement;
19import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
20import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
21
22/**
23 * @since 2.0
24 */
25public abstract class AbstractTransitiveClosure extends KeyedEnumerablePConstraint<PQuery> implements IQueryReference, ITypeInfoProviderConstraint {
26
27 public AbstractTransitiveClosure(PBody pBody, Tuple variablesTuple, PQuery supplierKey) {
28 super(pBody, variablesTuple, supplierKey);
29 }
30
31 @Override
32 public PQuery getReferredQuery() {
33 return supplierKey;
34 }
35
36 /**
37 * @since 1.3
38 */
39 @Override
40 public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) {
41 return PositivePatternCall.getTypesImpliedByCall(supplierKey, variablesTuple);
42 }
43
44} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java
new file mode 100644
index 00000000..e3dae240
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java
@@ -0,0 +1,57 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2010 Zoltan Ujhelyi, 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.matchers.psystem.basicenumerables;
11
12import tools.refinery.viatra.runtime.matchers.context.IInputKey;
13import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
14import tools.refinery.viatra.runtime.matchers.psystem.PBody;
15import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17
18/**
19 * For a binary base pattern over an enumerable universe type, computes the reflexive transitive closure (base)*
20 *
21 * @author Gabor Bergmann, Zoltan Ujhelyi
22 * @since 2.0
23 */
24public class BinaryReflexiveTransitiveClosure extends AbstractTransitiveClosure {
25
26 private final IInputKey universeType;
27
28 public BinaryReflexiveTransitiveClosure(PBody pBody, Tuple variablesTuple,
29 PQuery pattern, IInputKey universeType) {
30 super(pBody, variablesTuple, pattern);
31 this.universeType = universeType;
32 }
33
34 @Override
35 protected String keyToString() {
36 return supplierKey.getFullyQualifiedName() + "*";
37 }
38
39 /**
40 * Returns the type whose instances should be returned as 0-long paths.
41 * @since 2.0
42 */
43 public IInputKey getUniverseType() {
44 return universeType;
45 }
46
47 @Override
48 public void checkSanity() {
49 if (!universeType.isEnumerable() || universeType.getArity() != 1) {
50 throw new QueryProcessingException(
51 String.format("Invalid universe type %s - it should be enumerable and must have an arity of 1.",
52 universeType.getPrettyPrintableName()),
53 pBody.getPattern());
54 }
55 }
56
57}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java
new file mode 100644
index 00000000..716d043b
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java
@@ -0,0 +1,33 @@
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.matchers.psystem.basicenumerables;
11
12import tools.refinery.viatra.runtime.matchers.psystem.PBody;
13import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15
16/**
17 * For a binary base pattern, computes the irreflexive transitive closure (base)+
18 *
19 * @author Gabor Bergmann
20 *
21 */
22public class BinaryTransitiveClosure extends AbstractTransitiveClosure {
23
24 public BinaryTransitiveClosure(PBody pBody, Tuple variablesTuple,
25 PQuery pattern) {
26 super(pBody, variablesTuple, pattern);
27 }
28
29 @Override
30 protected String keyToString() {
31 return supplierKey.getFullyQualifiedName() + "+";
32 }
33}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java
new file mode 100644
index 00000000..10da2e21
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java
@@ -0,0 +1,11 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables;
7
8public enum Connectivity {
9 WEAK,
10 STRONG;
11}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java
new file mode 100644
index 00000000..251146c8
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java
@@ -0,0 +1,57 @@
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.matchers.psystem.basicenumerables;
11
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.Map;
15import java.util.Set;
16
17import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
18import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint;
19import tools.refinery.viatra.runtime.matchers.psystem.PBody;
20import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
22
23/**
24 * @author Gabor Bergmann
25 *
26 */
27public class ConstantValue extends KeyedEnumerablePConstraint<Object> {
28
29 private PVariable variable;
30
31 public ConstantValue(PBody pBody, PVariable variable, Object value) {
32 super(pBody, Tuples.staticArityFlatTupleOf(variable), value);
33 this.variable = variable;
34 }
35
36 @Override
37 protected String keyToString() {
38 return supplierKey.toString();
39 }
40
41 /**
42 * @since 1.7
43 */
44 public PVariable getVariable() {
45 return variable;
46 }
47
48 @Override
49 public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) {
50 final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>();
51 final Set<PVariable> emptySet = Collections.emptySet(); // a constant value is functionally determined by everything
52 result.put(emptySet, Collections.singleton(getVariableInTuple(0)));
53 return result;
54 }
55
56
57}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java
new file mode 100644
index 00000000..25ab34b4
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java
@@ -0,0 +1,76 @@
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.matchers.psystem.basicenumerables;
11
12import java.util.HashSet;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.matchers.context.IInputKey;
16import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
17import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference;
18import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint;
19import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint;
20import tools.refinery.viatra.runtime.matchers.psystem.PBody;
21import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement;
22import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
23import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
24import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
25
26/**
27 * @author Gabor Bergmann
28 *
29 */
30public class PositivePatternCall extends KeyedEnumerablePConstraint<PQuery> implements IQueryReference, ITypeInfoProviderConstraint {
31
32 public PositivePatternCall(PBody pBody, Tuple variablesTuple,
33 PQuery pattern) {
34 super(pBody, variablesTuple, pattern);
35 }
36
37 @Override
38 protected String keyToString() {
39 return supplierKey.getFullyQualifiedName();
40 }
41
42 // Note: #getFunctionalDependencies is intentionally not implemented - use QueryAnalyzer instead!
43// @Override
44// public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) {
45// return super.getFunctionalDependencies(context);
46// }
47
48 @Override
49 public PQuery getReferredQuery() {
50 return supplierKey;
51 }
52
53 @Override
54 public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) {
55 return getTypesImpliedByCall(supplierKey, variablesTuple);
56 }
57
58 /**
59 * @since 1.3
60 */
61 public static Set<TypeJudgement> getTypesImpliedByCall(PQuery calledQuery, Tuple actualParametersTuple) {
62 Set<TypeJudgement> result = new HashSet<TypeJudgement>();
63 for (TypeJudgement parameterJudgement : calledQuery.getTypeGuarantees()) {
64 IInputKey inputKey = parameterJudgement.getInputKey();
65 Tuple judgementIndexTuple = parameterJudgement.getVariablesTuple();
66
67 Object[] judgementVariables = new Object[judgementIndexTuple.getSize()];
68 for (int i=0; i<judgementVariables.length; ++i)
69 judgementVariables[i] = actualParametersTuple.get((int) judgementIndexTuple.get(i));
70
71 result.add(new TypeJudgement(inputKey, Tuples.flatTupleOf(judgementVariables)));
72 }
73 return result;
74 }
75
76}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RepresentativeElectionConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java
index e146213e..b97ff55f 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RepresentativeElectionConstraint.java
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java
@@ -3,14 +3,12 @@
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6package tools.refinery.store.query.viatra.internal.pquery; 6package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables;
7 7
8import org.eclipse.viatra.query.runtime.matchers.context.IQueryMetaContext; 8import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
9import org.eclipse.viatra.query.runtime.matchers.psystem.*; 9import tools.refinery.viatra.runtime.matchers.psystem.*;
10import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; 10import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
11import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery; 11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
13import tools.refinery.store.query.literal.Connectivity;
14 12
15import java.util.Set; 13import java.util.Set;
16 14
@@ -40,6 +38,6 @@ public class RepresentativeElectionConstraint extends KeyedEnumerablePConstraint
40 38
41 @Override 39 @Override
42 protected String keyToString() { 40 protected String keyToString() {
43 return supplierKey.getFullyQualifiedName() + "#representative"; 41 return supplierKey.getFullyQualifiedName() + "#" + connectivity + "#representative";
44 } 42 }
45} 43}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java
new file mode 100644
index 00000000..2ca54cc0
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java
@@ -0,0 +1,79 @@
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.matchers.psystem.basicenumerables;
10
11import java.util.Collections;
12import java.util.Map;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.matchers.context.IInputKey;
16import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
17import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint;
18import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint;
19import tools.refinery.viatra.runtime.matchers.psystem.PBody;
20import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
21import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement;
22import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
23
24/**
25 * Represents an enumerable type constraint that asserts that values substituted for the given tuple of variables
26 * form a tuple that belongs to an enumerable extensional relation identified by an {@link IInputKey}.
27 *
28 * <p> The InputKey must be enumerable!
29 *
30 * @author Zoltan Ujhelyi
31 *
32 */
33public class TypeConstraint extends KeyedEnumerablePConstraint<IInputKey> implements ITypeConstraint {
34
35 private TypeJudgement equivalentJudgement;
36
37 public TypeConstraint(PBody pBody, Tuple variablesTuple, IInputKey inputKey) {
38 super(pBody, variablesTuple, inputKey);
39 this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple);
40
41 if (! inputKey.isEnumerable())
42 throw new IllegalArgumentException(
43 this.getClass().getSimpleName() +
44 " applicable for enumerable input keys only; received instead " +
45 inputKey);
46 if (variablesTuple.getSize() != inputKey.getArity())
47 throw new IllegalArgumentException(
48 this.getClass().getSimpleName() +
49 " applied for variable tuple " + variablesTuple + " having wrong arity for input key " +
50 inputKey);
51 }
52
53 @Override
54 protected String keyToString() {
55 return supplierKey.getPrettyPrintableName();
56 }
57
58 @Override
59 public TypeJudgement getEquivalentJudgement() {
60 return equivalentJudgement;
61 }
62
63 @Override
64 public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) {
65 return Collections.singleton(equivalentJudgement);
66 //return equivalentJudgement.getDirectlyImpliedJudgements(context);
67 }
68
69 @Override
70 public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) {
71 return TypeConstraintUtil.getFunctionalDependencies(context, supplierKey, variablesTuple);
72 }
73
74 @Override
75 public void doReplaceVariable(PVariable obsolete, PVariable replacement) {
76 super.doReplaceVariable(obsolete, replacement);
77 this.equivalentJudgement = new TypeJudgement(getSupplierKey(), variablesTuple);
78 }
79} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java
new file mode 100644
index 00000000..2c03a894
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java
@@ -0,0 +1,231 @@
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.matchers.psystem.queries;
10
11import java.util.ArrayList;
12import java.util.Collections;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Objects;
16import java.util.Optional;
17import java.util.Set;
18import java.util.stream.Collectors;
19import java.util.stream.Stream;
20
21import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
22import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
23import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
24import tools.refinery.viatra.runtime.matchers.context.IInputKey;
25import tools.refinery.viatra.runtime.matchers.psystem.PBody;
26import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement;
27import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation;
28import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
29import tools.refinery.viatra.runtime.matchers.util.Preconditions;
30
31/**
32 * Default implementation of PQuery.
33 *
34 * @author Bergmann Gabor
35 */
36public abstract class BasePQuery implements PQuery {
37
38 protected PQueryStatus status = PQueryStatus.UNINITIALIZED;
39 /**
40 * @since 2.0
41 */
42 protected final PVisibility visibility;
43 protected List<PProblem> pProblems = new ArrayList<PProblem>();
44 private List<PAnnotation> annotations = new ArrayList<PAnnotation>();
45 private QueryEvaluationHint evaluationHints = new QueryEvaluationHint(null, (IQueryBackendFactory)null);
46 PDisjunction canonicalDisjunction;
47 private List<String> parameterNames = null; // Lazy initialization
48
49 /** For traceability only. */
50 private List<Object> wrappingQuerySpecifications = new ArrayList<Object>(1);
51
52 @Override
53 public Integer getPositionOfParameter(String parameterName) {
54 ensureInitialized();
55 int index = getParameterNames().indexOf(parameterName);
56 return index != -1 ? index : null;
57 }
58
59 protected void setStatus(PQueryStatus newStatus) {
60 this.status = newStatus;
61 }
62
63 protected void addError(PProblem problem) {
64 status = PQueryStatus.ERROR;
65 pProblems.add(problem);
66 }
67
68 @Override
69 public PQueryStatus getStatus() {
70 return status;
71 }
72
73 @Override
74 public List<PProblem> getPProblems() {
75 return Collections.unmodifiableList(pProblems);
76 }
77
78 @Override
79 public boolean isMutable() {
80 return status.equals(PQueryStatus.UNINITIALIZED) || status.equals(PQueryStatus.INITIALIZING);
81 }
82
83 @Override
84 public void checkMutability() {
85 Preconditions.checkState(isMutable(), "Cannot edit query definition %s", getFullyQualifiedName());
86 }
87
88 /**
89 * @since 1.5
90 */
91 public void setEvaluationHints(QueryEvaluationHint hints) {
92 checkMutability();
93 this.evaluationHints = hints;
94 }
95
96 @Override
97 public QueryEvaluationHint getEvaluationHints() {
98 ensureInitialized();
99 return evaluationHints;
100 // TODO instead of field, compute something from annotations?
101 }
102
103 protected void addAnnotation(PAnnotation annotation) {
104 checkMutability();
105 annotations.add(annotation);
106 }
107
108 @Override
109 public List<PAnnotation> getAllAnnotations() {
110 ensureInitialized();
111 return new ArrayList<>(annotations);
112 }
113
114 private Stream<PAnnotation> getAnnotationStreamByName(final String name) {
115 ensureInitialized();
116 return annotations.stream().filter(Objects::nonNull).filter(annotation -> Objects.equals(name, annotation.getName()));
117 }
118
119 @Override
120 public List<PAnnotation> getAnnotationsByName(final String annotationName) {
121 return getAnnotationStreamByName(annotationName).collect(Collectors.toList());
122 }
123
124 @Override
125 public Optional<PAnnotation> getFirstAnnotationByName(String annotationName) {
126 return getAnnotationStreamByName(annotationName).findFirst();
127 }
128
129 @Override
130 public List<String> getParameterNames() {
131 ensureInitialized();
132 if (parameterNames == null) {
133 parameterNames = getParameters().stream().map(PParameter::getName).collect(Collectors.toList());
134 }
135 return parameterNames;
136 }
137
138 @Override
139 public Set<PQuery> getDirectReferredQueries() {
140 ensureInitialized();
141 return canonicalDisjunction.getDirectReferredQueries();
142 }
143
144 @Override
145 public Set<PQuery> getAllReferredQueries() {
146 ensureInitialized();
147 return canonicalDisjunction.getAllReferredQueries();
148 }
149
150
151 @Override
152 public List<Object> publishedAs() {
153 return wrappingQuerySpecifications;
154 }
155
156 @Override
157 public Set<TypeJudgement> getTypeGuarantees() {
158 ensureInitialized();
159 Set<TypeJudgement> result = new HashSet<TypeJudgement>();
160
161 List<PParameter> parameters = getParameters();
162 for (int i=0; i<parameters.size(); ++i) {
163 PParameter parameter = parameters.get(i);
164 IInputKey declaredUnaryType = parameter.getDeclaredUnaryType();
165 if (declaredUnaryType != null) {
166 result.add(new TypeJudgement(declaredUnaryType, Tuples.staticArityFlatTupleOf(i)));
167 }
168 }
169
170 return result;
171 }
172
173 /**
174 * @since 2.0
175 */
176 public BasePQuery(PVisibility visibility) {
177 super();
178 this.visibility = visibility;
179 }
180
181 @Override
182 public PDisjunction getDisjunctBodies() {
183 ensureInitialized();
184 Preconditions.checkState(!status.equals(PQueryStatus.ERROR), "Query %s contains errors.", getFullyQualifiedName());
185 return canonicalDisjunction;
186 }
187
188 @Override
189 public final void ensureInitialized() {
190 try {
191 if (status.equals(PQueryStatus.UNINITIALIZED)) {
192 setStatus(PQueryStatus.INITIALIZING);
193 setBodies(doGetContainedBodies());
194 setStatus(PQueryStatus.OK);
195 }
196 } catch (QueryInitializationException e) {
197 addError(new PProblem(e, e.getShortMessage()));
198 throw e;
199 }
200 }
201
202 protected final void setBodies(Set<PBody> bodies) {
203 canonicalDisjunction = new PDisjunction(this, bodies);
204 for (PBody body : canonicalDisjunction.getBodies()) {
205 body.setStatus(null);
206 }
207 setStatus(PQueryStatus.OK);
208 }
209
210 /**
211 * Creates and returns the bodies of the query. If recalled again, a new instance is created.
212 *
213 * @return
214 * @throws ViatraQueryRuntimeException
215 */
216 protected abstract Set<PBody> doGetContainedBodies();
217
218 @Override
219 public String toString() {
220 return String.format("PQuery<%s>=%s", getFullyQualifiedName(), super.toString());
221 }
222
223 /**
224 * @since 2.0
225 */
226 @Override
227 public PVisibility getVisibility() {
228 return visibility;
229 }
230
231} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java
new file mode 100644
index 00000000..eae4eacf
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java
@@ -0,0 +1,104 @@
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.matchers.psystem.queries;
10
11import java.util.Collections;
12import java.util.LinkedHashSet;
13import java.util.Set;
14import java.util.stream.Collectors;
15
16import tools.refinery.viatra.runtime.matchers.psystem.PBody;
17
18/**
19 *
20 * A disjunction is a set of bodies representing separate conditions. A {@link PQuery} has a single, canonical
21 * PDisjunction, that can be replaced using rewriter
22 *
23 * @author Zoltan Ujhelyi
24 *
25 */
26public class PDisjunction {
27
28 private Set<PBody> bodies;
29 private PQuery query;
30
31 public PDisjunction(Set<PBody> bodies) {
32 this(bodies.iterator().next().getPattern(), bodies);
33 }
34
35 public PDisjunction(PQuery query, Set<PBody> bodies) {
36 super();
37 this.query = query;
38 this.bodies = Collections.unmodifiableSet(new LinkedHashSet<>(bodies));
39 this.bodies.forEach(body -> body.setContainerDisjunction(this));
40 }
41
42 /**
43 * Returns an immutable set of bodies that consists of this disjunction
44 *
45 * @return the bodies
46 */
47 public Set<PBody> getBodies() {
48 return bodies;
49 }
50
51 /**
52 * Returns the corresponding query specification. May be null if not set.
53 */
54 public PQuery getQuery() {
55 return query;
56 }
57
58 /**
59 * Returns all queries directly referred in the constraints. They are all required to evaluate this query
60 *
61 * @return a non-null, but possibly empty list of query definitions
62 */
63 public Set<PQuery> getDirectReferredQueries() {
64 return this.getBodies().stream().
65 flatMap(PQueries.directlyReferencedQueriesFunction()). // flatten stream of streams
66 collect(Collectors.toCollection(LinkedHashSet::new));
67 }
68
69 /**
70 * Returns all queries required to evaluate this query (transitively).
71 *
72 * @return a non-null, but possibly empty list of query definitions
73 */
74 public Set<PQuery> getAllReferredQueries() {
75 Set<PQuery> processedQueries = new LinkedHashSet<>();
76 processedQueries.add(this.getQuery());
77 Set<PQuery> foundQueries = getDirectReferredQueries();
78 Set<PQuery> newQueries = new LinkedHashSet<>(foundQueries);
79
80 while(!processedQueries.containsAll(newQueries)) {
81 PQuery query = newQueries.iterator().next();
82 processedQueries.add(query);
83 newQueries.remove(query);
84 Set<PQuery> referred = query.getDirectReferredQueries();
85 referred.removeAll(processedQueries);
86 foundQueries.addAll(referred);
87 newQueries.addAll(referred);
88 }
89 return foundQueries;
90 }
91
92 /**
93 * Decides whether a disjunction is mutable. A disjunction is mutable if all its contained bodies are mutable.
94 *
95 */
96 public boolean isMutable() {
97 for (PBody body : bodies) {
98 if (!body.isMutable()) {
99 return false;
100 }
101 }
102 return true;
103 }
104}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java
new file mode 100644
index 00000000..07165aa2
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java
@@ -0,0 +1,105 @@
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.matchers.psystem.queries;
10
11import java.util.Objects;
12
13import tools.refinery.viatra.runtime.matchers.context.IInputKey;
14
15/**
16 * A descriptor for declared PQuery parameters. A parameter has a name, a declared type and a direction constraint
17 *
18 * @author Zoltan Ujhelyi
19 *
20 */
21public class PParameter {
22
23 private final String name;
24 private final String typeName;
25 private final IInputKey declaredUnaryType;
26 private final PParameterDirection direction;
27
28 public PParameter(String name) {
29 this(name, (String) null);
30 }
31
32 public PParameter(String name, String typeName) {
33 this(name, typeName, (IInputKey) null);
34 }
35
36 public PParameter(String name, String typeName, IInputKey declaredUnaryType) {
37 this(name, typeName, declaredUnaryType, PParameterDirection.INOUT);
38 }
39
40 /**
41 * @since 1.4
42 */
43 public PParameter(String name, String typeName, IInputKey declaredUnaryType, PParameterDirection direction) {
44 super();
45 this.name = name;
46 this.typeName = typeName;
47 this.declaredUnaryType = declaredUnaryType;
48 this.direction = direction;
49
50 if (declaredUnaryType != null && declaredUnaryType.getArity() != 1) {
51 throw new IllegalArgumentException(
52 "PParameter declared type must be unary instead of " + declaredUnaryType.getPrettyPrintableName());
53 }
54 }
55
56 /**
57 * @return the direction
58 * @since 1.4
59 */
60 public PParameterDirection getDirection() {
61 return direction;
62 }
63
64 /**
65 * @return the name of the parameter
66 */
67 public String getName() {
68 return name;
69 }
70
71 /**
72 * Returns a textual representation of the declared type of the parameter
73 *
74 * @return the type description, or null if not available
75 */
76 public String getTypeName() {
77 return typeName;
78 }
79
80 /**
81 * Yield an {@link IInputKey} representation of the type declared for this parameter.
82 *
83 * @return the unary type that was declared on this parameter in the query header, or null if not available
84 */
85 public IInputKey getDeclaredUnaryType() {
86 return declaredUnaryType;
87 }
88
89 @Override
90 public boolean equals(Object obj) {
91 if (obj instanceof PParameter) {
92 return Objects.equals(name, ((PParameter) obj).name)
93 && Objects.equals(typeName, ((PParameter) obj).typeName)
94 && Objects.equals(declaredUnaryType, ((PParameter) obj).declaredUnaryType)
95 && Objects.equals(direction, ((PParameter) obj).direction);
96 }
97 return false;
98 }
99
100 @Override
101 public int hashCode() {
102 return Objects.hash(name, typeName, declaredUnaryType);
103 }
104
105}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java
new file mode 100644
index 00000000..c94d4797
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java
@@ -0,0 +1,35 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.matchers.psystem.queries;
10
11/**
12 * Values of this enum describe a constraint to the calling of patterns regarding its parameters.
13 *
14 * @author Grill Balázs
15 * @since 1.4
16 *
17 */
18public enum PParameterDirection {
19
20 /**
21 * Default value, no additional constraint is applied
22 */
23 INOUT,
24
25 /**
26 * The parameters marked with this constraints shall be set to a value before calling the pattern
27 */
28 IN,
29
30 /**
31 * The parameters marked with this constraints shall not be set to a value before calling the pattern
32 */
33 OUT
34
35}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java
new file mode 100644
index 00000000..1fe4f541
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java
@@ -0,0 +1,68 @@
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.matchers.psystem.queries;
10
11import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
12
13/**
14 * Represents an error that was detected while the {@link PQuery} object was built from a source.
15 * @author Bergmann Gabor
16 *
17 */
18public class PProblem {
19
20 private final String shortMessage;
21 private final String location;
22 private final Exception exception;
23
24 public PProblem(String shortMessage) {
25 this(null, shortMessage, null, null);
26 }
27 /**
28 * @since 2.0
29 */
30 public PProblem(String shortMessage, Integer line, Integer column) {
31 this(null, shortMessage, line, column);
32 }
33 public PProblem(QueryProcessingException exception) {
34 this(exception, exception.getShortMessage(), null, null);
35 }
36 public PProblem(Exception exception, String shortMessage) {
37 this(exception, shortMessage, null, null);
38 }
39
40 /**
41 * @since 2.0
42 */
43 public PProblem(Exception exception, String shortMessage, Integer line, Integer column) {
44 this.shortMessage = shortMessage;
45 this.exception = exception;
46 if (line == null) {
47 location = "Unspecified location";
48 } else if (column == null) {
49 location = String.format("Line %d", line);
50 } else {
51 location = String.format("Line %d Column %d", line, column);
52 }
53 }
54
55 public String getShortMessage() {
56 return shortMessage;
57 }
58 public Exception getException() {
59 return exception;
60 }
61 /**
62 * @since 2.0
63 */
64 public String getLocation() {
65 return location;
66 }
67
68}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java
new file mode 100644
index 00000000..56f8ca76
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java
@@ -0,0 +1,110 @@
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.matchers.psystem.queries;
10
11import java.util.HashSet;
12import java.util.Set;
13import java.util.function.Function;
14import java.util.function.Predicate;
15import java.util.stream.Stream;
16
17import tools.refinery.viatra.runtime.matchers.context.IInputKey;
18import tools.refinery.viatra.runtime.matchers.psystem.IMultiQueryReference;
19import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint;
20import tools.refinery.viatra.runtime.matchers.psystem.PBody;
21import tools.refinery.viatra.runtime.matchers.psystem.PTraceable;
22import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
23import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus;
24
25/**
26 * Utility class for using PQueries in functional/streaming collection operations effectively
27 *
28 * @author Zoltan Ujhelyi
29 *
30 */
31public final class PQueries {
32
33 /**
34 * Hidden constructor for utility class
35 */
36 private PQueries() {
37 }
38
39 /**
40 * Predicate checking for the status of selected queries
41 *
42 */
43 public static Predicate<PQuery> queryStatusPredicate(final PQueryStatus status) {
44 return query -> query.getStatus().equals(status);
45 }
46
47 /**
48 * Enumerates referred queries (without duplicates) for the given body
49 */
50 public static Function<PBody, Stream<PQuery>> directlyReferencedQueriesFunction() {
51 return body -> (body.getConstraintsOfType(IMultiQueryReference.class).stream()
52 .flatMap(e -> e.getReferredQueries().stream()).distinct());
53 }
54
55 /**
56 * Enumerates directly referred extensional relations (without duplicates) in the canonical form of the given query
57 *
58 * @param enumerablesOnly
59 * only enumerable type constraints are considered
60 * @since 2.0
61 */
62 public static Stream<IInputKey> directlyRequiredTypesOfQuery(PQuery query, boolean enumerablesOnly) {
63 return directlyRequiredTypesOfDisjunction(query.getDisjunctBodies(), enumerablesOnly);
64 }
65
66 /**
67 * Enumerates directly referred extensional relations (without duplicates) for the given formulation of a query.
68 *
69 * @param enumerablesOnly
70 * only enumerable type constraints are considered
71 * @since 2.0
72 */
73 public static Stream<IInputKey> directlyRequiredTypesOfDisjunction(PDisjunction disjunctBodies,
74 boolean enumerablesOnly) {
75 Class<? extends ITypeConstraint> filterClass = enumerablesOnly ? TypeConstraint.class : ITypeConstraint.class;
76 return disjunctBodies.getBodies().stream().flatMap(body -> body.getConstraintsOfType(filterClass).stream())
77 .map(constraint -> constraint.getEquivalentJudgement().getInputKey()).distinct();
78 }
79
80 /**
81 * @since 1.4
82 */
83 public static Predicate<PParameter> parameterDirectionPredicate(final PParameterDirection direction) {
84 return input -> input.getDirection() == direction;
85 }
86
87 /**
88 * Returns all {@link PTraceable}s contained in the given {@link PQuery}: itself, its bodies and their constraints.
89 *
90 * @since 1.6
91 */
92 public static Set<PTraceable> getTraceables(PQuery query) {
93 final Set<PTraceable> traceables = new HashSet<>();
94 traceables.add(query);
95 query.getDisjunctBodies().getBodies().forEach(body -> {
96 traceables.add(body);
97 body.getConstraints().forEach(traceables::add);
98 });
99 return traceables;
100 }
101
102 /**
103 * Calculates the simple name related from a given qualified name by finding the part after the last '.' character.
104 *
105 * @since 2.0
106 */
107 public static String calculateSimpleName(String qualifiedName) {
108 return qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1);
109 }
110}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java
new file mode 100644
index 00000000..a909c650
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java
@@ -0,0 +1,154 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.matchers.psystem.queries;
10
11import java.util.List;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
15import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
16import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
17import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
18import tools.refinery.viatra.runtime.matchers.psystem.PBody;
19import tools.refinery.viatra.runtime.matchers.psystem.PTraceable;
20import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement;
21
22/**
23 * Internal representation of a query / graph pattern (using a constraint system formalism),
24 * to be interpreted by a query evaluator ({@link IQueryBackend}).
25 * End-users of VIATRA Query should access a query as an IQuerySpecification instead.
26 *
27 * <p>
28 * PQuerys are definitions of queries usable inside pattern descriptions. Such description always has (a non-null) name. The query
29 * itself is defined as a (non-empty) set of {@link PBody} instances, the result is the disjunction of the single
30 * {@link PBody} instances. </p>
31 * <p>
32 * A PQuery might be constructed from erroneous patterns or might be uninitialized - this is represented by its status.
33 *
34 * @author Zoltan Ujhelyi
35 * @since 0.8.0
36 * @noimplement This interface is not intended to be implemented by clients. Use {@link BasePQuery} as a base class instead.
37 */
38public interface PQuery extends PQueryHeader, PTraceable {
39
40 // TODO rewritten as / rewritten from traceability to PDisjunction?
41
42 /**
43 * @author Zoltan Ujhelyi
44 *
45 */
46 public enum PQueryStatus {
47 /**
48 * Marks that the query definition is not initialized
49 */
50 UNINITIALIZED,
51 /**
52 * Marks that the query definition is being initialized
53 * @since 1.4
54 */
55 INITIALIZING,
56 /**
57 * The query definition was successfully initialized
58 */
59 OK,
60 /**
61 * The query definition was initialized, but some issues were present
62 */
63 WARNING,
64 /**
65 * The query definition was not successfully initialized because of an error
66 */
67 ERROR
68 }
69
70 /**
71 * Returns all bodies associated with the query in their canonical form. If called multiple times, the same set with
72 * the same contents will be returned.
73 *
74 */
75 PDisjunction getDisjunctBodies();
76
77 /**
78 * Returns all queries directly referred in the constraints. They are all required to evaluate this query
79 *
80 * @return a non-null, but possibly empty list of query definitions
81 */
82 Set<PQuery> getDirectReferredQueries();
83
84 /**
85 * Returns all queries required to evaluate this query (transitively).
86 *
87 * @return a non-null, but possibly empty list of query definitions
88 */
89 Set<PQuery> getAllReferredQueries();
90
91 /**
92 * Returns the initialization status of the definition
93 *
94 */
95 PQueryStatus getStatus();
96
97 /**
98 * Returns a list describing the problems that were found in this query.
99 *
100 * <p> TODO: formulate invariant connecting {@link #getPProblems()} and {@link #getStatus()}.
101 *
102 * @return a non-null, but possibly empty list of problems
103 */
104 List<PProblem> getPProblems();
105
106 /**
107 * Before a modification operation is executed, a mutability check is performed (via the {@link #getStatus()}
108 * implementation, and in case of problems an {@link IllegalStateException} is thrown.
109 */
110 void checkMutability();
111
112 /**
113 * An option to check mutability of the query. It can be used to avoid getting an {@link IllegalStateException} by
114 * the execution of {@link #checkMutability()}.
115 *
116 * @return true if the query specification is still editable
117 */
118 boolean isMutable();
119
120 /**
121 * Optional hints regarding the query evaluation strategy, to be interpreted by the query engine.
122 * <p> To ensure the possibility of external overrides,
123 * the evaluation engine should not directly consult this field,
124 * but use an {@link IQueryBackendHintProvider} instead.
125 */
126 public QueryEvaluationHint getEvaluationHints();
127
128
129 /**
130 * Type information, expressed on query parameters, that all matches of the query are guaranteed to respect.
131 * <p> At the very minimum, this should include the declared types of the parameters.
132 * <p> The type judgement tuples shall contain the <i>parameter index</i>, NOT the {@link PParameter} object.
133 *
134 * @return a non-null set of type judgements that the query guarantees for its matches
135 */
136 public Set<TypeJudgement> getTypeGuarantees();
137
138 /**
139 * If the query definition is uninitialized, initializes it.
140 * @throws ViatraQueryRuntimeException if initialization of query specification fails
141 */
142 public abstract void ensureInitialized();
143
144 /**
145 * Returns the end-user query specification API objects that wrap this query.
146 *
147 * <p> Intended for traceability and debug purposes, not part of normal operation.
148 * Returned list is intended to be appended during query specification construction time.
149 *
150 * @return a non-null, but possibly empty list of query specification objects;
151 */
152 List<Object> publishedAs();
153
154} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java
new file mode 100644
index 00000000..f3671934
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java
@@ -0,0 +1,101 @@
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.matchers.psystem.queries;
10
11import java.util.List;
12import java.util.Optional;
13
14import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation;
15
16/**
17 * Represents header information (metainfo) about a query.
18 * <p> To be implemented both by IQuerySpecifications intended for end users,
19 * and the internal query representation {@link PQuery}.
20 *
21 *
22 * @author Bergmann Gabor
23 * @since 0.9
24 */
25public interface PQueryHeader {
26
27 /**
28 * Identifies the pattern for which matchers can be instantiated.
29 */
30 public String getFullyQualifiedName();
31
32 /**
33 * Return the list of parameter names
34 *
35 * @return a non-null, but possibly empty list of parameter names
36 */
37 public List<String> getParameterNames();
38
39 /**
40 * Returns a list of parameter descriptions
41 *
42 * @return a non-null, but possibly empty list of parameter descriptions
43 */
44 public List<PParameter> getParameters();
45
46 /**
47 * Returns the index of a named parameter
48 *
49 * @param parameterName
50 * @return the index, or null of no such parameter is available
51 */
52 public Integer getPositionOfParameter(String parameterName);
53
54 /**
55 * Returns a parameter by name if exists
56 * @since 2.1
57 */
58 default Optional<PParameter> getParameter(String parameterName) {
59 return Optional.ofNullable(getPositionOfParameter(parameterName))
60 .map(getParameters()::get);
61 }
62
63 /**
64 * Returns the list of annotations specified for this query
65 *
66 * @return a non-null, but possibly empty list of annotations
67 */
68 public List<PAnnotation> getAllAnnotations();
69
70 /**
71 * Returns the list of annotations with a specified name
72 *
73 * @param annotationName
74 * @return a non-null, but possibly empty list of annotations
75 */
76 public List<PAnnotation> getAnnotationsByName(String annotationName);
77
78 /**
79 * Returns the first annotation with a specified name
80 *
81 * @since 2.0
82 */
83 public Optional<PAnnotation> getFirstAnnotationByName(String annotationName);
84
85 /**
86 * Returns the visibility information about the query.
87 * @since 2.0
88 */
89 public PVisibility getVisibility();
90
91 /**
92 * Returns the non-qualified name of the query. By default this means returning the qualified name after the last
93 * '.' character.
94 *
95 * @since 2.0
96 */
97 public default String getSimpleName() {
98 return PQueries.calculateSimpleName(getFullyQualifiedName());
99 }
100
101} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java
new file mode 100644
index 00000000..7cb312bd
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java
@@ -0,0 +1,37 @@
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.matchers.psystem.queries;
10
11/**
12 * @author Zoltan Ujhelyi
13 * @since 2.0
14 *
15 */
16public enum PVisibility {
17
18 /**
19 * A public (default) visibility means a pattern can be called at any time.
20 */
21 PUBLIC,
22 /**
23 * A private query is not expected to be called directly, only by a different query matcher.
24 */
25 PRIVATE,
26 /**
27 * A query that is only used inside a single caller query and is not visible outside its container query. Such
28 * patterns must also fulfill the following additional constraints:
29 *
30 * <ul>
31 * <li>An embedded query must have only a single body.</li>
32 * <li>An embedded query must not be recursice.</li>
33 * </ul>
34 */
35 EMBEDDED
36
37}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java
new file mode 100644
index 00000000..470d7287
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java
@@ -0,0 +1,35 @@
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.matchers.psystem.queries;
10
11import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
12
13/**
14 * Represent an exception that occurred while initializing the specification of a query.
15 * @author Bergmann Gabor
16 * @since 0.9
17 *
18 */
19public class QueryInitializationException extends QueryProcessingException {
20
21 public QueryInitializationException(String message, String[] context, String shortMessage, Object patternDescription,
22 Throwable cause) {
23 super(message, context, shortMessage, patternDescription, cause);
24 }
25
26 public QueryInitializationException(String message, String[] context, String shortMessage, Object patternDescription) {
27 super(message, context, shortMessage, patternDescription);
28 }
29
30 private static final long serialVersionUID = 9106033062252951489L;
31
32
33
34
35}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java
new file mode 100644
index 00000000..276b2b42
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java
@@ -0,0 +1,53 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.matchers.psystem.rewriters;
10
11import java.util.Objects;
12
13import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
14import tools.refinery.viatra.runtime.matchers.psystem.PTraceable;
15
16/**
17 * @since 1.6
18 *
19 */
20public class AbstractRewriterTraceSource {
21
22 private IRewriterTraceCollector traceCollector = NopTraceCollector.INSTANCE;
23
24 public void setTraceCollector(IRewriterTraceCollector traceCollector) {
25 this.traceCollector = Objects.requireNonNull(traceCollector);
26 }
27
28 public IPTraceableTraceProvider getTraces() {
29 return traceCollector;
30 }
31
32 protected IRewriterTraceCollector getTraceCollector() {
33 return traceCollector;
34 }
35
36 /**
37 * Mark the given derivative to be originated from the given original constraint.
38 * @since 1.6
39 */
40 protected void addTrace(PTraceable original, PTraceable derivative){
41 traceCollector.addTrace(original, derivative);
42 }
43
44 /**
45 * Indicate that the given derivative is removed from the resulting query, thus its trace
46 * information should be removed also.
47 * @since 1.6
48 */
49 protected void derivativeRemoved(PConstraint derivative, IDerivativeModificationReason reason){
50 traceCollector.derivativeRemoved(derivative, reason);
51 }
52
53}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java
new file mode 100644
index 00000000..237a762d
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java
@@ -0,0 +1,23 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.matchers.psystem.rewriters;
10
11/**
12 * Common reasons for removing constraint through rewriters
13 *
14 * @noreference This enum is not intended to be referenced by clients.
15 */
16public enum ConstraintRemovalReason implements IDerivativeModificationReason {
17
18 MOOT_EQUALITY,
19 WEAK_INEQUALITY_SELF_LOOP,
20 TYPE_SUBSUMED,
21 DUPLICATE
22
23}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java
new file mode 100644
index 00000000..3b5d7390
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java
@@ -0,0 +1,23 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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.matchers.psystem.rewriters;
10import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
11
12/**
13 * @author Marton Bur
14 *
15 */
16public class DefaultFlattenCallPredicate implements IFlattenCallPredicate {
17
18 @Override
19 public boolean shouldFlatten(PositivePatternCall positivePatternCall) {
20 return true;
21 }
22
23}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java
new file mode 100644
index 00000000..06b8d372
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java
@@ -0,0 +1,129 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, 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.matchers.psystem.rewriters;
10
11import java.util.HashMap;
12import java.util.List;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Objects;
16import java.util.Set;
17import java.util.stream.Collectors;
18
19import tools.refinery.viatra.runtime.matchers.psystem.PBody;
20import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
21import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
22import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality;
23import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
24import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation;
25import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
26import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
27import tools.refinery.viatra.runtime.matchers.util.Preconditions;
28
29/**
30 * This rewriter class can add new equality constraints to the copied body
31 *
32 * @author Marton Bur
33 *
34 */
35class FlattenerCopier extends PBodyCopier {
36
37 private final Map<PositivePatternCall, CallInformation> calls;
38
39 private static class CallInformation {
40 final PBody body;
41 final Map<PVariable, PVariable> variableMapping;
42
43 private CallInformation(PBody body) {
44 this.body = body;
45 this.variableMapping = new HashMap<>();
46 }
47 }
48
49 public FlattenerCopier(PQuery query, Map<PositivePatternCall, PBody> callsToFlatten) {
50 super(query);
51 this.calls = callsToFlatten.entrySet().stream().collect(Collectors.toMap(Entry::getKey, entry -> new CallInformation(entry.getValue())));
52 }
53
54 protected void copyVariable(PositivePatternCall contextPatternCall, PVariable variable, String newName) {
55 PVariable newPVariable = body.getOrCreateVariableByName(newName);
56 calls.get(contextPatternCall).variableMapping.put(variable, newPVariable);
57 variableMapping.put(variable, newPVariable);
58 }
59
60 /**
61 * Merge all variables and constraints from the body called through the given pattern call to a target body. If
62 * multiple bodies are merged into a single one, use the renamer and filter options to avoid collisions.
63 *
64 * @param sourceBody
65 * @param namingTool
66 * @param filter
67 */
68 public void mergeBody(PositivePatternCall contextPatternCall, IVariableRenamer namingTool,
69 IConstraintFilter filter) {
70
71 PBody sourceBody = calls.get(contextPatternCall).body;
72
73 // Copy variables
74 Set<PVariable> allVariables = sourceBody.getAllVariables();
75 for (PVariable pVariable : allVariables) {
76 if (pVariable.isUnique()) {
77 copyVariable(contextPatternCall, pVariable,
78 namingTool.createVariableName(pVariable, sourceBody.getPattern()));
79 }
80 }
81
82 // Copy constraints which are not filtered
83 Set<PConstraint> constraints = sourceBody.getConstraints();
84 for (PConstraint pConstraint : constraints) {
85 if (!(pConstraint instanceof ExportedParameter) && !filter.filter(pConstraint)) {
86 copyConstraint(pConstraint);
87 }
88 }
89 }
90
91 @Override
92 protected void copyPositivePatternCallConstraint(PositivePatternCall positivePatternCall) {
93
94 if (!calls.containsKey(positivePatternCall)) {
95 // If the call was not flattened, copy the constraint
96 super.copyPositivePatternCallConstraint(positivePatternCall);
97 } else {
98 PBody calledBody = Objects.requireNonNull(calls.get(positivePatternCall).body);
99 Preconditions.checkArgument(positivePatternCall.getReferredQuery().equals(calledBody.getPattern()));
100
101 List<PVariable> symbolicParameters = calledBody.getSymbolicParameterVariables();
102 Object[] elements = positivePatternCall.getVariablesTuple().getElements();
103 for (int i = 0; i < elements.length; i++) {
104 // Create equality constraints between the caller PositivePatternCall and the corresponding body
105 // parameter variables
106 createEqualityConstraint((PVariable) elements[i], symbolicParameters.get(i), positivePatternCall);
107 }
108
109 }
110 }
111
112 private void createEqualityConstraint(PVariable pVariable1, PVariable pVariable2,
113 PositivePatternCall contextPatternCall) {
114 PVariable who = variableMapping.get(pVariable1);
115 PVariable withWhom = calls.get(contextPatternCall).variableMapping.get(pVariable2);
116 addTrace(contextPatternCall, new Equality(body, who, withWhom));
117 }
118
119 @Override
120 protected void copyExpressionEvaluationConstraint(final ExpressionEvaluation expressionEvaluation) {
121 Map<PVariable, PVariable> variableMapping = this.variableMapping.entrySet().stream()
122 .filter(input -> expressionEvaluation.getPSystem().getAllVariables().contains(input.getKey()))
123 .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
124
125 PVariable mappedOutputVariable = variableMapping.get(expressionEvaluation.getOutputVariable());
126 addTrace(expressionEvaluation, new ExpressionEvaluation(body, new VariableMappingExpressionEvaluatorWrapper(expressionEvaluation.getEvaluator(), variableMapping), mappedOutputVariable, expressionEvaluation.isUnwinding()));
127 }
128
129}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java
new file mode 100644
index 00000000..518b9c64
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java
@@ -0,0 +1,48 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Zoltan Ujhelyi, Marton Bur, 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.matchers.psystem.rewriters;
10
11import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
12import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
13
14/**
15 * Helper interface to exclude constraints from PBody copy processes
16 *
17 * @author Marton Bur
18 *
19 */
20public interface IConstraintFilter {
21 /**
22 * Returns true, if the given constraint should be filtered (thus should not be copied)
23 *
24 * @param constraint
25 * to check
26 * @return true, if the constraint should be filtered
27 */
28 boolean filter(PConstraint constraint);
29
30 public static class ExportedParameterFilter implements IConstraintFilter {
31
32 @Override
33 public boolean filter(PConstraint constraint) {
34 return constraint instanceof ExportedParameter;
35 }
36
37 }
38
39 public static class AllowAllFilter implements IConstraintFilter {
40
41 @Override
42 public boolean filter(PConstraint constraint) {
43 // Nothing is filtered
44 return false;
45 }
46
47 }
48} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java
new file mode 100644
index 00000000..dbd6a78d
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java
@@ -0,0 +1,19 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.matchers.psystem.rewriters;
10
11/**
12 * This is a role indication interface, implementations may provide a reason about
13 * why a modification is made during PQuery normalization.
14 * @since 1.6
15 *
16 */
17public interface IDerivativeModificationReason {
18
19}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java
new file mode 100644
index 00000000..7e224e98
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java
@@ -0,0 +1,50 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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.matchers.psystem.rewriters;
10
11import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
12
13
14/**
15 * Interface used by the PQueryFlattener to decide which positive pattern calls to flatten
16 *
17 * @author Marton Bur
18 *
19 */
20public interface IFlattenCallPredicate {
21
22 /**
23 * Decides whether the called query by the pattern call should be flattened into the caller or not.
24 *
25 * @param positivePatternCall
26 * the pattern call
27 * @return true if the call should be flattened
28 */
29 boolean shouldFlatten(PositivePatternCall positivePatternCall);
30
31 /**
32 * Flattens only if all operand predicates vote for flattening.
33 * @author Gabor Bergmann
34 * @since 2.1
35 */
36 public static class And implements IFlattenCallPredicate {
37 private IFlattenCallPredicate[] operands;
38 public And(IFlattenCallPredicate... operands) {
39 this.operands = operands;
40 }
41
42 @Override
43 public boolean shouldFlatten(PositivePatternCall positivePatternCall) {
44 for (IFlattenCallPredicate operand : operands) {
45 if (!operand.shouldFlatten(positivePatternCall)) return false;
46 }
47 return true;
48 }
49 }
50}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java
new file mode 100644
index 00000000..84da4d1b
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java
@@ -0,0 +1,55 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.matchers.psystem.rewriters;
10
11import java.util.stream.Stream;
12
13import tools.refinery.viatra.runtime.matchers.psystem.PTraceable;
14import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
15
16/**
17 * This interface provides methods to trace the {@link PTraceable}s of a transformed {@link PQuery} produced by
18 * a {@link PDisjunctionRewriter}. In case the associated rewriter is a composite (a.k.a. {@link PDisjunctionRewriterCacher}),
19 * this trace provider handles traces end-to-end, hiding all the intermediate transformation steps.
20 *
21 * @since 1.6
22 * @noimplement This interface is not intended to be implemented by clients.
23 */
24public interface IPTraceableTraceProvider {
25
26 /**
27 * Find and return the canonical {@link PTraceable}s in the original query which are the sources of the given derivative
28 * {@link PTraceable} according to the transformation.
29 *
30 * @param derivative a {@link PTraceable} which is contained by the {@link PQuery} produced by the associated rewriter
31 * @since 2.0
32 */
33 public Stream<PTraceable> getCanonicalTraceables(PTraceable derivative);
34
35 /**
36 * Find and return the {@link PTraceable}s in the rewritten query which are the destinations of the given source
37 * {@link PTraceable} according to the transformation.
38 *
39 * @param source a {@link PTraceable} which is contained by a {@link PQuery} before rewriting
40 * @since 2.0
41 */
42 public Stream<PTraceable> getRewrittenTraceables(PTraceable source);
43
44 /**
45 * Returns whether the given traceable element has been removed by every rewriter for a reason.
46 */
47 public boolean isRemoved(PTraceable traceable);
48
49 /**
50 * Returns the reasons for which the traceable element has been removed by the rewriters.
51 * @return the reasons of removal during rewriting
52 * @since 2.0
53 */
54 public Stream<IDerivativeModificationReason> getRemovalReasons(PTraceable traceable);
55}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java
new file mode 100644
index 00000000..70771ea7
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java
@@ -0,0 +1,33 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.matchers.psystem.rewriters;
10
11import tools.refinery.viatra.runtime.matchers.psystem.PTraceable;
12
13/**
14 * This is the internal API of {@link IPTraceableTraceProvider} expected to be used by
15 * copier and rewriter implementations.
16 *
17 * @since 1.6
18 * @noreference This interface is not intended to be referenced by clients.
19 */
20public interface IRewriterTraceCollector extends IPTraceableTraceProvider {
21
22 /**
23 * Mark the given derivative to be originated from the given original constraint.
24 */
25 public void addTrace(PTraceable origin, PTraceable derivative);
26
27 /**
28 * Indicate that the given derivative is removed from the resulting query, thus its trace
29 * information should be removed also.
30 */
31 public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason);
32
33}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java
new file mode 100644
index 00000000..ce446e0d
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java
@@ -0,0 +1,59 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Zoltan Ujhelyi, Marton Bur, 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.matchers.psystem.rewriters;
10
11import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
12import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
13
14/**
15 * Helper interface to ease the naming of the new variables during flattening
16 *
17 * @author Marton Bur
18 *
19 */
20public interface IVariableRenamer {
21 /**
22 * Creates a variable name based on a given variable and a given query. It only creates a String, doesn't set
23 * anything.
24 *
25 * @param pVariable
26 * @param query
27 * @return the new variable name as a String
28 */
29 String createVariableName(PVariable pVariable, PQuery query);
30
31 public class SameName implements IVariableRenamer {
32 @Override
33 public String createVariableName(PVariable pVariable, PQuery query) {
34 return pVariable.getName();
35 }
36 }
37
38 public class HierarchicalName implements IVariableRenamer {
39
40 private int callCount;
41
42 public void setCallCount(int callCount) {
43 this.callCount = callCount;
44 }
45
46 @Override
47 public String createVariableName(PVariable pVariable, PQuery query) {
48 // make sure to keep the "_" prefix before anonymous variables
49 String newVarName = getShortName(query) + "<" + callCount + ">" + "_" + pVariable.getName();
50 return pVariable.getName().startsWith("_") ? "_" + newVarName : newVarName ;
51 }
52
53 private String getShortName(PQuery query) {
54 String fullyQualifiedName = query.getFullyQualifiedName();
55 int beginIndex = fullyQualifiedName.lastIndexOf('.') + 1;
56 return fullyQualifiedName.substring(beginIndex);
57 }
58 }
59} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java
new file mode 100644
index 00000000..7429fc60
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java
@@ -0,0 +1,135 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.matchers.psystem.rewriters;
10
11import java.util.Collections;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.LinkedList;
15import java.util.Map;
16import java.util.Queue;
17import java.util.Set;
18import java.util.function.Predicate;
19import java.util.stream.Stream;
20
21import tools.refinery.viatra.runtime.matchers.psystem.PTraceable;
22import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
23import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
24import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
25import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
26import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
27import tools.refinery.viatra.runtime.matchers.util.Preconditions;
28
29/**
30 * Multimap-based implementation to contain and query traces
31 *
32 * @since 1.6
33 *
34 */
35public class MappingTraceCollector implements IRewriterTraceCollector {
36
37 /**
38 * Traces from derivative to original
39 */
40 private final IMultiLookup<PTraceable, PTraceable> traces = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class);
41
42 /**
43 * Traces from original to derivative
44 */
45 private final IMultiLookup<PTraceable, PTraceable> inverseTraces = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class);
46
47 /**
48 * Reasons for removing {@link PTraceable}s
49 */
50 private final Map<PTraceable, IDerivativeModificationReason> removals = new HashMap<>();
51
52 /**
53 * Decides whether {@link PTraceable} is removed
54 */
55 private final Predicate<PTraceable> removed = removals::containsKey;
56
57 /**
58 * @since 2.0
59 */
60 @Override
61 public Stream<PTraceable> getCanonicalTraceables(PTraceable derivative) {
62 return findTraceEnds(derivative, traces).stream();
63 }
64
65 /**
66 * @since 2.0
67 */
68 @Override
69 public Stream<PTraceable> getRewrittenTraceables(PTraceable source) {
70 return findTraceEnds(source, inverseTraces).stream();
71 }
72
73 /**
74 * Returns the end of trace chains starting from the given {@link PTraceable} along the given trace edges.
75 */
76 private Set<PTraceable> findTraceEnds(PTraceable traceable, IMultiLookup<PTraceable, PTraceable> traceRecords) {
77 if (traceable instanceof PQuery) { // PQueries are preserved
78 return Collections.singleton(traceable);
79 }
80 Set<PTraceable> visited = new HashSet<>();
81 Set<PTraceable> result = new HashSet<>();
82 Queue<PTraceable> queue = new LinkedList<>();
83 queue.add(traceable);
84 while(!queue.isEmpty()){
85 PTraceable aDerivative = queue.poll();
86 // Track visited elements to avoid infinite loop via directed cycles in traces
87 visited.add(aDerivative);
88 IMemoryView<PTraceable> nextOrigins = traceRecords.lookup(aDerivative);
89 if (nextOrigins == null){
90 // End of trace chain
91 result.add(aDerivative);
92 } else {
93 // Follow traces
94 for(PTraceable nextOrigin : nextOrigins){
95 if (!visited.contains(nextOrigin)){
96 queue.add(nextOrigin);
97 }
98 }
99 }
100 }
101 return result;
102 }
103
104 @Override
105 public void addTrace(PTraceable original, PTraceable derivative){
106 traces.addPairOrNop(derivative, original);
107 inverseTraces.addPairOrNop(original, derivative);
108 // Even if this element was marked as removed earlier, now we replace it with another constraint!
109 removals.remove(original);
110 }
111
112 @Override
113 public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason){
114 Preconditions.checkState(!removals.containsKey(derivative), "Traceable %s removed multiple times", derivative);
115 // XXX the derivative must not be removed from the trace chain, as some rewriters, e.g. the normalizer keeps trace links to deleted elements
116 if (!inverseTraces.lookupExists(derivative)) {
117 // If there already exists a trace link, this removal means an update
118 removals.put(derivative, reason);
119 }
120 }
121
122 @Override
123 public boolean isRemoved(PTraceable traceable) {
124 return getRewrittenTraceables(traceable).allMatch(removed);
125 }
126
127 /**
128 * @since 2.0
129 */
130 @Override
131 public Stream<IDerivativeModificationReason> getRemovalReasons(PTraceable traceable) {
132 return getRewrittenTraceables(traceable).filter(removed).map(removals::get);
133 }
134
135}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java
new file mode 100644
index 00000000..96c0b205
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java
@@ -0,0 +1,26 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.matchers.psystem.rewriters;
10
11import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
12
13/**
14 * @author Grill Balázs
15 * @since 1.4
16 *
17 */
18public class NeverFlattenCallPredicate implements IFlattenCallPredicate {
19
20
21 @Override
22 public boolean shouldFlatten(PositivePatternCall positivePatternCall) {
23 return false;
24 }
25
26}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java
new file mode 100644
index 00000000..15cf577e
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java
@@ -0,0 +1,68 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.matchers.psystem.rewriters;
10
11import java.util.stream.Stream;
12
13import tools.refinery.viatra.runtime.matchers.psystem.PTraceable;
14
15/**
16 * This implementation does not store any traces and scales to NOP for every traceability feature.
17 * @since 1.6
18 *
19 */
20public class NopTraceCollector implements IRewriterTraceCollector {
21
22 public static final IRewriterTraceCollector INSTANCE = new NopTraceCollector();
23
24 private NopTraceCollector() {
25 // Private constructor to force using the common instance
26 }
27
28 /**
29 * @since 2.0
30 */
31 @Override
32 public Stream<PTraceable> getCanonicalTraceables(PTraceable derivative) {
33 return Stream.empty();
34 }
35
36 /**
37 * @since 2.0
38 */
39 @Override
40 public Stream<PTraceable> getRewrittenTraceables(PTraceable source) {
41 return Stream.empty();
42 }
43
44
45 @Override
46 public void addTrace(PTraceable origin, PTraceable derivative) {
47 // ignored
48 }
49
50 @Override
51 public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason) {
52 // ignored
53 }
54
55 @Override
56 public boolean isRemoved(PTraceable traceable) {
57 return false;
58 }
59
60 /**
61 * @since 2.0
62 */
63 @Override
64 public Stream<IDerivativeModificationReason> getRemovalReasons(PTraceable traceable) {
65 return Stream.empty();
66 }
67
68}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java
new file mode 100644
index 00000000..e66c4eea
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java
@@ -0,0 +1,306 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, 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.matchers.psystem.rewriters;
10
11import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
12import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint;
13import tools.refinery.viatra.runtime.matchers.psystem.PBody;
14import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
15import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
16import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.*;
17import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.*;
18import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
19import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
20import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.AllowAllFilter;
21import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.SameName;
22import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
23import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
24
25import java.util.*;
26import java.util.stream.Collectors;
27
28/**
29 * This class can create a new PBody for a PQuery. The result body contains a copy of given variables and constraints.
30 *
31 * @author Marton Bur
32 *
33 */
34public class PBodyCopier extends AbstractRewriterTraceSource {
35
36 /**
37 * The created body
38 */
39 protected PBody body;
40 /**
41 * Mapping between the original and the copied variables
42 */
43 protected Map<PVariable, PVariable> variableMapping = new HashMap<>();
44
45 public Map<PVariable, PVariable> getVariableMapping() {
46 return variableMapping;
47 }
48
49 /**
50 * @since 1.6
51 */
52 public PBodyCopier(PBody body, IRewriterTraceCollector traceCollector) {
53 this.body = new PBody(body.getPattern());
54 setTraceCollector(traceCollector);
55
56 // do the actual copying
57 mergeBody(body);
58 }
59
60 /**
61 * @since 1.6
62 */
63 public PBodyCopier(PQuery query) {
64 this.body = new PBody(query);
65 }
66
67 public void mergeBody(PBody sourceBody) {
68 mergeBody(sourceBody, new SameName(), new AllowAllFilter());
69 }
70
71 /**
72 * Merge all variables and constraints from a source body to a target body. If multiple bodies are merged into a
73 * single one, use the renamer and filter options to avoid collisions.
74 */
75 public void mergeBody(PBody sourceBody, IVariableRenamer namingTool, IConstraintFilter filter) {
76
77 // Copy variables
78 Set<PVariable> allVariables = sourceBody.getAllVariables();
79 for (PVariable pVariable : allVariables) {
80 if (pVariable.isUnique()) {
81 copyVariable(pVariable, namingTool.createVariableName(pVariable, sourceBody.getPattern()));
82 }
83 }
84
85 // Copy exported parameters
86 this.body.setSymbolicParameters(sourceBody.getSymbolicParameters().stream()
87 .map(this::copyExportedParameterConstraint).collect(Collectors.toList()));
88
89 // Copy constraints which are not filtered
90 Set<PConstraint> constraints = sourceBody.getConstraints();
91 for (PConstraint pConstraint : constraints) {
92 if (!(pConstraint instanceof ExportedParameter) && !filter.filter(pConstraint)) {
93 copyConstraint(pConstraint);
94 }
95 }
96
97 // Add trace between original and copied body
98 addTrace(sourceBody, body);
99 }
100
101 protected void copyVariable(PVariable variable, String newName) {
102 PVariable newPVariable = body.getOrCreateVariableByName(newName);
103 variableMapping.put(variable, newPVariable);
104 }
105
106 /**
107 * Returns the body with the copied variables and constraints. The returned body is still uninitialized.
108 */
109 public PBody getCopiedBody() {
110 return body;
111 }
112
113 protected void copyConstraint(PConstraint constraint) {
114 if (constraint instanceof ExportedParameter) {
115 copyExportedParameterConstraint((ExportedParameter) constraint);
116 } else if (constraint instanceof Equality) {
117 copyEqualityConstraint((Equality) constraint);
118 } else if (constraint instanceof Inequality) {
119 copyInequalityConstraint((Inequality) constraint);
120 } else if (constraint instanceof TypeConstraint) {
121 copyTypeConstraint((TypeConstraint) constraint);
122 } else if (constraint instanceof TypeFilterConstraint) {
123 copyTypeFilterConstraint((TypeFilterConstraint) constraint);
124 } else if (constraint instanceof ConstantValue) {
125 copyConstantValueConstraint((ConstantValue) constraint);
126 } else if (constraint instanceof PositivePatternCall) {
127 copyPositivePatternCallConstraint((PositivePatternCall) constraint);
128 } else if (constraint instanceof NegativePatternCall) {
129 copyNegativePatternCallConstraint((NegativePatternCall) constraint);
130 } else if (constraint instanceof BinaryTransitiveClosure) {
131 copyBinaryTransitiveClosureConstraint((BinaryTransitiveClosure) constraint);
132 } else if (constraint instanceof RepresentativeElectionConstraint) {
133 copyRepresentativeElectionConstraint((RepresentativeElectionConstraint) constraint);
134 } else if (constraint instanceof RelationEvaluation) {
135 copyRelationEvaluationConstraint((RelationEvaluation) constraint);
136 } else if (constraint instanceof BinaryReflexiveTransitiveClosure) {
137 copyBinaryReflexiveTransitiveClosureConstraint((BinaryReflexiveTransitiveClosure) constraint);
138 } else if (constraint instanceof PatternMatchCounter) {
139 copyPatternMatchCounterConstraint((PatternMatchCounter) constraint);
140 } else if (constraint instanceof AggregatorConstraint) {
141 copyAggregatorConstraint((AggregatorConstraint) constraint);
142 } else if (constraint instanceof ExpressionEvaluation) {
143 copyExpressionEvaluationConstraint((ExpressionEvaluation) constraint);
144 } else {
145 throw new QueryProcessingException("Unknown PConstraint {0} encountered while copying PBody",
146 new String[] { constraint.getClass().getName() }, "Unknown PConstraint", body.getPattern());
147 }
148 }
149
150 protected ExportedParameter copyExportedParameterConstraint(ExportedParameter exportedParameter) {
151 PVariable mappedPVariable = variableMapping.get(exportedParameter.getParameterVariable());
152 PParameter parameter = exportedParameter.getPatternParameter();
153 ExportedParameter newExportedParameter;
154 newExportedParameter = new ExportedParameter(body, mappedPVariable, parameter);
155 body.getSymbolicParameters().add(newExportedParameter);
156 addTrace(exportedParameter, newExportedParameter);
157 return newExportedParameter;
158 }
159
160 protected void copyEqualityConstraint(Equality equality) {
161 PVariable who = equality.getWho();
162 PVariable withWhom = equality.getWithWhom();
163 addTrace(equality, new Equality(body, variableMapping.get(who), variableMapping.get(withWhom)));
164 }
165
166 protected void copyInequalityConstraint(Inequality inequality) {
167 PVariable who = inequality.getWho();
168 PVariable withWhom = inequality.getWithWhom();
169 addTrace(inequality, new Inequality(body, variableMapping.get(who), variableMapping.get(withWhom)));
170 }
171
172 protected void copyTypeConstraint(TypeConstraint typeConstraint) {
173 PVariable[] mappedVariables = extractMappedVariables(typeConstraint);
174 Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables);
175 addTrace(typeConstraint, new TypeConstraint(body, variablesTuple, typeConstraint.getSupplierKey()));
176 }
177
178 protected void copyTypeFilterConstraint(TypeFilterConstraint typeConstraint) {
179 PVariable[] mappedVariables = extractMappedVariables(typeConstraint);
180 Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables);
181 addTrace(typeConstraint, new TypeFilterConstraint(body, variablesTuple, typeConstraint.getInputKey()));
182 }
183
184 protected void copyConstantValueConstraint(ConstantValue constantValue) {
185 PVariable pVariable = (PVariable) constantValue.getVariablesTuple().getElements()[0];
186 addTrace(constantValue,
187 new ConstantValue(body, variableMapping.get(pVariable), constantValue.getSupplierKey()));
188 }
189
190 protected void copyPositivePatternCallConstraint(PositivePatternCall positivePatternCall) {
191 PVariable[] mappedVariables = extractMappedVariables(positivePatternCall);
192 Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables);
193 addTrace(positivePatternCall,
194 new PositivePatternCall(body, variablesTuple, positivePatternCall.getReferredQuery()));
195 }
196
197 protected void copyNegativePatternCallConstraint(NegativePatternCall negativePatternCall) {
198 PVariable[] mappedVariables = extractMappedVariables(negativePatternCall);
199 Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables);
200 addTrace(negativePatternCall,
201 new NegativePatternCall(body, variablesTuple, negativePatternCall.getReferredQuery()));
202 }
203
204 protected void copyBinaryTransitiveClosureConstraint(BinaryTransitiveClosure binaryTransitiveClosure) {
205 PVariable[] mappedVariables = extractMappedVariables(binaryTransitiveClosure);
206 Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables);
207 addTrace(binaryTransitiveClosure,
208 new BinaryTransitiveClosure(body, variablesTuple, binaryTransitiveClosure.getReferredQuery()));
209 }
210
211 protected void copyRepresentativeElectionConstraint(RepresentativeElectionConstraint constraint) {
212 var mappedVariables = extractMappedVariables(constraint);
213 var variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables);
214 addTrace(constraint, new RepresentativeElectionConstraint(body, variablesTuple, constraint.getReferredQuery(),
215 constraint.getConnectivity()));
216 }
217
218 /**
219 * @since 2.8
220 */
221 protected void copyRelationEvaluationConstraint(RelationEvaluation relationEvaluation) {
222 PVariable[] mappedVariables = extractMappedVariables(relationEvaluation);
223 Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables);
224 addTrace(relationEvaluation, new RelationEvaluation(body, variablesTuple, relationEvaluation.getReferredQueries(),
225 relationEvaluation.getEvaluator()));
226 }
227
228 /**
229 * @since 2.0
230 */
231 protected void copyBinaryReflexiveTransitiveClosureConstraint(
232 BinaryReflexiveTransitiveClosure binaryReflexiveTransitiveClosure) {
233 PVariable[] mappedVariables = extractMappedVariables(binaryReflexiveTransitiveClosure);
234 Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables);
235 addTrace(binaryReflexiveTransitiveClosure,
236 new BinaryReflexiveTransitiveClosure(body, variablesTuple,
237 binaryReflexiveTransitiveClosure.getReferredQuery(),
238 binaryReflexiveTransitiveClosure.getUniverseType()));
239 }
240
241 protected void copyPatternMatchCounterConstraint(PatternMatchCounter patternMatchCounter) {
242 PVariable[] mappedVariables = extractMappedVariables(patternMatchCounter);
243 PVariable mappedResultVariable = variableMapping.get(patternMatchCounter.getResultVariable());
244 Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables);
245 addTrace(patternMatchCounter, new PatternMatchCounter(body, variablesTuple,
246 patternMatchCounter.getReferredQuery(), mappedResultVariable));
247 }
248
249 /**
250 * @since 1.4
251 */
252 protected void copyAggregatorConstraint(AggregatorConstraint constraint) {
253 PVariable[] mappedVariables = extractMappedVariables(constraint);
254 PVariable mappedResultVariable = variableMapping.get(constraint.getResultVariable());
255 Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables);
256 addTrace(constraint, new AggregatorConstraint(constraint.getAggregator(), body, variablesTuple,
257 constraint.getReferredQuery(), mappedResultVariable, constraint.getAggregatedColumn()));
258 }
259
260 protected void copyExpressionEvaluationConstraint(ExpressionEvaluation expressionEvaluation) {
261 PVariable mappedOutputVariable = variableMapping.get(expressionEvaluation.getOutputVariable());
262 addTrace(expressionEvaluation, new ExpressionEvaluation(body,
263 new VariableMappingExpressionEvaluatorWrapper(expressionEvaluation.getEvaluator(), variableMapping),
264 mappedOutputVariable, expressionEvaluation.isUnwinding()));
265 }
266
267 /**
268 * For positive pattern calls
269 *
270 * @param positivePatternCall
271 * @return the mapped variables to the pattern's parameters
272 */
273 protected PVariable[] extractMappedVariables(EnumerablePConstraint enumerablePConstraint) {
274 Object[] pVariables = enumerablePConstraint.getVariablesTuple().getElements();
275 return mapVariableList(pVariables);
276 }
277
278 /**
279 * For negative and count pattern calls.
280 *
281 * @param patternMatchCounter
282 * @return the mapped variables to the pattern's parameters
283 */
284 private PVariable[] extractMappedVariables(PatternCallBasedDeferred patternCallBasedDeferred) {
285 Object[] pVariables = patternCallBasedDeferred.getActualParametersTuple().getElements();
286 return mapVariableList(pVariables);
287 }
288
289 /**
290 * For type filters.
291 */
292 private PVariable[] extractMappedVariables(TypeFilterConstraint typeFilterConstraint) {
293 Object[] pVariables = typeFilterConstraint.getVariablesTuple().getElements();
294 return mapVariableList(pVariables);
295 }
296
297 private PVariable[] mapVariableList(Object[] pVariables) {
298 List<PVariable> list = new ArrayList<PVariable>();
299 for (int i = 0; i < pVariables.length; i++) {
300 PVariable mappedVariable = variableMapping.get(pVariables[i]);
301 list.add(mappedVariable);
302 }
303 return list.toArray(new PVariable[0]);
304 }
305
306}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java
new file mode 100644
index 00000000..90943129
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java
@@ -0,0 +1,310 @@
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.matchers.psystem.rewriters;
11
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.Comparator;
16import java.util.HashMap;
17import java.util.HashSet;
18import java.util.Iterator;
19import java.util.LinkedHashSet;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.Map;
23import java.util.Queue;
24import java.util.Set;
25
26import tools.refinery.viatra.runtime.matchers.context.IInputKey;
27import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
28import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
29import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper;
30import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint;
31import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint;
32import tools.refinery.viatra.runtime.matchers.psystem.PBody;
33import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
34import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement;
35import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality;
36import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality;
37import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction;
38import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
39import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus;
40import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
41
42/**
43 * A disjunction rewriter for creating a normalized form of specification, unifying variables and running basic sanity
44 * checks. This rewriter does not copy but modifies directly the original specification, requiring a mutable
45 * disjunction.
46 *
47 * @author Gabor Bergmann
48 *
49 */
50public class PBodyNormalizer extends PDisjunctionRewriter {
51
52 private IQueryMetaContext context;
53
54 public PBodyNormalizer(IQueryMetaContext context) {
55 this.context = context;
56 }
57
58 /**
59 * Returns whether unary constraint elimination is enabled. This behavior can be customized by creating a subclass
60 * with a custom implementation.
61 *
62 * @since 1.6
63 */
64 protected boolean shouldCalculateImpliedTypes(PQuery query) {
65 return true;
66 }
67
68 /**
69 * Returns whether 'weakened alternative' suggestions of the context shall be expanded as additional PConstraints.
70 * This behavior can be customized by creating a subclass
71 * with a custom implementation.
72 *
73 * @since 1.6
74 */
75 protected boolean shouldExpandWeakenedAlternatives(PQuery query) {
76 return false;
77 }
78
79 @Override
80 public PDisjunction rewrite(PDisjunction disjunction) {
81 Set<PBody> normalizedBodies = new LinkedHashSet<>();
82 for (PBody body : disjunction.getBodies()) {
83 PBodyCopier copier = new PBodyCopier(body, getTraceCollector());
84 PBody modifiedBody = copier.getCopiedBody();
85 normalizeBody(modifiedBody);
86 normalizedBodies.add(modifiedBody);
87 modifiedBody.setStatus(PQueryStatus.OK);
88 }
89 return new PDisjunction(normalizedBodies);
90 }
91
92 public void setContext(IQueryMetaContext context) {
93 this.context = context;
94 }
95
96 /**
97 * Provides a normalized version of the pattern body. May return a different version than the original version if
98 * needed.
99 *
100 * @param body
101 */
102 public PBody normalizeBody(PBody body) {
103 try {
104 return normalizeBodyInternal(body);
105 } catch (QueryProcessingException e) {
106 throw new RewriterException("Error during rewriting: {1}", new String[] { e.getMessage() },
107 e.getShortMessage(), body.getPattern(), e);
108 }
109 }
110
111 PBody normalizeBodyInternal(PBody body) {
112 // UNIFICATION AND WEAK INEQUALITY ELIMINATION
113 unifyVariablesAlongEqualities(body);
114 eliminateWeakInequalities(body);
115 removeMootEqualities(body);
116
117 // ADDING WEAKENED ALTERNATIVES
118 if (shouldExpandWeakenedAlternatives(body.getPattern())) {
119 expandWeakenedAlternativeConstraints(body);
120 }
121
122 // CONSTRAINT ELIMINATION WITH TYPE INFERENCE
123 if (shouldCalculateImpliedTypes(body.getPattern())) {
124 eliminateInferrableTypes(body, context);
125 } else {
126 // ELIMINATE DUPLICATE TYPE CONSTRAINTS
127 eliminateDuplicateTypeConstraints(body);
128 }
129
130
131 // PREVENTIVE CHECKS
132 checkSanity(body);
133 return body;
134 }
135
136 private void removeMootEqualities(PBody body) {
137 Set<Equality> equals = body.getConstraintsOfType(Equality.class);
138 for (Equality equality : equals) {
139 if (equality.isMoot()) {
140 equality.delete();
141 derivativeRemoved(equality, ConstraintRemovalReason.MOOT_EQUALITY);
142 }
143 }
144 }
145
146 /**
147 * Unifies allVariables along equalities so that they can be handled as one.
148 *
149 * @param body
150 */
151 void unifyVariablesAlongEqualities(PBody body) {
152 Set<Equality> equals = body.getConstraintsOfType(Equality.class);
153 for (Equality equality : equals) {
154 if (!equality.isMoot()) {
155 equality.getWho().unifyInto(equality.getWithWhom());
156 }
157 }
158 }
159
160 /**
161 * Eliminates weak inequalities if they are not substantiated.
162 *
163 * @param body
164 */
165 void eliminateWeakInequalities(PBody body) {
166 for (Inequality inequality : body.getConstraintsOfType(Inequality.class)){
167 if (inequality.isEliminable()){
168 inequality.eliminateWeak();
169 derivativeRemoved(inequality, ConstraintRemovalReason.WEAK_INEQUALITY_SELF_LOOP);
170 }
171 }
172 }
173
174 /**
175 * Eliminates all type constraints that are inferrable from other constraints.
176 */
177 void eliminateInferrableTypes(final PBody body, IQueryMetaContext context) {
178 Set<TypeJudgement> subsumedByRetainedConstraints = new HashSet<TypeJudgement>();
179 LinkedList<ITypeConstraint> allTypeConstraints = new LinkedList<ITypeConstraint>();
180 for (PConstraint pConstraint : body.getConstraints()) {
181 if (pConstraint instanceof ITypeConstraint) {
182 allTypeConstraints.add((ITypeConstraint) pConstraint);
183 } else if (pConstraint instanceof ITypeInfoProviderConstraint) {
184 // non-type constraints are all retained
185 final Set<TypeJudgement> directJudgements = ((ITypeInfoProviderConstraint) pConstraint)
186 .getImpliedJudgements(context);
187 subsumedByRetainedConstraints = TypeHelper.typeClosure(subsumedByRetainedConstraints, directJudgements,
188 context);
189 }
190 }
191 Comparator<ITypeConstraint> eliminationOrder = (o1, o2) -> {
192 IInputKey type1 = o1.getEquivalentJudgement().getInputKey();
193 IInputKey type2 = o2.getEquivalentJudgement().getInputKey();
194
195 int result = context.getSuggestedEliminationOrdering().compare(type1, type2);
196 return (result == 0)
197 ? PConstraint.COMPARE_BY_MONOTONOUS_ID.compare(o1, o2)
198 : result;
199 };
200
201 Collections.sort(allTypeConstraints, eliminationOrder);
202 Queue<ITypeConstraint> potentialConstraints = allTypeConstraints; // rename for better comprehension
203
204 while (!potentialConstraints.isEmpty()) {
205 ITypeConstraint candidate = potentialConstraints.poll();
206
207 boolean isSubsumed = subsumedByRetainedConstraints.contains(candidate.getEquivalentJudgement());
208 if (!isSubsumed) {
209 Set<TypeJudgement> typeClosure = subsumedByRetainedConstraints;
210 for (ITypeConstraint subsuming : potentialConstraints) { // the remaining ones
211 final Set<TypeJudgement> directJudgements = subsuming.getImpliedJudgements(context);
212 typeClosure = TypeHelper.typeClosure(typeClosure, directJudgements, context);
213
214 if (typeClosure.contains(candidate.getEquivalentJudgement())) {
215 isSubsumed = true;
216 break;
217 }
218 }
219 }
220 if (isSubsumed) { // eliminated
221 candidate.delete();
222 derivativeRemoved(candidate, ConstraintRemovalReason.TYPE_SUBSUMED);
223 } else { // retained
224 subsumedByRetainedConstraints = TypeHelper.typeClosure(subsumedByRetainedConstraints,
225 candidate.getImpliedJudgements(context), context);
226 }
227 }
228 }
229
230 /**
231 * Inserts "weakened alternative" constraints suggested by the meta context that aid in coming up with a query plan.
232 */
233 void expandWeakenedAlternativeConstraints(PBody body) {
234 Set<TypeJudgement> allJudgements = new HashSet<TypeJudgement>();
235 Set<TypeJudgement> newJudgementsToAdd = new HashSet<TypeJudgement>();
236 Queue<TypeJudgement> judgementsToProcess = new LinkedList<TypeJudgement>();
237 Map<TypeJudgement, List<PConstraint>> traceability = CollectionsFactory.createMap();
238
239 for (ITypeConstraint typeConstraint : body.getConstraintsOfType(ITypeConstraint.class)) {
240 TypeJudgement equivalentJudgement = typeConstraint.getEquivalentJudgement();
241 judgementsToProcess.add(equivalentJudgement);
242 allJudgements.add(equivalentJudgement);
243 traceability.computeIfAbsent(equivalentJudgement, k-> new ArrayList<>()).add(typeConstraint);
244 }
245
246 while (!judgementsToProcess.isEmpty()) {
247 TypeJudgement judgement = judgementsToProcess.poll();
248 for (TypeJudgement alternativeJudgement : judgement.getWeakenedAlternativeJudgements(context)) {
249 if (allJudgements.add(alternativeJudgement)) {
250 newJudgementsToAdd.add(alternativeJudgement);
251 judgementsToProcess.add(alternativeJudgement);
252 traceability.merge(
253 alternativeJudgement,
254 traceability.getOrDefault(judgement, new ArrayList<>()),
255 (old,further) -> {old.addAll(further); return old;}
256 );
257 }
258 }
259 }
260
261 for (TypeJudgement typeJudgement : newJudgementsToAdd) {
262 PConstraint newConstraint = typeJudgement.createConstraintFor(body);
263 for (PConstraint source : traceability.getOrDefault(typeJudgement, Collections.emptyList())) {
264 addTrace(source, newConstraint);
265 }
266 }
267 }
268
269 private Object getConstraintKey(PConstraint constraint) {
270 if (constraint instanceof ITypeConstraint) {
271 return ((ITypeConstraint) constraint).getEquivalentJudgement();
272 }
273 // Do not check duplication for any other types
274 return constraint;
275 }
276
277 void eliminateDuplicateTypeConstraints(PBody body) {
278 Map<Object, PConstraint> constraints = new HashMap<>();
279 for (PConstraint constraint : body.getConstraints()) {
280 Object key = getConstraintKey(constraint);
281 // Retain first found instance of a constraint
282 if (!constraints.containsKey(key)) {
283 constraints.put(key, constraint);
284 }
285 }
286
287 // Retain collected constraints, remove everything else
288 Iterator<PConstraint> iterator = body.getConstraints().iterator();
289 Collection<PConstraint> toRetain = constraints.values();
290 while(iterator.hasNext()){
291 PConstraint next = iterator.next();
292 if (!toRetain.contains(next)){
293 derivativeRemoved(next, ConstraintRemovalReason.DUPLICATE);
294 iterator.remove();
295 }
296 }
297 }
298
299 /**
300 * Verifies the sanity of all constraints. Should be issued as a preventive check before layouting.
301 *
302 * @param body
303 * @throws RetePatternBuildException
304 */
305 void checkSanity(PBody body) {
306 for (PConstraint pConstraint : body.getConstraints())
307 pConstraint.checkSanity();
308 }
309
310}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java
new file mode 100644
index 00000000..c844ccf7
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java
@@ -0,0 +1,27 @@
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.matchers.psystem.rewriters;
10
11import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction;
12import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
13
14/**
15 * An abstract base class for creating alternative representations for PDisjunctions.
16 * @author Zoltan Ujhelyi
17 *
18 */
19public abstract class PDisjunctionRewriter extends AbstractRewriterTraceSource{
20
21 public abstract PDisjunction rewrite(PDisjunction disjunction);
22
23 public PDisjunction rewrite(PQuery query) {
24 return rewrite(query.getDisjunctBodies());
25 }
26
27}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java
new file mode 100644
index 00000000..eb5422ca
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java
@@ -0,0 +1,64 @@
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.matchers.psystem.rewriters;
10
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collections;
14import java.util.List;
15import java.util.WeakHashMap;
16
17import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction;
18
19/**
20 * A rewriter that stores the previously computed results of a rewriter or a rewriter chain.
21 *
22 * @author Zoltan Ujhelyi
23 * @since 1.0
24 */
25public class PDisjunctionRewriterCacher extends PDisjunctionRewriter {
26
27 private final List<PDisjunctionRewriter> rewriterChain;
28 private WeakHashMap<PDisjunction, PDisjunction> cachedResults =
29 new WeakHashMap<PDisjunction, PDisjunction>();
30
31 private void setupTraceCollectorInChain(){
32 IRewriterTraceCollector collector = getTraceCollector();
33 for(PDisjunctionRewriter rewriter: rewriterChain){
34 rewriter.setTraceCollector(collector);
35 }
36 }
37
38 public PDisjunctionRewriterCacher(PDisjunctionRewriter rewriter) {
39 rewriterChain = Collections.singletonList(rewriter);
40 }
41
42 public PDisjunctionRewriterCacher(PDisjunctionRewriter... rewriters) {
43 rewriterChain = new ArrayList<>(Arrays.asList(rewriters));
44 }
45
46 public PDisjunctionRewriterCacher(List<PDisjunctionRewriter> rewriterChain) {
47 this.rewriterChain = new ArrayList<>(rewriterChain);
48 }
49
50 @Override
51 public PDisjunction rewrite(PDisjunction disjunction) {
52 if (!cachedResults.containsKey(disjunction)) {
53 PDisjunction rewritten = disjunction;
54 setupTraceCollectorInChain();
55 for (PDisjunctionRewriter rewriter : rewriterChain) {
56 rewritten = rewriter.rewrite(rewritten);
57 }
58
59 cachedResults.put(disjunction, rewritten);
60 }
61 return cachedResults.get(disjunction);
62 }
63
64}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java
new file mode 100644
index 00000000..76311d8f
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java
@@ -0,0 +1,253 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, 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.matchers.psystem.rewriters;
10
11import java.util.ArrayDeque;
12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.Deque;
15import java.util.HashMap;
16import java.util.HashSet;
17import java.util.LinkedHashSet;
18import java.util.LinkedList;
19import java.util.List;
20import java.util.Map;
21import java.util.Set;
22
23import tools.refinery.viatra.runtime.matchers.psystem.PBody;
24import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
25import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
26import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction;
27import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
28import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus;
29import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.AllowAllFilter;
30import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.ExportedParameterFilter;
31import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.HierarchicalName;
32import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.SameName;
33import tools.refinery.viatra.runtime.matchers.util.Preconditions;
34import tools.refinery.viatra.runtime.matchers.util.Sets;
35
36/**
37 * This rewriter class holds the query flattening logic
38 *
39 * @author Marton Bur
40 *
41 */
42public class PQueryFlattener extends PDisjunctionRewriter {
43
44 /**
45 * Utility function to produce the permutation of every possible mapping of values.
46 *
47 * @param values
48 * @return
49 */
50 private static <K, V> Set<Map<K, V>> permutation(Map<K, Set<V>> values) {
51 // An ordering of keys is defined here which will help restoring the appropriate values after the execution of
52 // the cartesian product
53 List<K> keyList = new ArrayList<>(values.keySet());
54
55 // Produce list of value sets with the ordering defined by keyList
56 List<Set<V>> valuesList = new ArrayList<Set<V>>(keyList.size());
57 for (K key : keyList) {
58 valuesList.add(values.get(key));
59 }
60
61 // Cartesian product will obey ordering of the list
62 Set<List<V>> valueMappings = Sets.cartesianProduct(valuesList);
63
64 // Build result
65 Set<Map<K, V>> result = new LinkedHashSet<>();
66 for (List<V> valueList : valueMappings) {
67 Map<K, V> map = new HashMap<>();
68 for (int i = 0; i < keyList.size(); i++) {
69 map.put(keyList.get(i), valueList.get(i));
70 }
71 result.add(map);
72 }
73
74 return result;
75 }
76
77 private IFlattenCallPredicate flattenCallPredicate;
78
79 public PQueryFlattener(IFlattenCallPredicate flattenCallPredicate) {
80 this.flattenCallPredicate = flattenCallPredicate;
81 }
82
83 @Override
84 public PDisjunction rewrite(PDisjunction disjunction) {
85 PQuery query = disjunction.getQuery();
86
87 // Check for recursion
88 Set<PQuery> allReferredQueries = disjunction.getAllReferredQueries();
89 for (PQuery referredQuery : allReferredQueries) {
90 if (referredQuery.getAllReferredQueries().contains(referredQuery)) {
91 throw new RewriterException("Recursive queries are not supported, can't flatten query named \"{1}\"",
92 new String[] { query.getFullyQualifiedName() }, "Unsupported recursive query", query);
93 }
94 }
95
96 return this.doFlatten(disjunction);
97 }
98
99 /**
100 * Return the list of dependencies (including the root) in chronological order
101 *
102 * @param rootDisjunction
103 * @return
104 */
105 private List<PDisjunction> disjunctionDependencies(PDisjunction rootDisjunction) {
106 // Disjunctions are first collected into a list usign a depth-first approach,
107 // which can be iterated backwards while removing duplicates
108 Deque<PDisjunction> stack = new ArrayDeque<>();
109 LinkedList<PDisjunction> list = new LinkedList<>();
110 stack.push(rootDisjunction);
111 list.add(rootDisjunction);
112
113 while (!stack.isEmpty()) {
114 PDisjunction disjunction = stack.pop();
115 // Collect dependencies
116 for (PBody pBody : disjunction.getBodies()) {
117 for (PConstraint constraint : pBody.getConstraints()) {
118 if (constraint instanceof PositivePatternCall) {
119 PositivePatternCall positivePatternCall = (PositivePatternCall) constraint;
120 if (flattenCallPredicate.shouldFlatten(positivePatternCall)) {
121 // If the above preconditions meet, the call should be flattened
122 PDisjunction calledDisjunction = positivePatternCall.getReferredQuery().getDisjunctBodies();
123 stack.push(calledDisjunction);
124 list.add(calledDisjunction);
125 }
126 }
127 }
128 }
129 }
130
131 // Remove duplicates (keeping the last instance) and reverse order
132 Set<PDisjunction> visited = new HashSet<PDisjunction>();
133 List<PDisjunction> result = new ArrayList<PDisjunction>(list.size());
134
135 list.descendingIterator().forEachRemaining(item -> {
136 if (!visited.contains(item)) {
137 result.add(item);
138 visited.add(item);
139 }
140
141 });
142
143 return result;
144 }
145
146 /**
147 * This function holds the actual flattening logic for a PQuery
148 *
149 * @param rootDisjunction
150 * to be flattened
151 * @return the flattened bodies of the pQuery
152 */
153 private PDisjunction doFlatten(PDisjunction rootDisjunction) {
154
155 Map<PDisjunction, Set<PBody>> flatBodyMapping = new HashMap<>();
156
157 List<PDisjunction> dependencies = disjunctionDependencies(rootDisjunction);
158
159 for (PDisjunction disjunction : dependencies) {
160 Set<PBody> flatBodies = new LinkedHashSet<>();
161 for (PBody body : disjunction.getBodies()) {
162 if (isFlatteningNeeded(body)) {
163 Map<PositivePatternCall, Set<PBody>> flattenedBodies = new HashMap<>();
164 for (PConstraint pConstraint : body.getConstraints()) {
165
166 if (pConstraint instanceof PositivePatternCall) {
167 PositivePatternCall positivePatternCall = (PositivePatternCall) pConstraint;
168 if (flattenCallPredicate.shouldFlatten(positivePatternCall)) {
169 // If the above preconditions meet, do the flattening and return the disjoint bodies
170 PDisjunction calledDisjunction = positivePatternCall.getReferredQuery()
171 .getDisjunctBodies();
172
173 Set<PBody> flattenedBodySet = flatBodyMapping.get(calledDisjunction);
174 Preconditions.checkArgument(!flattenedBodySet.isEmpty());
175 flattenedBodies.put(positivePatternCall, flattenedBodySet);
176 }
177 }
178 }
179 flatBodies.addAll(createSetOfFlatPBodies(body, flattenedBodies));
180 } else {
181 flatBodies.add(prepareFlatPBody(body));
182 }
183 }
184 flatBodyMapping.put(disjunction, flatBodies);
185 }
186
187 return new PDisjunction(rootDisjunction.getQuery(), flatBodyMapping.get(rootDisjunction));
188 }
189
190 /**
191 * Creates the flattened bodies based on the caller body and the called (and already flattened) disjunctions
192 *
193 * @param pBody
194 * the body to flatten
195 * @param flattenedDisjunctions
196 * the
197 * @param flattenedCalls
198 * @return
199 */
200 private Set<PBody> createSetOfFlatPBodies(PBody pBody, Map<PositivePatternCall, Set<PBody>> flattenedCalls) {
201 PQuery pQuery = pBody.getPattern();
202
203 Set<Map<PositivePatternCall, PBody>> conjunctedCalls = permutation(flattenedCalls);
204
205 // The result set containing the merged conjuncted bodies
206 Set<PBody> conjunctedBodies = new HashSet<>();
207
208 for (Map<PositivePatternCall, PBody> calledBodies : conjunctedCalls) {
209 FlattenerCopier copier = createBodyCopier(pQuery, calledBodies);
210
211 int i = 0;
212 HierarchicalName hierarchicalNamingTool = new HierarchicalName();
213 for (PositivePatternCall patternCall : calledBodies.keySet()) {
214 // Merge each called body
215 hierarchicalNamingTool.setCallCount(i++);
216 copier.mergeBody(patternCall, hierarchicalNamingTool, new ExportedParameterFilter());
217 }
218
219 // Merge the caller's constraints to the conjunct body
220 copier.mergeBody(pBody);
221
222 PBody copiedBody = copier.getCopiedBody();
223 copiedBody.setStatus(PQueryStatus.OK);
224 conjunctedBodies.add(copiedBody);
225 }
226
227 return conjunctedBodies;
228 }
229
230 private FlattenerCopier createBodyCopier(PQuery query, Map<PositivePatternCall, PBody> calledBodies) {
231 FlattenerCopier flattenerCopier = new FlattenerCopier(query, calledBodies);
232 flattenerCopier.setTraceCollector(getTraceCollector());
233 return flattenerCopier;
234 }
235
236 private PBody prepareFlatPBody(PBody pBody) {
237 PBodyCopier copier = createBodyCopier(pBody.getPattern(), Collections.<PositivePatternCall, PBody> emptyMap());
238 copier.mergeBody(pBody, new SameName(), new AllowAllFilter());
239 // the copying of the body here is necessary for only one containing PDisjunction can be assigned to a PBody
240 return copier.getCopiedBody();
241 }
242
243 private boolean isFlatteningNeeded(PBody pBody) {
244 // Check if the body contains positive pattern call AND if it should be flattened
245 for (PConstraint pConstraint : pBody.getConstraints()) {
246 if (pConstraint instanceof PositivePatternCall) {
247 return flattenCallPredicate.shouldFlatten((PositivePatternCall) pConstraint);
248 }
249 }
250 return false;
251 }
252
253}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java
new file mode 100644
index 00000000..d0fc286b
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java
@@ -0,0 +1,31 @@
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.matchers.psystem.rewriters;
10
11import tools.refinery.viatra.runtime.matchers.psystem.queries.QueryInitializationException;
12
13/**
14 * An exception to wrap various issues during PDisjunction rewriting.
15 * @author Zoltan Ujhelyi
16 *
17 */
18public class RewriterException extends QueryInitializationException {
19
20 private static final long serialVersionUID = -4703825954995497932L;
21
22 public RewriterException(String message, String[] context, String shortMessage, Object patternDescription,
23 Throwable cause) {
24 super(message, context, shortMessage, patternDescription, cause);
25 }
26
27 public RewriterException(String message, String[] context, String shortMessage, Object patternDescription) {
28 super(message, context, shortMessage, patternDescription);
29 }
30
31}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java
new file mode 100644
index 00000000..71459558
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java
@@ -0,0 +1,63 @@
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.matchers.psystem.rewriters;
10
11import java.util.LinkedHashSet;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.context.IInputKey;
15import tools.refinery.viatra.runtime.matchers.context.surrogate.SurrogateQueryRegistry;
16import tools.refinery.viatra.runtime.matchers.psystem.PBody;
17import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
18import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
19import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint;
20import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction;
21import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
22import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus;
23import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
24import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
25
26/**
27 * @author Zoltan Ujhelyi
28 *
29 */
30public class SurrogateQueryRewriter extends PDisjunctionRewriter {
31
32 @Override
33 public PDisjunction rewrite(PDisjunction disjunction) {
34 Set<PBody> replacedBodies = new LinkedHashSet<>();
35 for (PBody body : disjunction.getBodies()) {
36 PBodyCopier copier = new PBodyCopier(body, getTraceCollector()) {
37
38 @Override
39 protected void copyTypeConstraint(TypeConstraint typeConstraint) {
40 PVariable[] mappedVariables = extractMappedVariables(typeConstraint);
41 Tuple variablesTuple = Tuples.flatTupleOf((Object[])mappedVariables);
42 final IInputKey supplierKey = typeConstraint.getSupplierKey();
43 if(SurrogateQueryRegistry.instance().hasSurrogateQueryFQN(supplierKey)) {
44 PQuery surrogateQuery = SurrogateQueryRegistry.instance().getSurrogateQuery(supplierKey);
45 if (surrogateQuery == null) {
46 throw new IllegalStateException(
47 String.format("Surrogate query for feature %s not found",
48 supplierKey.getPrettyPrintableName()));
49 }
50 addTrace(typeConstraint, new PositivePatternCall(getCopiedBody(), variablesTuple, surrogateQuery));
51 } else {
52 addTrace(typeConstraint, new TypeConstraint(getCopiedBody(), variablesTuple, supplierKey));
53 }
54 }
55 };
56 PBody modifiedBody = copier.getCopiedBody();
57 replacedBodies.add(modifiedBody);
58 modifiedBody.setStatus(PQueryStatus.OK);
59 }
60 return new PDisjunction(replacedBodies);
61 }
62
63}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java
new file mode 100644
index 00000000..10337979
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java
@@ -0,0 +1,88 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.matchers.psystem.rewriters;
10
11import java.util.HashMap;
12import java.util.LinkedHashMap;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator;
16import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider;
17import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
18import tools.refinery.viatra.runtime.matchers.util.Preconditions;
19
20/**
21 * A wrapper for {@link IExpressionEvaluator} which is capable of correctly mapping variable names used by the
22 * expression.
23 *
24 * @author Grill Balázs
25 *
26 */
27class VariableMappingExpressionEvaluatorWrapper implements IExpressionEvaluator {
28
29 private final IExpressionEvaluator wrapped;
30 private final Map<String, String> variableMapping;
31
32 public VariableMappingExpressionEvaluatorWrapper(IExpressionEvaluator wrapped,
33 Map<PVariable, PVariable> variableMapping) {
34
35 // Support to rewrap an already wrapped expression.
36 boolean rewrap = wrapped instanceof VariableMappingExpressionEvaluatorWrapper;
37 this.wrapped = rewrap ? ((VariableMappingExpressionEvaluatorWrapper)wrapped).wrapped : wrapped;
38
39 // Instead of just saving the reference of the mapping, save the actual (trimmed) state of the mapping as it
40 // may change during copying (especially during flattening). A LinkedHashMap is used to retain ordering of
41 // original parameter names iterator.
42 this.variableMapping = new LinkedHashMap<>();
43
44 // Index map by variable names
45 Map<String, PVariable> names = new HashMap<>();
46 for (PVariable originalVar : variableMapping.keySet()) {
47 names.put(originalVar.getName(), originalVar);
48 }
49
50 // In case of rewrapping, current names are contained by the previous mapping
51 Map<String, String> previousMapping = null;
52 if (rewrap){
53 previousMapping = ((VariableMappingExpressionEvaluatorWrapper)wrapped).variableMapping;
54 }
55
56 // Populate mapping
57 for (String inputParameterName : this.wrapped.getInputParameterNames()) {
58 String parameterName = rewrap ? previousMapping.get(inputParameterName) : inputParameterName;
59 Preconditions.checkArgument(parameterName != null);
60 PVariable original = names.get(parameterName);
61 Preconditions.checkArgument(original != null);
62 PVariable mapped = variableMapping.get(original);
63 if (mapped != null){
64 this.variableMapping.put(inputParameterName, mapped.getName());
65 }
66 }
67 }
68
69 @Override
70 public String getShortDescription() {
71 return wrapped.getShortDescription();
72 }
73
74 @Override
75 public Iterable<String> getInputParameterNames() {
76 return variableMapping.values();
77 }
78
79 @Override
80 public Object evaluateExpression(final IValueProvider provider) throws Exception {
81 return wrapped.evaluateExpression(variableName -> {
82 String mappedVariableName = variableMapping.get(variableName);
83 Preconditions.checkArgument(mappedVariableName != null, "Could not find variable %s", variableName);
84 return provider.getValue(mappedVariableName);
85 });
86 }
87
88}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/IStorageBackend.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/IStorageBackend.java
new file mode 100644
index 00000000..16f40358
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/IStorageBackend.java
@@ -0,0 +1,53 @@
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.matchers.scopes;
10
11import tools.refinery.viatra.runtime.matchers.context.IInputKey;
12import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableContext;
13import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableWriterBinary;
14import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableWriterUnary;
15
16/**
17 * An abstract storage backend that instantiates tables and coordinates transactions.
18 *
19 * <p><strong>EXPERIMENTAL</strong>. This class or interface has been added as
20 * part of a work in progress. There is no guarantee that this API will
21 * work or that it will remain the same.
22 *
23 * @author Gabor Bergmann
24 *
25 * @since 2.1
26 */
27public interface IStorageBackend {
28
29
30 /**
31 * Marks the beginning of a transaction.
32 * In transaction mode, table updates may be temporarily delayed ({@link tools.refinery.viatra.runtime.matchers.scopes.tables.IIndexTable} methods may return stale answers) for better performance.
33 */
34 void startTransaction();
35 /**
36 * Marks the end of a transaction.
37 * Any updates delayed during the transaction must now be flushed.
38 */
39 void finishTransaction();
40
41 /**
42 * Creates an index table for a simple value set.
43 * @param unique client promises to only insert a given tuple with multiplicity one
44 */
45 ITableWriterUnary.Table<Object> createUnaryTable(IInputKey key, ITableContext tableContext, boolean unique);
46 /**
47 * Creates an index table for a simple source-target bidirectional mapping.
48 * @param unique client promises to only insert a given tuple with multiplicity one
49 */
50 ITableWriterBinary.Table<Object,Object> createBinaryTable(IInputKey key, ITableContext tableContext, boolean unique);
51
52
53}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleLocalStorageBackend.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleLocalStorageBackend.java
new file mode 100644
index 00000000..fd1f7b7e
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleLocalStorageBackend.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 *******************************************************************************/
9package tools.refinery.viatra.runtime.matchers.scopes;
10
11import tools.refinery.viatra.runtime.matchers.context.IInputKey;
12import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableContext;
13import tools.refinery.viatra.runtime.matchers.scopes.tables.SimpleBinaryTable;
14import tools.refinery.viatra.runtime.matchers.scopes.tables.SimpleUnaryTable;
15
16/**
17 * Basic storage backend implementation based on local collections.
18 *
19 * <p><strong>EXPERIMENTAL</strong>. This class or interface has been added as
20 * part of a work in progress. There is no guarantee that this API will
21 * work or that it will remain the same.
22 *
23 * @author Gabor Bergmann
24 * @since 2.1
25 */
26public class SimpleLocalStorageBackend implements IStorageBackend {
27
28 @Override
29 public void startTransaction() {
30 // NOP
31 }
32
33 @Override
34 public void finishTransaction() {
35 // NOP
36 }
37
38 @Override
39 public SimpleUnaryTable<Object> createUnaryTable(IInputKey key, ITableContext tableContext, boolean unique) {
40 return new SimpleUnaryTable<>(key, tableContext, unique);
41 }
42
43 @Override
44 public SimpleBinaryTable<Object, Object> createBinaryTable(IInputKey key, ITableContext tableContext,
45 boolean unique) {
46 return new SimpleBinaryTable<>(key, tableContext, unique);
47 }
48
49
50
51}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleRuntimeContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleRuntimeContext.java
new file mode 100644
index 00000000..a3a827dc
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleRuntimeContext.java
@@ -0,0 +1,132 @@
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.matchers.scopes;
11
12import java.lang.reflect.InvocationTargetException;
13import java.util.concurrent.Callable;
14
15import tools.refinery.viatra.runtime.matchers.context.IInputKey;
16import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
17import tools.refinery.viatra.runtime.matchers.context.IndexingService;
18import tools.refinery.viatra.runtime.matchers.context.common.JavaTransitiveInstancesKey;
19import tools.refinery.viatra.runtime.matchers.scopes.tables.IIndexTable;
20import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
22
23/**
24 * A simple demo implementation of the IQRC interface using tables.
25 *
26 * <p>
27 * Usage: first, instantiate {@link IIndexTable} tables with this as the 'tableContext' argument, and call
28 * {@link #registerIndexTable(IIndexTable)} manually to register them. Afterwards, they will be visible to the query
29 * backends.
30 * <p>
31 * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
32 * part of a work in progress. There is no guarantee that this API will
33 * work or that it will remain the same.
34 *
35 * @author Gabor Bergmann
36 * @since 2.0
37 */
38public class SimpleRuntimeContext extends TabularRuntimeContext {
39
40 private IQueryMetaContext metaContext;
41
42 public SimpleRuntimeContext(IQueryMetaContext metaContext) {
43 this.metaContext = metaContext;
44 }
45
46 @Override
47 public void logError(String message) {
48 System.err.println(message);
49 }
50
51 @Override
52 public IQueryMetaContext getMetaContext() {
53 return metaContext;
54 }
55
56 @Override
57 public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException {
58 try {
59 return callable.call();
60 } catch (Exception e) {
61 throw new InvocationTargetException(e);
62 }
63 }
64
65 @Override
66 public boolean isCoalescing() {
67 return false;
68 }
69
70 @Override
71 public boolean isIndexed(IInputKey key, IndexingService service) {
72 return peekIndexTable(key) != null;
73 }
74
75 @Override
76 public void ensureIndexed(IInputKey key, IndexingService service) {
77 if (peekIndexTable(key) == null)
78 throw new IllegalArgumentException(key.getPrettyPrintableName());
79 }
80
81 @Override
82 public Object wrapElement(Object externalElement) {
83 return externalElement;
84 }
85
86 @Override
87 public Object unwrapElement(Object internalElement) {
88 return internalElement;
89 }
90
91 @Override
92 public Tuple wrapTuple(Tuple externalElements) {
93 return externalElements;
94 }
95
96 @Override
97 public Tuple unwrapTuple(Tuple internalElements) {
98 return internalElements;
99 }
100
101 @Override
102 public void ensureWildcardIndexing(IndexingService service) {
103 throw new UnsupportedOperationException();
104 }
105
106 @Override
107 public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException {
108 runnable.run();
109 }
110
111 @Override
112 protected boolean isContainedInStatelessKey(IInputKey key, ITuple seed) {
113 if (key instanceof JavaTransitiveInstancesKey) {
114 Class<?> instanceClass = forceGetWrapperInstanceClass((JavaTransitiveInstancesKey) key);
115 return instanceClass != null && instanceClass.isInstance(seed.get(0));
116 } else
117 throw new IllegalArgumentException(key.getPrettyPrintableName());
118 }
119
120 private Class<?> forceGetWrapperInstanceClass(JavaTransitiveInstancesKey key) {
121 Class<?> instanceClass;
122 try {
123 instanceClass = key.forceGetWrapperInstanceClass();
124 } catch (ClassNotFoundException e) {
125 logError(
126 "Could not load instance class for type constraint " + key.getWrappedKey() + ": " + e.getMessage());
127 instanceClass = null;
128 }
129 return instanceClass;
130 }
131
132}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/TabularRuntimeContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/TabularRuntimeContext.java
new file mode 100644
index 00000000..e99e24d3
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/TabularRuntimeContext.java
@@ -0,0 +1,119 @@
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.matchers.scopes;
11
12import java.util.Map;
13import java.util.Optional;
14
15import tools.refinery.viatra.runtime.matchers.context.AbstractQueryRuntimeContext;
16import tools.refinery.viatra.runtime.matchers.context.IInputKey;
17import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener;
18import tools.refinery.viatra.runtime.matchers.scopes.tables.IIndexTable;
19import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableContext;
20import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
22import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
23import tools.refinery.viatra.runtime.matchers.util.Accuracy;
24import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
25
26/**
27 * An abstract runtime context that serves enumerable input key instances from tables.
28 *
29 * <p>
30 * Usage: first, instantiate {@link IIndexTable} tables with this as the 'tableContext' argument. Call
31 * {@link #registerIndexTable(IIndexTable)} to register them; this may happen either during a coalesced indexing, or on
32 * external initiation. Afterwards, they will be visible to the query backends.
33 * <p>
34 * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
35 * part of a work in progress. There is no guarantee that this API will
36 * work or that it will remain the same.
37 *
38 * @author Gabor Bergmann
39 * @since 2.0
40 */
41public abstract class TabularRuntimeContext extends AbstractQueryRuntimeContext implements ITableContext {
42
43 private Map<IInputKey, IIndexTable> instanceTables = CollectionsFactory.createMap();
44
45 public void registerIndexTable(IIndexTable table) {
46 IInputKey inputKey = table.getInputKey();
47 instanceTables.put(inputKey, table);
48 }
49
50 /**
51 * @return null if the table is not registered
52 */
53 public IIndexTable peekIndexTable(IInputKey key) {
54 return instanceTables.get(key);
55 }
56
57 /**
58 * If the table is not registered, {@link #handleUnregisteredTableRequest(IInputKey)} is invoked; it may handle it
59 * by raising an error or e.g. on-demand index construction
60 */
61 public IIndexTable getIndexTable(IInputKey key) {
62 IIndexTable table = instanceTables.get(key);
63 if (table != null)
64 return table;
65 else
66 return handleUnregisteredTableRequest(key);
67 }
68
69 /**
70 * Override this to provide on-demand table registration
71 */
72 protected IIndexTable handleUnregisteredTableRequest(IInputKey key) {
73 throw new IllegalArgumentException(key.getPrettyPrintableName());
74 }
75
76 @Override
77 public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
78 return getIndexTable(key).countTuples(seedMask, seed);
79 }
80
81 @Override
82 public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) {
83 return getIndexTable(key).estimateProjectionSize(groupMask, requiredAccuracy);
84 }
85
86 @Override
87 public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
88 return getIndexTable(key).enumerateTuples(seedMask, seed);
89 }
90
91 @Override
92 public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed) {
93 return getIndexTable(key).enumerateValues(seedMask, seed);
94 }
95
96 @Override
97 public boolean containsTuple(IInputKey key, ITuple seed) {
98 if (key.isEnumerable()) {
99 return getIndexTable(key).containsTuple(seed);
100 } else {
101 return isContainedInStatelessKey(key, seed);
102 }
103 }
104
105 @Override
106 public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) {
107 getIndexTable(key).addUpdateListener(seed, listener);
108 }
109 @Override
110 public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) {
111 getIndexTable(key).removeUpdateListener(seed, listener);
112 }
113
114 /**
115 * Handles non-enumerable input keys that are not backed by a table
116 */
117 protected abstract boolean isContainedInStatelessKey(IInputKey key, ITuple seed);
118
119}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/AbstractIndexTable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/AbstractIndexTable.java
new file mode 100644
index 00000000..7b557c33
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/AbstractIndexTable.java
@@ -0,0 +1,266 @@
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.matchers.scopes.tables;
10
11import java.util.List;
12
13import tools.refinery.viatra.runtime.matchers.context.IInputKey;
14import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener;
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
20import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
21
22/**
23 * <p>
24 * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
25 * part of a work in progress. There is no guarantee that this API will
26 * work or that it will remain the same.
27 *
28 * @since 2.0
29 * @author Gabor Bergmann
30 */
31public abstract class AbstractIndexTable implements IIndexTable {
32
33 private IInputKey inputKey;
34 protected ITableContext tableContext;
35
36 protected final TupleMask emptyMask;
37 protected final Tuple emptyTuple;
38
39
40 public AbstractIndexTable(IInputKey inputKey, ITableContext tableContext) {
41 this.inputKey = inputKey;
42 this.tableContext = tableContext;
43
44 this.emptyMask = TupleMask.empty(getInputKey().getArity());
45 this.emptyTuple = Tuples.flatTupleOf(new Object[inputKey.getArity()]);
46 }
47
48 @Override
49 public String toString() {
50 return this.getClass().getSimpleName() + ":" + inputKey.getPrettyPrintableName();
51 }
52
53 @Override
54 public IInputKey getInputKey() {
55 return inputKey;
56 }
57
58 protected void logError(String message) {
59 tableContext.logError(message);
60 }
61
62
63 /// UPDATE HANDLING SECTION
64
65 // The entire mechanism is designed to accommodate a large number of update listeners,
66 // but maybe there will typically be only a single, universal (unseeded) listener?
67 // TODO Create special handling for that case.
68
69 // Invariant: true iff #listenerGroupsAndSeed is nonempty
70 protected boolean emitNotifications = false;
71 // Subscribed listeners grouped by their seed mask (e.g. all those that seed columns 2 and 5 are together),
72 // groups are stored in a list for quick delivery-time iteration (at the expense of adding / removing);
73 // individual listeners can be looked up based on their seed tuple
74 protected List<IListenersWithSameMask> listenerGroups = CollectionsFactory.createObserverList();
75
76
77 /**
78 * Implementors shall call this to deliver all notifications.
79 * Call may be conditioned to {@link #emitNotifications}
80 */
81 protected void deliverChangeNotifications(Tuple updateTuple, boolean isInsertion) {
82 for (IListenersWithSameMask listenersForSeed : listenerGroups) {
83 listenersForSeed.deliver(updateTuple, isInsertion);
84 }
85 }
86
87 @Override
88 public void addUpdateListener(Tuple seed, IQueryRuntimeContextListener listener) {
89 TupleMask seedMask;
90 if (seed == null) {
91 seed = emptyTuple;
92 seedMask = emptyMask;
93 } else {
94 seedMask = TupleMask.fromNonNullIndices(seed);
95 }
96 IListenersWithSameMask listenerGroup = getListenerGroup(seedMask);
97 if (listenerGroup == null) { // create new group
98 switch (seedMask.getSize()) {
99 case 0:
100 listenerGroup = new UniversalListeners();
101 break;
102 case 1:
103 listenerGroup = new ColumnBoundListeners(seedMask.indices[0]);
104 break;
105 default:
106 listenerGroup = new GenericBoundListeners(seedMask);
107 }
108 listenerGroups.add(listenerGroup);
109 emitNotifications = true;
110 }
111 listenerGroup.addUpdateListener(seed, listener);
112 }
113
114 @Override
115 public void removeUpdateListener(Tuple seed, IQueryRuntimeContextListener listener) {
116 TupleMask seedMask;
117 if (seed == null) {
118 seed = emptyTuple;
119 seedMask = emptyMask;
120 } else {
121 seedMask = TupleMask.fromNonNullIndices(seed);
122 }
123 IListenersWithSameMask listenerGroup = getListenerGroup(seedMask);
124 if (listenerGroup == null)
125 throw new IllegalStateException("no listener subscribed with mask" + seedMask);
126
127 if (listenerGroup.removeUpdateListener(seed, listener)) {
128 listenerGroups.remove(listenerGroup);
129 emitNotifications = !listenerGroups.isEmpty();
130 }
131 }
132
133 protected IListenersWithSameMask getListenerGroup(TupleMask seedMask) {
134 for (IListenersWithSameMask candidateGroup : listenerGroups) { // group already exists?
135 if (seedMask.equals(candidateGroup.getSeedMask())) {
136 return candidateGroup;
137 }
138 }
139 return null;
140 }
141
142
143 /**
144 * Represents all listeners subscribed to seeds with the given seed mask.
145 *
146 * @author Bergmann Gabor
147 */
148 protected static interface IListenersWithSameMask {
149
150 public TupleMask getSeedMask();
151
152 public void deliver(Tuple updateTuple, boolean isInsertion);
153
154 public void addUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener);
155 /**
156 * @return true if this was the last listener, and the {@link IListenersWithSameMask} can be disposed of.
157 */
158 public boolean removeUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener);
159 }
160 /**
161 * Listeners interested in all tuples
162 */
163 protected final class UniversalListeners implements IListenersWithSameMask {
164 private final TupleMask mask = TupleMask.empty(inputKey.getArity());
165 private List<IQueryRuntimeContextListener> listeners = CollectionsFactory.createObserverList();
166
167 @Override
168 public TupleMask getSeedMask() {
169 return mask;
170 }
171 @Override
172 public void deliver(Tuple updateTuple, boolean isInsertion) {
173 IInputKey key = inputKey;
174 for (IQueryRuntimeContextListener listener : listeners) {
175 listener.update(key, updateTuple, isInsertion);
176 }
177 }
178 @Override
179 public void addUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener) {
180 listeners.add(listener);
181 }
182 @Override
183 public boolean removeUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener) {
184 listeners.remove(listener);
185 return listeners.isEmpty();
186 }
187 }
188 /**
189 * Listeners interested in all tuples seeded by a single columns
190 */
191 protected final class ColumnBoundListeners implements IListenersWithSameMask {
192 private int seedPosition;
193 protected final TupleMask mask;
194 // indexed by projected seed tuple
195 protected IMultiLookup<Object,IQueryRuntimeContextListener> listeners =
196 CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class);
197
198 public ColumnBoundListeners(int seedPosition) {
199 this.seedPosition = seedPosition;
200 this.mask = TupleMask.selectSingle(seedPosition, inputKey.getArity());
201 }
202
203 @Override
204 public TupleMask getSeedMask() {
205 return mask;
206 }
207 @Override
208 public void deliver(Tuple updateTuple, boolean isInsertion) {
209 IInputKey key = inputKey;
210 Object projectedSeed = updateTuple.get(seedPosition);
211 for (IQueryRuntimeContextListener listener : listeners.lookupOrEmpty(projectedSeed)) {
212 listener.update(key, updateTuple, isInsertion);
213 }
214 }
215 @Override
216 public void addUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener) {
217 Object projectedSeed = originalSeed.get(seedPosition);
218 listeners.addPair(projectedSeed, listener);
219 }
220 @Override
221 public boolean removeUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener) {
222 Object projectedSeed = originalSeed.get(seedPosition);
223 listeners.removePair(projectedSeed, listener);
224 return listeners.countKeys() == 0;
225 }
226 }
227 /**
228 * Listeners interested in all tuples seeded by a tuple of values
229 */
230 protected final class GenericBoundListeners implements IListenersWithSameMask {
231 protected final TupleMask mask;
232 // indexed by projected seed tuple
233 protected IMultiLookup<Tuple,IQueryRuntimeContextListener> listeners =
234 CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class);
235
236 public GenericBoundListeners(TupleMask mask) {
237 this.mask = mask;
238 }
239
240 @Override
241 public TupleMask getSeedMask() {
242 return mask;
243 }
244 @Override
245 public void deliver(Tuple updateTuple, boolean isInsertion) {
246 IInputKey key = inputKey;
247 Tuple projectedSeed = mask.transform(updateTuple);
248 for (IQueryRuntimeContextListener listener : listeners.lookupOrEmpty(projectedSeed)) {
249 listener.update(key, updateTuple, isInsertion);
250 }
251 }
252 @Override
253 public void addUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener) {
254 Tuple projectedSeed = mask.transform(originalSeed);
255 listeners.addPair(projectedSeed, listener);
256 }
257 @Override
258 public boolean removeUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener) {
259 Tuple projectedSeed = mask.transform(originalSeed);
260 listeners.removePair(projectedSeed, listener);
261 return listeners.countKeys() == 0;
262 }
263 }
264
265
266} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DefaultIndexTable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DefaultIndexTable.java
new file mode 100644
index 00000000..39475d11
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DefaultIndexTable.java
@@ -0,0 +1,143 @@
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.matchers.scopes.tables;
10
11import java.util.Map;
12import java.util.Optional;
13import java.util.stream.Stream;
14
15import tools.refinery.viatra.runtime.matchers.context.IInputKey;
16import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory;
17import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.util.Accuracy;
21import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
22import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
23import tools.refinery.viatra.runtime.matchers.util.Direction;
24import tools.refinery.viatra.runtime.matchers.util.IMemory;
25
26/**
27 * Demo default implementation.
28 * <p>
29 * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
30 * part of a work in progress. There is no guarantee that this API will
31 * work or that it will remain the same.
32 *
33 * @since 2.0
34 * @author Gabor Bergmann
35 */
36public class DefaultIndexTable extends AbstractIndexTable implements ITableWriterGeneric {
37
38 protected IMemory<Tuple> rows = CollectionsFactory.createMultiset(); // TODO use SetMemory if unique
39 protected Map<TupleMask, MaskedTupleMemory<?>> indexMemories = CollectionsFactory.createMap();
40 private boolean unique;
41
42 /**
43 * @param unique
44 * client promises to only insert a given tuple with multiplicity one
45 */
46 public DefaultIndexTable(IInputKey inputKey, ITableContext tableContext, boolean unique) {
47 super(inputKey, tableContext);
48 this.unique = unique;
49 }
50
51 @Override
52 public void write(Direction direction, Tuple row) {
53 if (direction == Direction.INSERT) {
54 boolean changed = rows.addOne(row);
55 if (unique && !changed) {
56 String msg = String.format(
57 "Error: trying to add duplicate row %s to the unique table %s. This indicates some errors in underlying model representation.",
58 row, getInputKey().getPrettyPrintableName());
59 logError(msg);
60 }
61 if (changed) {
62 for (MaskedTupleMemory<?> indexMemory : indexMemories.values()) {
63 indexMemory.add(row);
64 }
65 if (emitNotifications) {
66 deliverChangeNotifications(row, true);
67 }
68 }
69 } else { // DELETE
70 boolean changed = rows.removeOne(row);
71 if (unique && !changed) {
72 String msg = String.format(
73 "Error: trying to remove duplicate value %s from the unique table %s. This indicates some errors in underlying model representation.",
74 row, getInputKey().getPrettyPrintableName());
75 logError(msg);
76 }
77 if (changed) {
78 for (MaskedTupleMemory<?> indexMemory : indexMemories.values()) {
79 indexMemory.remove(row);
80 }
81 if (emitNotifications) {
82 deliverChangeNotifications(row, false);
83 }
84 }
85 }
86 }
87
88 @Override
89 public boolean containsTuple(ITuple seed) {
90 return rows.distinctValues().contains(seed);
91 }
92
93 private MaskedTupleMemory<?> getIndexMemory(TupleMask seedMask) {
94 return indexMemories.computeIfAbsent(seedMask, mask -> {
95 MaskedTupleMemory<?> memory = MaskedTupleMemory.create(seedMask, MemoryType.SETS, DefaultIndexTable.this);
96 for (Tuple tuple : rows.distinctValues()) {
97 memory.add(tuple);
98 }
99 return memory;
100 });
101 }
102
103 @Override
104 public int countTuples(TupleMask seedMask, ITuple seed) {
105 switch (seedMask.getSize()) {
106 case 0: // unseeded
107 return rows.size();
108 default:
109 return getIndexMemory(seedMask).getOrEmpty(seed).size();
110 }
111 }
112
113 @Override
114 public Optional<Long> estimateProjectionSize(TupleMask groupMask, Accuracy requiredAccuracy) {
115 // always exact count
116 if (groupMask.getSize() == 0) {
117 return rows.size() == 0 ? Optional.of(0L) : Optional.of(1L);
118 } else if (groupMask.getSize() == this.emptyTuple.getSize()) {
119 return Optional.of((long) rows.size());
120 } else {
121 return Optional.of((long)getIndexMemory(groupMask).getKeysetSize());
122 }
123 }
124
125 @Override
126 public Iterable<Tuple> enumerateTuples(TupleMask seedMask, ITuple seed) {
127 return getIndexMemory(seedMask).getOrEmpty(seed);
128 }
129
130 @Override
131 public Stream<? extends Tuple> streamTuples(TupleMask seedMask, ITuple seed) {
132 return getIndexMemory(seedMask).getOrEmpty(seed).stream();
133 }
134
135 @Override
136 public Stream<? extends Object> streamValues(TupleMask seedMask, ITuple seed) {
137 // we assume there is a single omitted index in the mask
138 int queriedColumn = seedMask.getFirstOmittedIndex().getAsInt();
139 return getIndexMemory(seedMask).getOrEmpty(seed).stream()
140 .map(row2 -> row2.get(queriedColumn));
141 }
142
143}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DisjointUnionTable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DisjointUnionTable.java
new file mode 100644
index 00000000..a3a65f14
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DisjointUnionTable.java
@@ -0,0 +1,192 @@
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.matchers.scopes.tables;
11
12import java.util.Collections;
13import java.util.List;
14import java.util.Objects;
15import java.util.Optional;
16import java.util.stream.Stream;
17
18import tools.refinery.viatra.runtime.matchers.context.IInputKey;
19import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener;
20import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
22import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
23import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
24import tools.refinery.viatra.runtime.matchers.util.Accuracy;
25import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
26
27/**
28 * Disjoint union of the provided child tables.
29 *
30 * Used e.g. to present a transitive instance table as a view composed from direct instance tables.
31 * <p>
32 * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
33 * part of a work in progress. There is no guarantee that this API will
34 * work or that it will remain the same.
35 *
36 * @since 2.0
37 * @author Gabor Bergmann
38 */
39public class DisjointUnionTable extends AbstractIndexTable {
40
41 protected List<IIndexTable> childTables = CollectionsFactory.createObserverList();
42
43 public DisjointUnionTable(IInputKey inputKey, ITableContext tableContext) {
44 super(inputKey, tableContext);
45 }
46
47 public List<IIndexTable> getChildTables() {
48 return Collections.unmodifiableList(childTables);
49 }
50
51 /**
52 * Precondition: the new child currently is, and will forever stay, disjoint from any other child tables.
53 */
54 public void addChildTable(IIndexTable child) {
55 if (getInputKey().getArity() != child.getInputKey().getArity())
56 throw new IllegalArgumentException(child.toString());
57
58 childTables.add(child);
59
60 if (emitNotifications) {
61 for (Tuple tuple : child.enumerateTuples(emptyMask, Tuples.staticArityFlatTupleOf())) {
62 deliverChangeNotifications(tuple, true);
63 }
64 }
65 }
66
67 @Override
68 public int countTuples(TupleMask seedMask, ITuple seed) {
69 int count = 0;
70 for (IIndexTable child : childTables) {
71 count += child.countTuples(seedMask, seed);
72 }
73 return count;
74 }
75
76
77 @Override
78 public Optional<Long> estimateProjectionSize(TupleMask groupMask, Accuracy requiredAccuracy) {
79 // exact results for trivial projections
80 if (groupMask.getSize() == 0) {
81 for (IIndexTable child : childTables) {
82 if (0 != child.countTuples(this.emptyMask, Tuples.staticArityFlatTupleOf()))
83 return Optional.of(1L);
84 }
85 return Optional.of(0L);
86 } else if (groupMask.getSize() == emptyTuple.getSize()) {
87 return Optional.of((long)countTuples(this.emptyMask, Tuples.staticArityFlatTupleOf()));
88 }
89 // summing child tables is an upper bound
90 if (Accuracy.BEST_UPPER_BOUND.atLeastAsPreciseAs(requiredAccuracy)) {
91 return Optional.of((long)countTuples(this.emptyMask, Tuples.staticArityFlatTupleOf()));
92 } else { // (Accuracy.BEST_LOWER_BOUND == requiredAccuracy)
93 //projections of child tables may coincide, but the largest one is still a lower bound
94 Optional<Long> maxProjection = Optional.empty();
95 for (IIndexTable child : childTables) {
96 Optional<Long> estimateOfChild = child.estimateProjectionSize(groupMask, requiredAccuracy);
97 if (estimateOfChild.isPresent()) {
98 maxProjection = Optional.of(Math.max(estimateOfChild.get(), maxProjection.orElse(0L)));
99 }
100 }
101 return maxProjection;
102 }
103 }
104
105 @Override
106 public Stream<? extends Tuple> streamTuples(TupleMask seedMask, ITuple seed) {
107 Stream<? extends Tuple> stream = Stream.empty();
108 for (IIndexTable child : childTables) {
109 Stream<? extends Tuple> childStream = child.streamTuples(seedMask, seed);
110 stream = Stream.concat(stream, childStream);
111 }
112 return stream;
113 }
114
115 @Override
116 public Stream<? extends Object> streamValues(TupleMask seedMask, ITuple seed) {
117 Stream<? extends Object> stream = Stream.empty();
118 for (IIndexTable child : childTables) {
119 Stream<? extends Object> childStream = child.streamValues(seedMask, seed);
120 stream = Stream.concat(stream, childStream);
121 }
122 return stream;
123 }
124
125 @Override
126 public boolean containsTuple(ITuple seed) {
127 for (IIndexTable child : childTables) {
128 if (child.containsTuple(seed))
129 return true;
130 }
131 return false;
132 }
133
134 @Override
135 public void addUpdateListener(Tuple seed, IQueryRuntimeContextListener listener) {
136 super.addUpdateListener(seed, listener);
137
138 for (IIndexTable table : childTables) {
139 table.addUpdateListener(seed, new ListenerWrapper(listener));
140 }
141 }
142 @Override
143 public void removeUpdateListener(Tuple seed, IQueryRuntimeContextListener listener) {
144 super.removeUpdateListener(seed, listener);
145
146 for (IIndexTable table : childTables) {
147 table.removeUpdateListener(seed, new ListenerWrapper(listener));
148 }
149 }
150
151
152 // TODO this would not be necessary
153 // if we moved from IQRCL to an interface that does not expose the input key
154 private class ListenerWrapper implements IQueryRuntimeContextListener {
155
156 private IQueryRuntimeContextListener wrappedListener;
157 public ListenerWrapper(IQueryRuntimeContextListener wrappedListener) {
158 this.wrappedListener = wrappedListener;
159 }
160
161 @Override
162 public void update(IInputKey key, Tuple updateTuple, boolean isInsertion) {
163 wrappedListener.update(getInputKey(), updateTuple, isInsertion);
164 }
165
166 @Override
167 public int hashCode() {
168 return Objects.hash(wrappedListener, DisjointUnionTable.this);
169 }
170 @Override
171 public boolean equals(Object obj) {
172 if (this == obj)
173 return true;
174 if (obj == null)
175 return false;
176 if (getClass() != obj.getClass())
177 return false;
178 ListenerWrapper other = (ListenerWrapper) obj;
179 if (!getOuterType().equals(other.getOuterType()))
180 return false;
181 return Objects.equals(wrappedListener, other.wrappedListener);
182 }
183 private DisjointUnionTable getOuterType() {
184 return DisjointUnionTable.this;
185 }
186 @Override
187 public String toString() {
188 return "Wrapper to DisjointUnion("+getInputKey().getPrettyPrintableName()+") for " + wrappedListener;
189 }
190 }
191
192}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/IIndexTable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/IIndexTable.java
new file mode 100644
index 00000000..be375393
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/IIndexTable.java
@@ -0,0 +1,212 @@
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.matchers.scopes.tables;
10
11import java.util.Iterator;
12import java.util.Optional;
13import java.util.stream.Stream;
14
15import tools.refinery.viatra.runtime.matchers.context.IInputKey;
16import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
17import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
18import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener;
19import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
20import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
21import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
22import tools.refinery.viatra.runtime.matchers.util.Accuracy;
23
24/**
25 * Read-only interface that provides the {@link IInputKey}-specific slice of an instance store to realize a
26 * {@link IQueryRuntimeContext}. Implemented by a customizable data store that is responsible for:
27 * <ul>
28 * <li>storing the instance tuples of the {@link IInputKey},</li>
29 * <li>providing efficient lookup via storage-specific indexing,</li>
30 * <li>delivering notifications. (TODO not designed yet)</li>
31 * </ul>
32 *
33 * <p>
34 * Can be specialized for unary / binary / etc., opposite edges or node subtypes, specific types, distributed storage,
35 * etc.
36 * <p>
37 * Writeable API is specific to the customized implementations (e.g. unary).
38 *
39 * <p>
40 * <b>Precondition:</b> the associated input key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}.
41 * <p>
42 * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
43 * part of a work in progress. There is no guarantee that this API will
44 * work or that it will remain the same.
45 *
46 * @since 2.0
47 * @author Gabor Bergmann
48 * @noimplement This interface is not intended to be implemented directly. Extend {@link AbstractIndexTable} instead.
49 */
50public interface IIndexTable {
51
52 // TODO add superinterface that represents a statistics-only counter?
53
54 /**
55 * @return the input key indexed by this table
56 */
57 public IInputKey getInputKey();
58
59 /**
60 * Returns the tuples, optionally seeded with the given tuple.
61 *
62 * <p> Consider using the more idiomatic {@link #streamTuples(TupleMask, ITuple)} instead.
63 *
64 * @param seedMask
65 * a mask that extracts those parameters of the input key (from the entire parameter list) that should be
66 * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most
67 * once in seedMask.
68 * @param seed
69 * the tuple of fixed values restricting the row set to be considered, in the same order as given in
70 * parameterSeedMask, so that for each considered row tuple,
71 * projectedParameterSeed.equals(parameterSeedMask.transform(row)) should hold. Must not be null.
72 * @return the tuples in the table for the given key and seed
73 */
74 @SuppressWarnings("unchecked")
75 public default Iterable<Tuple> enumerateTuples(TupleMask seedMask, ITuple seed) {
76 return () -> (Iterator<Tuple>) (streamTuples(seedMask, seed).iterator());
77 }
78
79 /**
80 * Returns the tuples, optionally seeded with the given tuple.
81 *
82 * @param seedMask
83 * a mask that extracts those parameters of the input key (from the entire parameter list) that should be
84 * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most
85 * once in seedMask.
86 * @param seed
87 * the tuple of fixed values restricting the row set to be considered, in the same order as given in
88 * parameterSeedMask, so that for each considered row tuple,
89 * projectedParameterSeed.equals(parameterSeedMask.transform(row)) should hold. Must not be null.
90 * @return the tuples in the table for the given key and seed
91 * @since 2.1
92 */
93 public Stream<? extends Tuple> streamTuples(TupleMask seedMask, ITuple seed);
94
95 /**
96 * Simpler form of {@link #enumerateTuples(TupleMask, ITuple)} in the case where all values of the tuples are bound
97 * by the seed except for one.
98 *
99 * <p>
100 * Selects the tuples in the table, optionally seeded with the given tuple, and then returns the single value from
101 * each tuple which is not bound by the seed mask.
102 *
103 * <p> Consider using the more idiomatic {@link #streamValues(TupleMask, ITuple)} instead.
104 *
105 * @param seedMask
106 * a mask that extracts those parameters of the input key (from the entire parameter list) that should be
107 * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most
108 * once in seedMask, and seedMask must include all parameters in any arbitrary order except one.
109 * @param seed
110 * the tuple of fixed values restricting the row set to be considered, in the same order as given in
111 * parameterSeedMask, so that for each considered row tuple,
112 * projectedParameterSeed.equals(parameterSeedMask.transform(row)) should hold. Must not be null.
113 * @return the objects in the table for the given key and seed
114 *
115 */
116 @SuppressWarnings("unchecked")
117 public default Iterable<? extends Object> enumerateValues(TupleMask seedMask, ITuple seed) {
118 return () -> (Iterator<Object>) (streamValues(seedMask, seed).iterator());
119 }
120
121 /**
122 * Simpler form of {@link #enumerateTuples(TupleMask, ITuple)} in the case where all values of the tuples are bound
123 * by the seed except for one.
124 *
125 * <p>
126 * Selects the tuples in the table, optionally seeded with the given tuple, and then returns the single value from
127 * each tuple which is not bound by the seed mask.
128 *
129 * @param seedMask
130 * a mask that extracts those parameters of the input key (from the entire parameter list) that should be
131 * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most
132 * once in seedMask, and seedMask must include all parameters in any arbitrary order except one.
133 * @param seed
134 * the tuple of fixed values restricting the row set to be considered, in the same order as given in
135 * parameterSeedMask, so that for each considered row tuple,
136 * projectedParameterSeed.equals(parameterSeedMask.transform(row)) should hold. Must not be null.
137 * @return the objects in the table for the given key and seed
138 *
139 * @since 2.1
140 */
141 public Stream<? extends Object> streamValues(TupleMask seedMask, ITuple seed);
142
143 /**
144 * Simpler form of {@link #enumerateTuples(TupleMask, ITuple)} in the case where all values of the tuples are bound
145 * by the seed.
146 *
147 * <p>
148 * Returns whether the given tuple is in the table identified by the input key.
149 *
150 * @param seed
151 * a row tuple of fixed values whose presence in the table is queried
152 * @return true iff there is a row tuple contained in the table that corresponds to the given seed
153 */
154 public boolean containsTuple(ITuple seed);
155
156 /**
157 * Returns the number of tuples, optionally seeded with the given tuple.
158 *
159 * <p>
160 * Selects the tuples in the table, optionally seeded with the given tuple, and then returns their number.
161 *
162 * @param seedMask
163 * a mask that extracts those parameters of the input key (from the entire parameter list) that should be
164 * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most
165 * once in seedMask.
166 * @param seed
167 * the tuple of fixed values restricting the row set to be considered, in the same order as given in
168 * parameterSeedMask, so that for each considered row tuple,
169 * projectedParameterSeed.equals(parameterSeedMask.transform(row)) should hold. Must not be null.
170 * @return the number of tuples in the table for the given key and seed
171 *
172 */
173 public int countTuples(TupleMask seedMask, ITuple seed);
174
175 /**
176 * Gives an estimate of the number of different groups the tuples of the table are projected into by the given mask
177 * (e.g. for an identity mask, this means the full relation size). The estimate must meet the required accuracy.
178 *
179 * <p> Derived tables may return {@link Optional#empty()} if it would be costly to provide an answer up to the required precision.
180 * Direct storage tables are expected to always be able to give an exact count.
181 *
182 * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask.
183 *
184 * @since 2.1
185 */
186 public Optional<Long> estimateProjectionSize(TupleMask groupMask, Accuracy requiredAccuracy);
187
188 /**
189 * Subscribes for updates in the table, optionally seeded with the given tuple.
190 * <p> This should be called after initializing a result cache by an enumeration method.
191 *
192 * @param seed can be null or a tuple with matching arity;
193 * if non-null, notifications will delivered only about those updates of the table
194 * that match the seed at positions where the seed is non-null.
195 * @param listener will be notified of future changes
196 *
197 * @since 2.1
198 */
199 public void addUpdateListener(Tuple seed, IQueryRuntimeContextListener listener);
200
201 /**
202 * Unsubscribes from updates in the table, optionally seeded with the given tuple.
203 *
204 * @param seed can be null or a tuple with matching arity;
205 * see {@link #addUpdateListener(Tuple, IQueryRuntimeContextListener)} for definition.
206 * @param listener will no longer be notified of future changes
207 *
208 * @since 2.1
209 */
210 public void removeUpdateListener(Tuple seed, IQueryRuntimeContextListener listener);
211
212}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableContext.java
new file mode 100644
index 00000000..69e83cd7
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableContext.java
@@ -0,0 +1,29 @@
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.matchers.scopes.tables;
10
11/**
12 * Callbacks that {@link IIndexTable} implementations are expected to invoke on their environment.
13 * <p>
14 * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
15 * part of a work in progress. There is no guarantee that this API will
16 * work or that it will remain the same.
17 *
18 * @since 2.0
19 * @author Gabor Bergmann
20 */
21public interface ITableContext {
22
23 // TODO notifications?
24
25 /**
26 * Indicates that an error has occurred in maintaining an index table, e.g. duplicate value.
27 */
28 public void logError(String message);
29}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterBinary.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterBinary.java
new file mode 100644
index 00000000..fb614014
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterBinary.java
@@ -0,0 +1,53 @@
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.matchers.scopes.tables;
11
12import tools.refinery.viatra.runtime.matchers.util.Direction;
13
14/**
15 * Modifies the contents of a binary {@link IIndexTable}.
16 * <p>
17 * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
18 * part of a work in progress. There is no guarantee that this API will
19 * work or that it will remain the same.
20 *
21 * @since 2.0
22 * @author Gabor Bergmann
23 */
24public interface ITableWriterBinary<Source, Target> {
25 /**
26 * Adds/removes a row to/from the table.
27 *
28 * @param direction
29 * tells whether putting a row into the table or deleting
30 *
31 * TODO: store as multiset, return bool?
32 */
33 void write(Direction direction, Source source, Target target);
34
35 /**
36 * Intersection type for writers that are also tables
37 */
38 interface Table<Source, Target> extends ITableWriterBinary<Source, Target>, IIndexTable {
39 }
40
41 /**
42 * /dev/null implementation
43 *
44 * @author Gabor Bergmann
45 */
46 static class Nop<Source, Target> implements ITableWriterBinary<Source, Target> {
47 @Override
48 public void write(Direction direction, Source source, Target target) {
49 // NO-OP
50 }
51
52 }
53}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterGeneric.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterGeneric.java
new file mode 100644
index 00000000..fb1ebcc0
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterGeneric.java
@@ -0,0 +1,52 @@
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.matchers.scopes.tables;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import tools.refinery.viatra.runtime.matchers.util.Direction;
13
14/**
15 * Modifies the contents of an {@link IIndexTable}.
16 * <p>
17 * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
18 * part of a work in progress. There is no guarantee that this API will
19 * work or that it will remain the same.
20 *
21 * @since 2.0
22 * @author Gabor Bergmann
23 */
24public interface ITableWriterGeneric {
25
26 /**
27 * Adds/removes a row to/from the table.
28 *
29 * @param direction
30 * tells whether putting a row into the table or deleting TODO: store as multiset, return bool?
31 */
32 void write(Direction direction, Tuple row);
33
34 /**
35 * Intersection type for writers that are also tables
36 */
37 interface Table extends ITableWriterGeneric, IIndexTable {
38 }
39
40 /**
41 * /dev/null implementation
42 *
43 * @author Gabor Bergmann
44 */
45 static class Nop implements ITableWriterGeneric {
46 @Override
47 public void write(Direction direction, Tuple row) {
48 // NO-OP
49 }
50
51 }
52}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterUnary.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterUnary.java
new file mode 100644
index 00000000..f90d5362
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterUnary.java
@@ -0,0 +1,52 @@
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.matchers.scopes.tables;
11
12import tools.refinery.viatra.runtime.matchers.util.Direction;
13
14/**
15 * Modifies the contents of a unary {@link IIndexTable}.
16 * <p>
17 * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
18 * part of a work in progress. There is no guarantee that this API will
19 * work or that it will remain the same.
20 *
21 * @since 2.0
22 * @author Gabor Bergmann
23 *
24 */
25public interface ITableWriterUnary<Value> {
26
27 /**
28 * Adds/removes a row to/from the table.
29 *
30 * @param direction
31 * tells whether putting a row into the table or deleting TODO: store as multiset, return bool?
32 */
33 void write(Direction direction, Value value);
34
35 /**
36 * Intersection type for writers that are also tables
37 */
38 interface Table<Value> extends ITableWriterUnary<Value>, IIndexTable {
39 }
40
41 /**
42 * /dev/null implementation
43 *
44 * @author Gabor Bergmann
45 */
46 static class Nop<Value> implements ITableWriterUnary<Value> {
47 @Override
48 public void write(Direction direction, Value value) {
49 // NO-OP
50 }
51 }
52}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleBinaryTable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleBinaryTable.java
new file mode 100644
index 00000000..588ed9d2
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleBinaryTable.java
@@ -0,0 +1,320 @@
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.matchers.scopes.tables;
10
11import java.util.Collections;
12import java.util.Optional;
13import java.util.Set;
14import java.util.stream.Stream;
15
16import tools.refinery.viatra.runtime.matchers.context.IInputKey;
17import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
21import tools.refinery.viatra.runtime.matchers.util.Accuracy;
22import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
23import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
24import tools.refinery.viatra.runtime.matchers.util.Direction;
25import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
26import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
27import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity;
28
29/**
30 * Simple source-target bidirectional mapping.
31 *
32 * <p>
33 * TODO: specialize for to-one features and unique to-many features
34 * <p>
35 * TODO: on-demand construction of valueToHolderMap
36 * <p>
37 * TODO: support for lean indexing, opposites, long surrogate ids, etc.
38 * <p>
39 * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
40 * part of a work in progress. There is no guarantee that this API will
41 * work or that it will remain the same.
42 *
43 * @since 2.0
44 * @author Gabor Bergmann
45 */
46public class SimpleBinaryTable<Source, Target> extends AbstractIndexTable
47 implements ITableWriterBinary.Table<Source, Target> {
48
49 /**
50 * value -> holder(s)
51 * <p>
52 * this is currently the primary store, may hold duplicates depending on the unique parameter
53 */
54 private IMultiLookup<Target, Source> valueToHolderMap;
55 /**
56 * holder -> value(s); constructed on-demand, null if unused
57 */
58 private IMultiLookup<Source, Target> holderToValueMap;
59 private int totalRowCount = 0;
60 private boolean unique;
61
62 /**
63 * @param unique
64 * client promises to only insert a given tuple with multiplicity one
65 */
66 public SimpleBinaryTable(IInputKey inputKey, ITableContext tableContext, boolean unique) {
67 super(inputKey, tableContext);
68 this.unique = unique;
69 valueToHolderMap = CollectionsFactory.createMultiLookup(Object.class,
70 unique ? MemoryType.SETS : MemoryType.MULTISETS, Object.class);
71 if (2 != inputKey.getArity())
72 throw new IllegalArgumentException(inputKey.toString());
73 }
74
75 @Override
76 public void write(Direction direction, Source holder, Target value) {
77 if (direction == Direction.INSERT) {
78 try {
79 // TODO we currently assume V2H map exists
80 boolean changed = addToValueToHolderMap(value, holder);
81 if (holderToValueMap != null) {
82 addToHolderToValueMap(value, holder);
83 }
84 if (changed) {
85 totalRowCount++;
86 if (emitNotifications) {
87 deliverChangeNotifications(Tuples.staticArityFlatTupleOf(holder, value), true);
88 }
89 }
90 } catch (IllegalStateException ex) { // if unique table and duplicate tuple
91 String msg = String.format(
92 "Error: trying to add duplicate value %s to the unique feature %s of host object %s. This indicates some errors in underlying model representation.",
93 value, getInputKey().getPrettyPrintableName(), holder);
94 logError(msg);
95 }
96 } else { // DELETE
97 try {
98 // TODO we currently assume V2H map exists
99 boolean changed = removeFromValueToHolderMap(value, holder);
100 if (holderToValueMap != null) {
101 removeFromHolderToValueMap(value, holder);
102 }
103 if (changed) {
104 totalRowCount--;
105 if (emitNotifications) {
106 deliverChangeNotifications(Tuples.staticArityFlatTupleOf(holder, value), false);
107 }
108 }
109 } catch (IllegalStateException ex) { // if unique table and duplicate tuple
110 String msg = String.format(
111 "Error: trying to remove non-existing value %s from the feature %s of host object %s. This indicates some errors in underlying model representation.",
112 value, getInputKey().getPrettyPrintableName(), holder);
113 logError(msg);
114 }
115 }
116 }
117
118 private boolean addToHolderToValueMap(Target value, Source holder) {
119 return ChangeGranularity.DUPLICATE != holderToValueMap.addPair(holder, value);
120 }
121
122 private boolean addToValueToHolderMap(Target value, Source holder) {
123 return ChangeGranularity.DUPLICATE != valueToHolderMap.addPair(value, holder);
124 }
125
126 /**
127 * @throws IllegalStateException
128 */
129 private boolean removeFromHolderToValueMap(Target value, Source holder) {
130 return ChangeGranularity.DUPLICATE != holderToValueMap.removePair(holder, value);
131 }
132
133 /**
134 * @throws IllegalStateException
135 */
136 private boolean removeFromValueToHolderMap(Target value, Source holder) {
137 return ChangeGranularity.DUPLICATE != valueToHolderMap.removePair(value, holder);
138 }
139
140 @SuppressWarnings("unchecked")
141 @Override
142 public int countTuples(TupleMask seedMask, ITuple seed) {
143 switch (seedMask.getSize()) {
144 case 0: // unseeded
145 // TODO we currently assume V2H map exists
146 return totalRowCount;
147 case 1: // lookup by source or target
148 int seedIndex = seedMask.indices[0];
149 if (seedIndex == 0) { // lookup by source
150 Source source = (Source) seed.get(0);
151 return getDistinctValuesOfHolder(source).size();
152 } else if (seedIndex == 1) { // lookup by target
153 Target target = (Target) seed.get(0);
154 return getDistinctHoldersOfValue(target).size();
155 } else
156 throw new IllegalArgumentException(seedMask.toString());
157 case 2: // containment check
158 // hack: if mask is not identity, then it is [1,0]/2, which is its own inverse
159 Source source = (Source) seedMask.getValue(seed, 0);
160 Target target = (Target) seedMask.getValue(seed, 1);
161 if (containsRow(source, target))
162 return 1;
163 else
164 return 0;
165 default:
166 throw new IllegalArgumentException(seedMask.toString());
167 }
168 }
169
170 @Override
171 public Optional<Long> estimateProjectionSize(TupleMask groupMask, Accuracy requiredAccuracy) {
172 // always exact count
173 if (groupMask.getSize() == 0) {
174 return totalRowCount == 0 ? Optional.of(0L) : Optional.of(1L);
175 } else if (groupMask.getSize() == 2) {
176 return Optional.of((long)totalRowCount);
177 } else if (groupMask.indices[0] == 0) { // project to holder
178 return Optional.of((long)getHolderToValueMap().countKeys());
179 } else { // (groupMask.indices[0] == 0) // project to value
180 return Optional.of((long)getValueToHolderMap().countKeys());
181 }
182 }
183
184 @Override
185 public Stream<? extends Tuple> streamTuples(TupleMask seedMask, ITuple seed) {
186 switch (seedMask.getSize()) {
187 case 0: // unseeded
188 // TODO we currently assume V2H map exists
189 return getAllDistinctValuesStream()
190 .flatMap(value -> valueToHolderMap.lookup(value).distinctValues().stream()
191 .map(source -> Tuples.staticArityFlatTupleOf(source, value)));
192
193 case 1: // lookup by source or target
194 int seedIndex = seedMask.indices[0];
195 if (seedIndex == 0) { // lookup by source
196 @SuppressWarnings("unchecked")
197 Source source = (Source) seed.get(0);
198 return getDistinctValuesOfHolder(source).stream()
199 .map(target -> Tuples.staticArityFlatTupleOf(source, target));
200 } else if (seedIndex == 1) { // lookup by target
201 @SuppressWarnings("unchecked")
202 Target target = (Target) seed.get(0);
203 return getDistinctHoldersOfValue(target).stream()
204 .map(source -> Tuples.staticArityFlatTupleOf(source, target));
205 } else
206 throw new IllegalArgumentException(seedMask.toString());
207 case 2: // containment check
208 // hack: if mask is not identity, then it is [1,0]/2, which is its own inverse
209 @SuppressWarnings("unchecked")
210 Source source = (Source) seedMask.getValue(seed, 0);
211 @SuppressWarnings("unchecked")
212 Target target = (Target) seedMask.getValue(seed, 1);
213
214 if (containsRow(source, target))
215 return Stream.of(Tuples.staticArityFlatTupleOf(source, target));
216 else
217 return Stream.empty();
218 default:
219 throw new IllegalArgumentException(seedMask.toString());
220 }
221 }
222
223 @Override
224 @SuppressWarnings("unchecked")
225 public Iterable<? extends Object> enumerateValues(TupleMask seedMask, ITuple seed) {
226 if (seedMask.getSize() != 1)
227 throw new IllegalArgumentException(seedMask.toString());
228
229 int seedIndex = seedMask.indices[0];
230 if (seedIndex == 0) { // lookup by source
231 return getDistinctValuesOfHolder((Source) seed.get(0));
232 } else if (seedIndex == 1) { // lookup by target
233 return getDistinctHoldersOfValue((Target) seed.get(0));
234 } else
235 throw new IllegalArgumentException(seedMask.toString());
236
237 }
238 @Override
239 public Stream<? extends Object> streamValues(TupleMask seedMask, ITuple seed) {
240 if (seedMask.getSize() != 1)
241 throw new IllegalArgumentException(seedMask.toString());
242
243 int seedIndex = seedMask.indices[0];
244 if (seedIndex == 0) { // lookup by source
245 return getDistinctValuesOfHolder((Source) seed.get(0)).stream();
246 } else if (seedIndex == 1) { // lookup by target
247 return getDistinctHoldersOfValue((Target) seed.get(0)).stream();
248 } else
249 throw new IllegalArgumentException(seedMask.toString());
250
251 }
252
253 @Override
254 @SuppressWarnings("unchecked")
255 public boolean containsTuple(ITuple seed) {
256 return containsRow((Source) seed.get(0), (Target) seed.get(1));
257 }
258
259 public boolean containsRow(Source source, Target target) {
260 // TODO we currently assume V2H map exists
261 if (valueToHolderMap != null) {
262 IMemoryView<Source> holders = valueToHolderMap.lookup(target);
263 return holders != null && holders.containsNonZero(source);
264 } else
265 throw new UnsupportedOperationException("TODO implement");
266 }
267
268 public Iterable<Source> getAllDistinctHolders() {
269 return getHolderToValueMap().distinctKeys();
270 }
271 public Stream<Source> getAllDistinctHoldersStream() {
272 return getHolderToValueMap().distinctKeysStream();
273 }
274 public Iterable<Target> getAllDistinctValues() {
275 return getValueToHolderMap().distinctKeys();
276 }
277 public Stream<Target> getAllDistinctValuesStream() {
278 return getValueToHolderMap().distinctKeysStream();
279 }
280
281 public Set<Source> getDistinctHoldersOfValue(Target value) {
282 IMemoryView<Source> holdersMultiset = getValueToHolderMap().lookup(value);
283 if (holdersMultiset == null)
284 return Collections.emptySet();
285 else
286 return holdersMultiset.distinctValues();
287 }
288
289 public Set<Target> getDistinctValuesOfHolder(Source holder) {
290 IMemoryView<Target> valuesMultiset = getHolderToValueMap().lookup(holder);
291 if (valuesMultiset == null)
292 return Collections.emptySet();
293 else
294 return valuesMultiset.distinctValues();
295 }
296
297 private IMultiLookup<Source, Target> getHolderToValueMap() {
298 if (holderToValueMap == null) {
299 holderToValueMap = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, // no duplicates, as
300 // this is the
301 // secondary
302 // collection
303 Object.class);
304
305 // TODO we currently assume V2H map exists
306 for (Target value : valueToHolderMap.distinctKeys()) {
307 for (Source holder : valueToHolderMap.lookup(value).distinctValues()) {
308 holderToValueMap.addPair(holder, value);
309 }
310 }
311 }
312 return holderToValueMap;
313 }
314
315 private IMultiLookup<Target, Source> getValueToHolderMap() {
316 // TODO we currently assume V2H map exists
317 return valueToHolderMap;
318 }
319
320}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleUnaryTable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleUnaryTable.java
new file mode 100644
index 00000000..428ea78b
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleUnaryTable.java
@@ -0,0 +1,140 @@
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.matchers.scopes.tables;
11
12import java.util.Optional;
13import java.util.stream.Stream;
14
15import tools.refinery.viatra.runtime.matchers.context.IInputKey;
16import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
20import tools.refinery.viatra.runtime.matchers.util.Accuracy;
21import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
22import tools.refinery.viatra.runtime.matchers.util.Direction;
23import tools.refinery.viatra.runtime.matchers.util.IMemory;
24
25/**
26 * Simple value set.
27 * <p>
28 * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
29 * part of a work in progress. There is no guarantee that this API will
30 * work or that it will remain the same.
31 *
32 * @since 2.0
33 * @author Gabor Bergmann
34 */
35public class SimpleUnaryTable<Value> extends AbstractIndexTable implements ITableWriterUnary.Table<Value> {
36
37 protected IMemory<Value> values = CollectionsFactory.createMultiset(); // TODO use SetMemory if unique
38
39 private boolean unique;
40
41 /**
42 * @param unique
43 * client promises to only insert a given tuple with multiplicity one
44 */
45 public SimpleUnaryTable(IInputKey inputKey, ITableContext tableContext, boolean unique) {
46 super(inputKey, tableContext);
47 this.unique = unique;
48 if (1 != inputKey.getArity())
49 throw new IllegalArgumentException(inputKey.toString());
50 }
51
52 @Override
53 public void write(Direction direction, Value value) {
54 if (direction == Direction.INSERT) {
55 boolean changed = values.addOne(value);
56 if (unique && !changed) {
57 String msg = String.format(
58 "Error: trying to add duplicate value %s to the unique set %s. This indicates some errors in underlying model representation.",
59 value, getInputKey().getPrettyPrintableName());
60 logError(msg);
61 }
62 if (changed && emitNotifications) {
63 deliverChangeNotifications(Tuples.staticArityFlatTupleOf(value), true);
64 }
65 } else { // DELETE
66 boolean changed = values.removeOne(value);
67 if (unique && !changed) {
68 String msg = String.format(
69 "Error: trying to remove duplicate value %s from the unique set %s. This indicates some errors in underlying model representation.",
70 value, getInputKey().getPrettyPrintableName());
71 logError(msg);
72 }
73 if (changed && emitNotifications) {
74 deliverChangeNotifications(Tuples.staticArityFlatTupleOf(value), false);
75 }
76 }
77 }
78
79 @Override
80 @SuppressWarnings("unchecked")
81 public boolean containsTuple(ITuple seed) {
82 return values.containsNonZero((Value) seed.get(0));
83 }
84
85 @Override
86 public int countTuples(TupleMask seedMask, ITuple seed) {
87 if (seedMask.getSize() == 0) { // unseeded
88 return values.size();
89 } else {
90 @SuppressWarnings("unchecked")
91 Value value = (Value) seed.get(0);
92 return values.containsNonZero(value) ? 1 : 0;
93 }
94 }
95
96
97 @Override
98 public Optional<Long> estimateProjectionSize(TupleMask groupMask, Accuracy requiredAccuracy) {
99 // always exact count
100 if (groupMask.getSize() == 0) {
101 return values.isEmpty() ? Optional.of(0L) : Optional.of(1L);
102 } else {
103 return Optional.of((long)values.size());
104 }
105 }
106
107
108 @Override
109 public Stream<? extends Tuple> streamTuples(TupleMask seedMask, ITuple seed) {
110 if (seedMask.getSize() == 0) { // unseeded
111 return values.distinctValues().stream().map(Tuples::staticArityFlatTupleOf);
112 } else {
113 @SuppressWarnings("unchecked")
114 Value value = (Value) seed.get(0);
115 if (values.containsNonZero(value))
116 return Stream.of(Tuples.staticArityFlatTupleOf(value));
117 else
118 return Stream.empty();
119 }
120 }
121
122 @Override
123 public Iterable<? extends Object> enumerateValues(TupleMask seedMask, ITuple seed) {
124 if (seedMask.getSize() == 0) { // unseeded
125 return values;
126 } else {
127 throw new IllegalArgumentException(seedMask.toString());
128 }
129 }
130 @Override
131 public Stream<? extends Object> streamValues(TupleMask seedMask, ITuple seed) {
132 if (seedMask.getSize() == 0) { // unseeded
133 return values.asStream();
134 } else {
135 throw new IllegalArgumentException(seedMask.toString());
136 }
137 }
138
139
140}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java
new file mode 100644
index 00000000..a26d9193
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java
@@ -0,0 +1,136 @@
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.matchers.tuple;
10
11import java.util.ArrayList;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Map;
16import java.util.Objects;
17import java.util.Set;
18
19/**
20 * Common implementation methods for immutable and volatile tuples. The class should not be used directly in client
21 * code, except for the definition of new tuple implementations.
22 *
23 * @author Zoltan Ujhelyi
24 * @since 1.7
25 */
26public abstract class AbstractTuple implements ITuple {
27
28 /**
29 * As the tuple is supposed to be immutable, do not modify the returned array.
30 *
31 * @return the array containing all elements of this Tuple
32 */
33 @Override
34 public Object[] getElements() {
35 Object[] allElements = new Object[getSize()];
36 for (int i = 0; i < allElements.length; ++i)
37 allElements[i] = get(i);
38 return allElements;
39 }
40
41 /**
42 * @return the set containing all distinct elements of this Tuple, cast as type T
43 */
44 @Override
45 @SuppressWarnings("unchecked")
46 public <T> Set<T> getDistinctElements() {
47 Set<T> result = new HashSet<T>();
48 Object[] elements = getElements();
49 for (Object object : elements) {
50 result.add((T) object);
51 }
52 return result;
53 }
54
55 /**
56 * Calculates an inverted index of the elements of this pattern. For each element, the index of the (last)
57 * occurrence is calculated.
58 *
59 * @return the inverted index mapping each element of this pattern to its index in the array
60 */
61 @Override
62 public Map<Object, Integer> invertIndex() {
63 Map<Object, Integer> result = new HashMap<Object, Integer>();
64 for (int i = 0; i < getSize(); i++)
65 result.put(get(i), i);
66 return result;
67 }
68
69 /**
70 * Calculates an inverted index of the elements of this pattern. For each element, the index of all of its
71 * occurrences is calculated.
72 *
73 * @return the inverted index mapping each element of this pattern to its index in the array
74 */
75 @Override
76 public Map<Object, List<Integer>> invertIndexWithMupliplicity() {
77 Map<Object, List<Integer>> result = new HashMap<Object, List<Integer>>();
78 for (int i = 0; i < getSize(); i++) {
79 Object value = get(i);
80 List<Integer> indices = result.computeIfAbsent(value, v -> new ArrayList<>());
81 indices.add(i);
82 }
83 return result;
84 }
85
86 /**
87 * @since 1.7
88 */
89 protected IndexOutOfBoundsException raiseIndexingError(int index) {
90 return new IndexOutOfBoundsException(
91 String.format("No value at position %d for %s instance %s", index, getClass().getSimpleName(), this));
92 }
93
94 /**
95 * Compares the elements stored in this tuple to another tuple
96 */
97 protected boolean internalEquals(ITuple other) {
98 if (getSize() != other.getSize())
99 return false;
100 boolean result = true;
101 for (int i = 0; result && i < getSize(); ++i) {
102 Object ours = get(i);
103 Object theirs = other.get(i);
104 result = result && Objects.equals(ours, theirs);
105 }
106 return result;
107 }
108
109 @Override
110 public String toString() {
111 StringBuilder s = new StringBuilder();
112 s.append("T(");
113 for (Object o : getElements()) {
114 s.append(o == null ? "null" : o.toString());
115 s.append(';');
116 }
117 s.append(')');
118 return s.toString();
119 }
120
121 /**
122 * @since 1.7
123 */
124 protected int doCalcHash() {
125 final int PRIME = 31;
126 int hash = 1;
127 for (int i = 0; i < getSize(); i++) {
128 hash = PRIME * hash;
129 Object element = get(i);
130 if (element != null)
131 hash += element.hashCode();
132 }
133 return hash;
134 }
135
136} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java
new file mode 100644
index 00000000..6f46b763
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java
@@ -0,0 +1,20 @@
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.matchers.tuple;
10
11/**
12 * Base class for all flat tuple implementations.
13 * Flat tuples store all elements locally (do not reference other tuples).
14 *
15 * @author Gabor Bergmann
16 * @since 1.7
17 */
18public abstract class BaseFlatTuple extends Tuple {
19
20}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java
new file mode 100644
index 00000000..03f9ea89
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java
@@ -0,0 +1,65 @@
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.matchers.tuple;
10
11/**
12 * Common functionality of left inheritance tuple implementations.
13 *
14 * <p>Left inheritance tuples inherit their first few elements from another tuple,
15 * and extend it with additional "local" elements.
16 *
17 * @author Gabor Bergmann
18 * @since 1.7
19 */
20public abstract class BaseLeftInheritanceTuple extends Tuple {
21
22 /**
23 * The number of elements that aren't stored locally, but inherited from an ancestor Tuple instead.
24 */
25 protected final int inheritedIndex;
26 /**
27 * This object contains the same elements as the ancestor on the first inheritedIndex positions
28 */
29 protected final Tuple ancestor;
30
31 /**
32 * @param ancestor
33 */
34 public BaseLeftInheritanceTuple(Tuple ancestor) {
35 super();
36 this.ancestor = ancestor;
37 this.inheritedIndex = ancestor.getSize();
38 }
39
40 /**
41 * @return the number of local (non-inherited) elements
42 */
43 public abstract int getLocalSize();
44
45 /**
46 * Optimized equals calculation (prediction: true, since hash values match)
47 */
48 @Override
49 protected boolean internalEquals(ITuple other) {
50 if (other instanceof BaseLeftInheritanceTuple) {
51 BaseLeftInheritanceTuple blit = (BaseLeftInheritanceTuple) other;
52 if (blit.inheritedIndex == this.inheritedIndex) {
53 if (this.ancestor.equals(blit.ancestor)) {
54 return localEquals(blit);
55 } else return false;
56 }
57 }
58 return super.internalEquals(other);
59 }
60
61 /**
62 * Checks the equivalence of local elements only, after ancestor tuple has been determined to be equal.
63 */
64 protected abstract boolean localEquals(BaseLeftInheritanceTuple other);
65}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java
new file mode 100644
index 00000000..8bbb0ac2
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java
@@ -0,0 +1,60 @@
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.matchers.tuple;
11
12import java.util.Arrays;
13
14/**
15 * Default Tuple implementation, with statically unknown arity.
16 * @author Gabor Bergmann
17 */
18public final class FlatTuple extends BaseFlatTuple {
19
20 /**
21 * Array of substituted values. DO NOT MODIFY! Use Constructor to build a new instance instead.
22 */
23 private final Object[] elements;
24
25 /**
26 * Creates a FlatTuple instance, fills it with the given array.
27 * <p> Users should consider calling {@link Tuples#flatTupleOf(Object...)} instead to save memory on low-arity tuples.
28 *
29 * @param elements
30 * array of substitution values
31 */
32 protected FlatTuple(Object... elements) {
33 this.elements = Arrays.copyOf(elements, elements.length);
34 calcHash();
35 }
36
37 @Override
38 public Object get(int index) {
39 return elements[index];
40 }
41
42 @Override
43 public int getSize() {
44 return elements.length;
45 }
46
47 @Override
48 public Object[] getElements() {
49 return elements;
50 }
51
52 @Override
53 protected boolean internalEquals(ITuple other) {
54 if (other instanceof FlatTuple) {
55 return Arrays.equals(elements, ((FlatTuple) other).elements);
56 } else
57 return super.internalEquals(other);
58 }
59
60}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java
new file mode 100644
index 00000000..93f412b7
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.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.matchers.tuple;
10
11/**
12 * Flat tuple with statically known arity of 0.
13 *
14 * @author Gabor Bergmann
15 * @since 1.7
16 *
17 */
18public final class FlatTuple0 extends BaseFlatTuple {
19 protected static final FlatTuple0 INSTANCE = new FlatTuple0();
20
21 private FlatTuple0() {
22 calcHash();
23 }
24
25 @Override
26 public int getSize() {
27 return 0;
28 }
29
30 @Override
31 public Object get(int index) {
32 throw raiseIndexingError(index);
33 }
34
35 private static final Object[] NULLARY_ARRAY = new Object[0];
36
37 @Override
38 public Object[] getElements() {
39 return NULLARY_ARRAY;
40 }
41
42 @Override
43 protected boolean internalEquals(ITuple other) {
44 return 0 == other.getSize();
45 }
46}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java
new file mode 100644
index 00000000..b3b1c312
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java
@@ -0,0 +1,44 @@
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.matchers.tuple;
10
11import java.util.Objects;
12
13/**
14 * Flat tuple with statically known arity of 1.
15 *
16 * @author Gabor Bergmann
17 * @since 1.7
18 *
19 */
20public final class FlatTuple1 extends BaseFlatTuple {
21 private final Object element0;
22
23 protected FlatTuple1(Object element0) {
24 this.element0 = element0;
25 calcHash();
26 }
27
28 @Override
29 public int getSize() {
30 return 1;
31 }
32
33 @Override
34 public Object get(int index) {
35 if (index == 0) return element0;
36 else throw raiseIndexingError(index);
37 }
38
39 @Override
40 protected boolean internalEquals(ITuple other) {
41 return 1 == other.getSize() &&
42 Objects.equals(element0, other.get(0));
43 }
44}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java
new file mode 100644
index 00000000..2dcfd718
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java
@@ -0,0 +1,51 @@
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.matchers.tuple;
10
11import java.util.Objects;
12
13/**
14 * Flat tuple with statically known arity of 2.
15 *
16 * @author Gabor Bergmann
17 * @since 1.7
18 *
19 */
20public final class FlatTuple2 extends BaseFlatTuple {
21 private final Object element0;
22 private final Object element1;
23
24 protected FlatTuple2(Object element0, Object element1) {
25 this.element0 = element0;
26 this.element1 = element1;
27 calcHash();
28 }
29
30 @Override
31 public int getSize() {
32 return 2;
33 }
34
35 @Override
36 public Object get(int index) {
37 switch(index) {
38 case 0 : return element0;
39 case 1 : return element1;
40 default: throw raiseIndexingError(index);
41 }
42 }
43
44 @Override
45 protected boolean internalEquals(ITuple other) {
46 return 2 == other.getSize() &&
47 Objects.equals(element0, other.get(0)) &&
48 Objects.equals(element1, other.get(1));
49 }
50
51}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java
new file mode 100644
index 00000000..50cee57e
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java
@@ -0,0 +1,55 @@
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.matchers.tuple;
10
11import java.util.Objects;
12
13/**
14 * Flat tuple with statically known arity of 3.
15 *
16 * @author Gabor Bergmann
17 * @since 1.7
18 *
19 */
20public final class FlatTuple3 extends BaseFlatTuple {
21 private final Object element0;
22 private final Object element1;
23 private final Object element2;
24
25 protected FlatTuple3(Object element0, Object element1, Object element2) {
26 this.element0 = element0;
27 this.element1 = element1;
28 this.element2 = element2;
29 calcHash();
30 }
31
32 @Override
33 public int getSize() {
34 return 3;
35 }
36
37 @Override
38 public Object get(int index) {
39 switch (index) {
40 case 0: return element0;
41 case 1: return element1;
42 case 2: return element2;
43 default: throw raiseIndexingError(index);
44 }
45 }
46
47 @Override
48 protected boolean internalEquals(ITuple other) {
49 return 3 == other.getSize() &&
50 Objects.equals(element0, other.get(0)) &&
51 Objects.equals(element1, other.get(1)) &&
52 Objects.equals(element2, other.get(2));
53 }
54
55}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java
new file mode 100644
index 00000000..024c94f4
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java
@@ -0,0 +1,59 @@
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.matchers.tuple;
10
11import java.util.Objects;
12
13/**
14 * Flat tuple with statically known arity of 4.
15 *
16 * @author Gabor Bergmann
17 * @since 1.7
18 *
19 */
20public final class FlatTuple4 extends BaseFlatTuple {
21 private final Object element0;
22 private final Object element1;
23 private final Object element2;
24 private final Object element3;
25
26 protected FlatTuple4(Object element0, Object element1, Object element2, Object element3) {
27 this.element0 = element0;
28 this.element1 = element1;
29 this.element2 = element2;
30 this.element3 = element3;
31 calcHash();
32 }
33
34 @Override
35 public int getSize() {
36 return 4;
37 }
38
39 @Override
40 public Object get(int index) {
41 switch(index) {
42 case 0: return element0;
43 case 1: return element1;
44 case 2: return element2;
45 case 3: return element3;
46 default: throw raiseIndexingError(index);
47 }
48 }
49
50 @Override
51 protected boolean internalEquals(ITuple other) {
52 return 4 == other.getSize() &&
53 Objects.equals(element0, other.get(0)) &&
54 Objects.equals(element1, other.get(1)) &&
55 Objects.equals(element2, other.get(2)) &&
56 Objects.equals(element3, other.get(3));
57 }
58
59}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java
new file mode 100644
index 00000000..f5dab2a5
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java
@@ -0,0 +1,27 @@
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.matchers.tuple;
10
11/**
12 * A tuple that allows modifying the underlying value. Should not be used for non-volatile tuples.
13 *
14 * @author Zoltan Ujhelyi
15 * @since 1.7
16 */
17public interface IModifiableTuple extends ITuple {
18
19 /**
20 * Sets the selected value for a tuple
21 *
22 * @pre: 0 <= index < getSize()
23 *
24 */
25 void set(int index, Object value);
26
27}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java
new file mode 100644
index 00000000..92014781
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java
@@ -0,0 +1,64 @@
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.matchers.tuple;
10
11import java.util.List;
12import java.util.Map;
13import java.util.Set;
14
15/**
16 * Represents both mutable and immutable tuples
17 *
18 * @author Zoltan Ujhelyi
19 * @since 1.7
20 *
21 */
22public interface ITuple {
23
24 /**
25 * @pre: 0 <= index < getSize()
26 *
27 * @return the element at the specified index
28 */
29 Object get(int index);
30
31 /**
32 * As the tuple is supposed to be immutable, do not modify the returned array.
33 * @return the array containing all elements of this Tuple
34 */
35 Object[] getElements();
36
37 /**
38 * @return the set containing all distinct elements of this Tuple, cast as type T
39 */
40 <T> Set<T> getDistinctElements();
41
42 /**
43 * @return number of elements
44 */
45 int getSize();
46
47 /**
48 * Calculates an inverted index of the elements of this pattern. For each element, the index of the (last)
49 * occurrence is calculated.
50 *
51 * @return the inverted index mapping each element of this pattern to its index in the array
52 */
53 Map<Object, Integer> invertIndex();
54
55 /**
56 * Calculates an inverted index of the elements of this pattern. For each element, the index of all of its
57 * occurrences is calculated.
58 *
59 * @return the inverted index mapping each element of this pattern to its index in the array
60 */
61 Map<Object, List<Integer>> invertIndexWithMupliplicity();
62
63 Tuple toImmutable();
64} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java
new file mode 100644
index 00000000..dcdfc376
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java
@@ -0,0 +1,172 @@
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.matchers.tuple;
11
12import java.util.Arrays;
13import java.util.Objects;
14
15/**
16 *
17 * Tuple that inherits another tuple on the left.
18 *
19 * @author Gabor Bergmann
20 *
21 */
22public final class LeftInheritanceTuple extends BaseLeftInheritanceTuple {
23 /**
24 * Array of substituted values above inheritedIndex. DO NOT MODIFY! Use Constructor to build a new instance instead.
25 */
26 private final Object[] localElements;
27
28 //
29 // /**
30 // * Creates a Tuple instance, fills it with the given array.
31 // * @pre: no elements are null
32 // * @param elements array of substitution values
33 // */
34 // public Tuple(Object[] elements)
35 // {
36 // this.localElements = elements;
37 // this.ancestor=null;
38 // this.inheritedIndex = 0;
39 // calcHash();
40 // }
41
42
43
44 /**
45 * Creates a Tuple instance, lets it inherit from an ancestor, extends it with a given array. @pre: no elements are
46 * null
47 *
48 * @param localElements
49 * array of substitution values
50 */
51 LeftInheritanceTuple(Tuple ancestor, Object[] localElements) {
52 super(ancestor);
53 this.localElements = localElements;
54 calcHash();
55 }
56
57 //
58 // /**
59 // * Creates a Tuple instance of size one, fills it with the given object.
60 // * @pre: o!=null
61 // * @param o the single substitution
62 // */
63 // public Tuple(Object o)
64 // {
65 // localElements = new Object [1];
66 // localElements[0] = o;
67 // this.ancestor=null;
68 // this.inheritedIndex = 0;
69 // calcHash();
70 // }
71 //
72 // /**
73 // * Creates a Tuple instance of size two, fills it with the given objects.
74 // * @pre: o1!=null, o2!=null
75 // */
76 // public Tuple(Object o1, Object o2)
77 // {
78 // localElements = new Object [2];
79 // localElements[0] = o1;
80 // localElements[1] = o2;
81 // this.ancestor=null;
82 // this.inheritedIndex = 0;
83 // calcHash();
84 // }
85 //
86 // /**
87 // * Creates a Tuple instance of size three, fills it with the given
88 // objects.
89 // * @pre: o1!=null, o2!=null, o3!=null
90 // */
91 // public Tuple(Object o1, Object o2, Object o3)
92 // {
93 // localElements = new Object [3];
94 // localElements[0] = o1;
95 // localElements[1] = o2;
96 // localElements[2] = o3;
97 // this.ancestor=null;
98 // this.inheritedIndex = 0;
99 // calcHash();
100 // }
101
102 /**
103 * @return number of elements
104 */
105 public int getSize() {
106 return inheritedIndex + localElements.length;
107 }
108
109 @Override
110 public int getLocalSize() {
111 return localElements.length;
112 }
113
114 /**
115 * @pre: 0 <= index < getSize()
116 *
117 * @return the element at the specified index
118 */
119 public Object get(int index) {
120 return (index < inheritedIndex) ? ancestor.get(index) : localElements[index - inheritedIndex];
121 }
122
123 /**
124 * Optimized hash calculation
125 */
126 @Override
127 void calcHash() {
128 final int PRIME = 31;
129 cachedHash = ancestor.hashCode();
130 for (int i = 0; i < localElements.length; i++) {
131 cachedHash = PRIME * cachedHash;
132 Object element = localElements[i];
133 if (element != null)
134 cachedHash += element.hashCode();
135 }
136 }
137
138 /**
139 * Optimized equals calculation (prediction: true, since hash values match)
140 */
141 @Override
142 protected boolean localEquals(BaseLeftInheritanceTuple other) {
143 if (other instanceof LeftInheritanceTuple) {
144 LeftInheritanceTuple lit = (LeftInheritanceTuple)other;
145 return Arrays.equals(this.localElements, lit.localElements);
146 } else {
147 if (localElements.length != other.getLocalSize())
148 return false;
149 int index = inheritedIndex;
150 for (int i = 0; i<localElements.length; ++i) {
151 if (! Objects.equals(localElements[i], other.get(index++)))
152 return false;
153 }
154 return true;
155 }
156 }
157
158 // public int compareTo(Object arg0) {
159 // Tuple other = (Tuple) arg0;
160 //
161 // int retVal = cachedHash - other.cachedHash;
162 // if (retVal==0) retVal = elements.length - other.elements.length;
163 // for (int i=0; retVal==0 && i<elements.length; ++i)
164 // {
165 // if (elements[i] == null && other.elements[i] != null) retVal = -1;
166 // else if (other.elements[i] == null) retVal = 1;
167 // else retVal = elements[i].compareTo(other.elements[i]);
168 // }
169 // return retVal;
170 // }
171
172}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple1.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple1.java
new file mode 100644
index 00000000..61123176
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple1.java
@@ -0,0 +1,83 @@
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.matchers.tuple;
10
11import java.util.Objects;
12
13/**
14 * @author Gabor Bergmann
15 * @since 1.7
16 */
17public final class LeftInheritanceTuple1 extends BaseLeftInheritanceTuple {
18 /**
19 * A single substituted value after inheritedIndex.
20 */
21 private final Object localElement;
22
23 /**
24 * @param ancestor
25 * @param localElement
26 */
27 protected LeftInheritanceTuple1(Tuple ancestor, Object localElement) {
28 super(ancestor);
29 this.localElement = localElement;
30 calcHash();
31 }
32
33 /**
34 * @return number of elements
35 */
36 public int getSize() {
37 return inheritedIndex + 1;
38 }
39
40 @Override
41 public int getLocalSize() {
42 return 1;
43 }
44
45 /**
46 * @pre: 0 <= index < getSize()
47 *
48 * @return the element at the specified index
49 */
50 public Object get(int index) {
51 int local = index - inheritedIndex;
52 if (local < 0)
53 return ancestor.get(index);
54 else if (local == 0) return localElement;
55 else throw raiseIndexingError(index);
56 }
57
58 /**
59 * Optimized hash calculation
60 */
61 @Override
62 void calcHash() {
63 final int PRIME = 31;
64 cachedHash = ancestor.hashCode();
65 cachedHash = PRIME * cachedHash;
66 if (localElement != null) cachedHash += localElement.hashCode();
67 }
68
69 /**
70 * Optimized equals calculation (prediction: true, since hash values match)
71 */
72 @Override
73 protected boolean localEquals(BaseLeftInheritanceTuple other) {
74 if (other instanceof LeftInheritanceTuple1) {
75 LeftInheritanceTuple1 lit = (LeftInheritanceTuple1)other;
76 return Objects.equals(this.localElement, lit.localElement);
77 } else {
78 return (1 == other.getLocalSize()) &&
79 Objects.equals(localElement, other.get(inheritedIndex));
80 }
81 }
82
83}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple2.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple2.java
new file mode 100644
index 00000000..e9fb98e8
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple2.java
@@ -0,0 +1,73 @@
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.matchers.tuple;
10
11import java.util.Objects;
12
13/**
14 * @author Gabor Bergmann
15 * @since 1.7
16 */
17public final class LeftInheritanceTuple2 extends BaseLeftInheritanceTuple {
18 private final Object localElement0;
19 private final Object localElement1;
20
21 protected LeftInheritanceTuple2(Tuple ancestor, Object localElement0, Object localElement1) {
22 super(ancestor);
23 this.localElement0 = localElement0;
24 this.localElement1 = localElement1;
25 calcHash();
26 }
27
28 @Override
29 public int getLocalSize() {
30 return 2;
31 }
32
33 @Override
34 public int getSize() {
35 return inheritedIndex + 2;
36 }
37
38 @Override
39 public Object get(int index) {
40 int local = index - inheritedIndex;
41 if (local < 0)
42 return ancestor.get(index);
43 else if (local == 0) return localElement0;
44 else if (local == 1) return localElement1;
45 else throw raiseIndexingError(index);
46 }
47
48 /**
49 * Optimized hash calculation
50 */
51 @Override
52 void calcHash() {
53 final int PRIME = 31;
54 cachedHash = ancestor.hashCode();
55 cachedHash = PRIME * cachedHash;
56 if (localElement0 != null) cachedHash += localElement0.hashCode();
57 cachedHash = PRIME * cachedHash;
58 if (localElement1 != null) cachedHash += localElement1.hashCode();
59 }
60
61 @Override
62 protected boolean localEquals(BaseLeftInheritanceTuple other) {
63 if (other instanceof LeftInheritanceTuple2) {
64 LeftInheritanceTuple2 lit = (LeftInheritanceTuple2)other;
65 return Objects.equals(this.localElement0, lit.localElement0) &&
66 Objects.equals(this.localElement1, lit.localElement1);
67 } else {
68 return (2 == other.getLocalSize()) &&
69 Objects.equals(localElement0, other.get(inheritedIndex)) &&
70 Objects.equals(localElement1, other.get(inheritedIndex + 1));
71 }
72 }
73}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple3.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple3.java
new file mode 100644
index 00000000..e61d196f
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple3.java
@@ -0,0 +1,81 @@
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.matchers.tuple;
10
11import java.util.Objects;
12
13/**
14 * @author Gabor Bergmann
15 * @since 1.7
16 */
17public final class LeftInheritanceTuple3 extends BaseLeftInheritanceTuple {
18 private final Object localElement0;
19 private final Object localElement1;
20 private final Object localElement2;
21
22 protected LeftInheritanceTuple3(Tuple ancestor, Object localElement0, Object localElement1, Object localElement2) {
23 super(ancestor);
24 this.localElement0 = localElement0;
25 this.localElement1 = localElement1;
26 this.localElement2 = localElement2;
27 calcHash();
28 }
29
30 @Override
31 public int getLocalSize() {
32 return 3;
33 }
34
35 @Override
36 public int getSize() {
37 return inheritedIndex + 3;
38 }
39
40 @Override
41 public Object get(int index) {
42 int local = index - inheritedIndex;
43 if (local < 0)
44 return ancestor.get(index);
45 else if (local == 0) return localElement0;
46 else if (local == 1) return localElement1;
47 else if (local == 2) return localElement2;
48 else throw raiseIndexingError(index);
49 }
50
51 /**
52 * Optimized hash calculation
53 */
54 @Override
55 void calcHash() {
56 final int PRIME = 31;
57 cachedHash = ancestor.hashCode();
58 cachedHash = PRIME * cachedHash;
59 if (localElement0 != null) cachedHash += localElement0.hashCode();
60 cachedHash = PRIME * cachedHash;
61 if (localElement1 != null) cachedHash += localElement1.hashCode();
62 cachedHash = PRIME * cachedHash;
63 if (localElement2 != null) cachedHash += localElement2.hashCode();
64 }
65
66 @Override
67 protected boolean localEquals(BaseLeftInheritanceTuple other) {
68 if (other instanceof LeftInheritanceTuple3) {
69 LeftInheritanceTuple3 lit = (LeftInheritanceTuple3)other;
70 return Objects.equals(this.localElement0, lit.localElement0) &&
71 Objects.equals(this.localElement1, lit.localElement1) &&
72 Objects.equals(this.localElement2, lit.localElement2);
73 } else {
74 return (3 == other.getLocalSize()) &&
75 Objects.equals(localElement0, other.get(inheritedIndex)) &&
76 Objects.equals(localElement1, other.get(inheritedIndex + 1)) &&
77 Objects.equals(localElement2, other.get(inheritedIndex + 2));
78 }
79 }
80
81}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple4.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple4.java
new file mode 100644
index 00000000..4e50f1e1
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple4.java
@@ -0,0 +1,88 @@
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.matchers.tuple;
10
11import java.util.Objects;
12
13/**
14 * @author Gabor Bergmann
15 * @since 1.7
16 */
17public final class LeftInheritanceTuple4 extends BaseLeftInheritanceTuple {
18 private final Object localElement0;
19 private final Object localElement1;
20 private final Object localElement2;
21 private final Object localElement3;
22
23 protected LeftInheritanceTuple4(Tuple ancestor, Object localElement0, Object localElement1, Object localElement2,
24 Object localElement3) {
25 super(ancestor);
26 this.localElement0 = localElement0;
27 this.localElement1 = localElement1;
28 this.localElement2 = localElement2;
29 this.localElement3 = localElement3;
30 calcHash();
31 }
32
33 @Override
34 public int getLocalSize() {
35 return 4;
36 }
37
38 @Override
39 public int getSize() {
40 return inheritedIndex + 4;
41 }
42
43 @Override
44 public Object get(int index) {
45 int local = index - inheritedIndex;
46 if (local < 0)
47 return ancestor.get(index);
48 else if (local == 0) return localElement0;
49 else if (local == 1) return localElement1;
50 else if (local == 2) return localElement2;
51 else if (local == 3) return localElement3;
52 else throw raiseIndexingError(index);
53 }
54
55 /**
56 * Optimized hash calculation
57 */
58 @Override
59 void calcHash() {
60 final int PRIME = 31;
61 cachedHash = ancestor.hashCode();
62 cachedHash = PRIME * cachedHash;
63 if (localElement0 != null) cachedHash += localElement0.hashCode();
64 cachedHash = PRIME * cachedHash;
65 if (localElement1 != null) cachedHash += localElement1.hashCode();
66 cachedHash = PRIME * cachedHash;
67 if (localElement2 != null) cachedHash += localElement2.hashCode();
68 cachedHash = PRIME * cachedHash;
69 if (localElement3 != null) cachedHash += localElement3.hashCode();
70 }
71
72 @Override
73 protected boolean localEquals(BaseLeftInheritanceTuple other) {
74 if (other instanceof LeftInheritanceTuple4) {
75 LeftInheritanceTuple4 lit = (LeftInheritanceTuple4)other;
76 return Objects.equals(this.localElement0, lit.localElement0) &&
77 Objects.equals(this.localElement1, lit.localElement1) &&
78 Objects.equals(this.localElement2, lit.localElement2) &&
79 Objects.equals(this.localElement3, lit.localElement3);
80 } else {
81 return (4 == other.getLocalSize()) &&
82 Objects.equals(localElement0, other.get(inheritedIndex)) &&
83 Objects.equals(localElement1, other.get(inheritedIndex + 1)) &&
84 Objects.equals(localElement2, other.get(inheritedIndex + 2)) &&
85 Objects.equals(localElement3, other.get(inheritedIndex + 3));
86 }
87 }
88}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/MaskedTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/MaskedTuple.java
new file mode 100644
index 00000000..a5d1991c
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/MaskedTuple.java
@@ -0,0 +1,48 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2009 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.matchers.tuple;
11
12/**
13 * A tuple that transparently provides a masked (transformed) view of another tuple.
14 *
15 * @author Gabor Bergmann
16 * @since 2.0
17 *
18 */
19public class MaskedTuple extends Tuple {
20
21 Tuple wrapped;
22 TupleMask mask;
23
24 public MaskedTuple(Tuple wrapped, TupleMask mask) {
25 super();
26 // if (wrapped instanceof MaskedTuple) {
27 // MaskedTuple parent = (MaskedTuple)wrapped;
28 // this.wrapped = parent.wrapped;
29 // this.mask = mask.transform(parent.mask);
30 // }
31 // else
32 //{
33 this.wrapped = wrapped;
34 this.mask = mask;
35 //}
36 }
37
38 @Override
39 public Object get(int index) {
40 return wrapped.get(mask.indices[index]);
41 }
42
43 @Override
44 public int getSize() {
45 return mask.indices.length;
46 }
47
48}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuple.java
new file mode 100644
index 00000000..d94f545f
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuple.java
@@ -0,0 +1,69 @@
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.matchers.tuple;
11
12/**
13 * Immutable tuple. Obtain instances via utility class {@link Tuples}.
14 * @author Gabor Bergmann
15 *
16 */
17public abstract class Tuple extends AbstractTuple {
18
19 /**
20 * Caches precalculated hash value
21 */
22 protected int cachedHash;
23
24 /**
25 * Creates a Tuple instance Derivatives should call calcHash()
26 */
27 protected Tuple() {
28 // calcHash();
29 }
30
31 /**
32 * Hash calculation. Overrides should keep semantics.
33 */
34 void calcHash() {
35 cachedHash = doCalcHash();
36 }
37
38 @Override
39 public boolean equals(Object obj) {
40 if (this == obj)
41 return true;
42 if (obj instanceof ITuple) {
43 final ITuple other = (ITuple) obj;
44 return cachedHash == other.hashCode() && internalEquals(other);
45 }
46 return false;
47 }
48
49 @Override
50 public int hashCode() {
51 // Calculated by #calcHash
52 return cachedHash;
53 }
54
55 public Tuple replaceAll(Object obsolete, Object replacement) {
56 Object[] oldElements = getElements();
57 Object[] newElements = new Object[oldElements.length];
58 for (int i = 0; i < oldElements.length; ++i) {
59 newElements[i] = obsolete.equals(oldElements[i]) ? replacement : oldElements[i];
60 }
61 return Tuples.flatTupleOf(newElements);
62 }
63
64 @Override
65 public Tuple toImmutable() {
66 return this;
67 }
68
69}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java
new file mode 100644
index 00000000..49c55fef
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java
@@ -0,0 +1,560 @@
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.matchers.tuple;
11
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.Collection;
15import java.util.HashSet;
16import java.util.LinkedList;
17import java.util.List;
18import java.util.OptionalInt;
19import java.util.Set;
20
21/**
22 *
23 * Specifies select indices of a tuple. If viewed through this mask (see {@link #transform(ITuple)}), the signature of the pattern will consist of its
24 * individual substitutions at the given positions, in the exact same order as they appear in indices[].
25 *
26 * @author Gabor Bergmann
27 */
28public class TupleMask {
29 /**
30 * indices[i] specifies the index of the substitution in the original tuple that occupies the i-th place in the
31 * masked signature.
32 */
33 public final int[] indices;
34 /**
35 * the size of the tuple this mask is applied to
36 */
37 public int sourceWidth;
38 /**
39 * indicesSorted is indices, sorted in ascending order.
40 * null by default, call {@link #ensureIndicesSorted()} before using
41 */
42 int[] indicesSorted;
43
44 /**
45 * true if no index occurs twice; computed on demand by {@link #isNonrepeating()}
46 */
47 Boolean isNonrepeating;
48
49 /**
50 * Creates a TupleMask instance with the given indices array
51 * <p> indicesSorted and isNonrepeating may be OPTIONALLY given if known.
52 * @since 2.0
53 */
54 protected TupleMask(int[] indices, int sourceWidth, int[] indicesSorted, Boolean isNonrepeating) {
55 this.indices = indices;
56 this.sourceWidth = sourceWidth;
57 this.indicesSorted = indicesSorted;
58 this.isNonrepeating = isNonrepeating;
59 }
60
61 /**
62 * Creates a TupleMask instance that selects given positions.
63 * The mask takes ownership of the array selectedIndices, the client must not modify it afterwards.
64 *
65 * <p> indicesSorted and isNonrepeating may be OPTIONALLY given if known.
66 * @since 2.0
67 */
68 protected static TupleMask fromSelectedIndicesInternal(
69 int[] selectedIndices, int sourceArity,
70 int[] indicesSorted, Boolean isNonrepeating)
71 {
72 if (selectedIndices.length == 0) // is it nullary?
73 return new TupleMask0(sourceArity);
74
75 // is it identity?
76 boolean identity = sourceArity == selectedIndices.length;
77 if (identity) {
78 for (int k=0; k < sourceArity; ++k) {
79 if (selectedIndices[k] != k) {
80 identity = false;
81 break;
82 }
83 }
84 }
85 if (identity)
86 return new TupleMaskIdentity(selectedIndices, sourceArity);
87
88 // generic case
89 return new TupleMask(selectedIndices, sourceArity, indicesSorted, isNonrepeating);
90 }
91
92 /**
93 * Creates a TupleMask instance that selects given positions in monotonically increasing order.
94 * The mask takes ownership of the array selectedIndices, the client must not modify it afterwards.
95 * @since 2.0
96 */
97 protected static TupleMask fromSelectedMonotonicIndicesInternal(int[] selectedIndices, int sourceArity)
98 {
99 return fromSelectedIndicesInternal(selectedIndices, sourceArity, selectedIndices /* also sorted */, true);
100 }
101
102 /**
103 * Creates a TupleMask instance of the given size that maps the first 'size' elements intact
104 */
105 public static TupleMask linear(int size, int sourceWidth) {
106 if (size == sourceWidth) return new TupleMaskIdentity(sourceWidth);
107 int[] indices = constructLinearSequence(size);
108 return fromSelectedMonotonicIndicesInternal(indices, sourceWidth);
109 }
110
111 /**
112 * An array containing the first {@link size} nonnegative integers in order
113 * @since 2.0
114 */
115 protected static int[] constructLinearSequence(int size) {
116 int[] indices = new int[size];
117 for (int i = 0; i < size; i++)
118 indices[i] = i;
119 return indices;
120 }
121
122 /**
123 * Creates a TupleMask instance of the given size that maps every single element intact
124 */
125 public static TupleMask identity(int size) {
126 return new TupleMaskIdentity(size);
127 }
128
129 /**
130 * Creates a TupleMask instance of the given size that does not emit output.
131 */
132 public static TupleMask empty(int sourceWidth) {
133 return linear(0, sourceWidth);
134 }
135
136 /**
137 * Creates a TupleMask instance that maps the tuple intact save for a single element at the specified index which is
138 * omitted
139 */
140 public static TupleMask omit(int omission, int sourceWidth) {
141 int size = sourceWidth - 1;
142 int[] indices = new int[size];
143 for (int i = 0; i < omission; i++)
144 indices[i] = i;
145 for (int i = omission; i < size; i++)
146 indices[i] = i + 1;
147 return fromSelectedMonotonicIndicesInternal(indices, sourceWidth);
148 }
149
150
151 /**
152 * Creates a TupleMask instance that selects positions where keep is true
153 * @since 1.7
154 */
155 public static TupleMask fromKeepIndicators(boolean[] keep) {
156 int size = 0;
157 for (int k = 0; k < keep.length; ++k)
158 if (keep[k])
159 size++;
160 if (size == keep.length) return new TupleMaskIdentity(size);
161 int[] indices = new int[size];
162 int l = 0;
163 for (int k = 0; k < keep.length; ++k)
164 if (keep[k])
165 indices[l++] = k;
166 return fromSelectedMonotonicIndicesInternal(indices, keep.length);
167 }
168
169 /**
170 * Creates a TupleMask instance that selects given positions.
171 * @since 1.7
172 */
173 public static TupleMask fromSelectedIndices(int sourceArity, Collection<Integer> selectedIndices) {
174 int[] selected = integersToIntArray(selectedIndices);
175 return fromSelectedIndicesInternal(selected, sourceArity, null, null);
176 }
177 /**
178 * Creates a TupleMask instance that selects given positions.
179 * @since 1.7
180 */
181 public static TupleMask fromSelectedIndices(int sourceArity, int[] selectedIndices) {
182 return fromSelectedIndicesInternal(Arrays.copyOf(selectedIndices, selectedIndices.length), sourceArity, null, null);
183 }
184 /**
185 * Creates a TupleMask instance that selects non-null positions of a given tuple
186 * @since 1.7
187 */
188 public static TupleMask fromNonNullIndices(ITuple tuple) {
189 List<Integer> indices = new ArrayList<>();
190 for (int i=0; i < tuple.getSize(); i++) {
191 if (tuple.get(i) != null) {
192 indices.add(i);
193 }
194 }
195 if (indices.size() == tuple.getSize()) return new TupleMaskIdentity(indices.size());
196 return fromSelectedMonotonicIndicesInternal(integersToIntArray(indices), tuple.getSize());
197 }
198 /**
199 * @since 1.7
200 */
201 public static int[] integersToIntArray(Collection<Integer> selectedIndices) {
202 int[] selected = new int[selectedIndices.size()];
203 int k=0;
204 for (Integer integer : selectedIndices) {
205 selected[k++] = integer;
206 }
207 return selected;
208 }
209
210
211 /**
212 * Creates a TupleMask instance that moves an element from one index to other, shifting the others if neccessary.
213 */
214 public static TupleMask displace(int from, int to, int sourceWidth) {
215 if (from == to) return new TupleMaskIdentity(sourceWidth);
216 int[] indices = new int[sourceWidth];
217 for (int i = 0; i < sourceWidth; i++)
218 if (i == to)
219 indices[i] = from;
220 else if (i >= from && i < to)
221 indices[i] = i + 1;
222 else if (i > to && i <= from)
223 indices[i] = i - 1;
224 else
225 indices[i] = i;
226 return fromSelectedIndicesInternal(indices, sourceWidth, null, true);
227 }
228
229 /**
230 * Creates a TupleMask instance that selects a single element of the tuple.
231 */
232 public static TupleMask selectSingle(int selected, int sourceWidth) {
233 int[] indices = { selected };
234 return fromSelectedMonotonicIndicesInternal(indices, sourceWidth);
235 }
236
237 /**
238 * Creates a TupleMask instance that selects whatever is selected by left, and appends whatever is selected by
239 * right. PRE: left and right have the same sourcewidth
240 */
241 public static TupleMask append(TupleMask left, TupleMask right) {
242 int leftLength = left.indices.length;
243 int rightLength = right.indices.length;
244 int[] indices = new int[leftLength + rightLength];
245 for (int i = 0; i < leftLength; ++i)
246 indices[i] = left.indices[i];
247 for (int i = 0; i < rightLength; ++i)
248 indices[i + leftLength] = right.indices[i];
249 return fromSelectedIndicesInternal(indices, left.sourceWidth, null, null);
250 }
251
252 /**
253 * Generates indicesSorted from indices on demand
254 */
255 void ensureIndicesSorted() {
256 if (indicesSorted == null) {
257 indicesSorted = new int[indices.length];
258 List<Integer> list = new LinkedList<Integer>();
259 for (int i = 0; i < indices.length; ++i)
260 list.add(indices[i]);
261 java.util.Collections.sort(list);
262 int i = 0;
263 for (Integer a : list)
264 indicesSorted[i++] = a;
265 }
266 }
267
268
269
270 /**
271 * Returns the first index of the source that is not selected by the mask, or empty if all indices are selected.
272 * <p> PRE: mask indices are all different
273 * @since 2.0
274 */
275 public OptionalInt getFirstOmittedIndex() {
276 ensureIndicesSorted();
277 int column = 0;
278 while (column < getSize() && indicesSorted[column] == column) column++;
279 if (column < getSourceWidth()) return OptionalInt.of(column);
280 else return OptionalInt.empty();
281 }
282
283
284 /**
285 * Returns a selected masked value from the selected tuple.
286 * @pre: 0 <= index < getSize()
287 * @since 1.7
288 */
289 public Object getValue(ITuple original, int index) {
290 return original.get(indices[index]);
291 }
292
293 /**
294 * Sets the selected value in the original tuple based on the mask definition
295 *
296 * @pre: 0 <= index < getSize()
297 * @since 1.7
298 */
299 public void set(IModifiableTuple tuple, int index, Object value) {
300 tuple.set(indices[index], value);
301 }
302
303 /**
304 * Generates an immutable, masked view of the original tuple.
305 * <p> The new tuple will have arity {@link #getSize()},
306 * and will consist of the elements of the original tuple, at positions indicated by this mask.
307 * @since 1.7
308 */
309 public Tuple transform(ITuple original) {
310 switch (indices.length) {
311 case 0:
312 return FlatTuple0.INSTANCE;
313 case 1:
314 return new FlatTuple1(original.get(indices[0]));
315 case 2:
316 return new FlatTuple2(original.get(indices[0]), original.get(indices[1]));
317 case 3:
318 return new FlatTuple3(original.get(indices[0]), original.get(indices[1]), original.get(indices[2]));
319 case 4:
320 return new FlatTuple4(original.get(indices[0]), original.get(indices[1]), original.get(indices[2]), original.get(indices[3]));
321 default:
322 Object signature[] = new Object[indices.length];
323 for (int i = 0; i < indices.length; ++i)
324 signature[i] = original.get(indices[i]);
325 return new FlatTuple(signature);
326 }
327 }
328
329 /**
330 * @return true iff no two selected indices are the same
331 * @since 2.0
332 */
333 public boolean isNonrepeating() {
334 if (isNonrepeating == null) {
335 ensureIndicesSorted();
336 int previous = -1;
337 int i;
338 for (i = 0; i < sourceWidth && previous != indicesSorted[i]; ++i) {
339 previous = indicesSorted[i];
340 }
341 isNonrepeating = (i == sourceWidth); // if not, stopped due to detected repetition
342 }
343 return isNonrepeating;
344 }
345
346 /**
347 * Returns a tuple `result` that satisfies `this.transform(result).equals(masked)`. Positions of the result tuple
348 * that are not determined this way will be filled with null.
349 *
350 * @pre: all indices of the mask must be different, i.e {@link #isNonrepeating()} must return true
351 * @since 1.7
352 */
353 public Tuple revertFrom(ITuple masked) {
354 Object[] signature = new Object[sourceWidth];
355 for (int i = 0; i < indices.length; ++i)
356 signature[indices[i]] = masked.get(i);
357 return Tuples.flatTupleOf(signature);
358 }
359
360 /**
361 * Returns a tuple `result`, same arity as the original tuple, that satisfies
362 * `this.transform(result).equals(this.transform(tuple))`.
363 * Positions of the result tuple that are not determined this way will be filled with null.
364 * <p> In other words, a copy of the original tuple is returned,
365 * with null substituted at each position that is <em>not</em> selected by this mask.
366 *
367 * @pre: all indices of the mask must be different, i.e {@link #isNonrepeating()} must return true
368 * @since 2.1
369 */
370 public Tuple keepSelectedIndices(ITuple original) {
371 Object[] signature = new Object[sourceWidth];
372 for (int i = 0; i < indices.length; ++i)
373 signature[indices[i]] = original.get(indices[i]);
374 return Tuples.flatTupleOf(signature);
375 }
376
377 /**
378 * Generates an immutable, masked view of the original tuple.
379 * <p> The list will have arity {@link #getSize()},
380 * and will consist of the elements of the original tuple, at positions indicated by this mask.
381 */
382 public <T> List<T> transform(List<T> original) {
383 List<T> signature = new ArrayList<T>(indices.length);
384 for (int i = 0; i < indices.length; ++i)
385 signature.add(original.get(indices[i]));
386 return signature;
387 }
388
389 /**
390 * Transforms a given mask directly, instead of transforming tuples that were transformed by the other mask.
391 *
392 * @return a mask that cascades the effects this mask after the mask provided as parameter.
393 */
394 public TupleMask transform(TupleMask mask) {
395 int[] cascadeIndices = new int[indices.length];
396 for (int i = 0; i < indices.length; ++i)
397 cascadeIndices[i] = mask.indices[indices[i]];
398 return fromSelectedIndicesInternal(cascadeIndices, mask.sourceWidth, null, null);
399 }
400
401 // /**
402 // * Generates a complementer mask that maps those elements that were
403 // untouched by the original mask.
404 // * Ordering is left intact.
405 // * A Tuple is used for reference concerning possible equalities among
406 // elements.
407 // */
408 // public TupleMask complementer(Tuple reference)
409 // {
410 // HashSet<Object> touched = new HashSet<Object>();
411 // LinkedList<Integer> untouched = new LinkedList<Integer>();
412 //
413 // for (int index : indices) touched.add(reference.get(index));
414 // for (int index=0; index<reference.getSize(); ++index)
415 // {
416 // if (touched.add(reference.get(index))) untouched.addLast(index);
417 // }
418 //
419 // int[] complementer = new int[untouched.size()];
420 // int k = 0;
421 // for (Integer integer : untouched) complementer[k++] = integer;
422 // return new TupleMask(complementer, reference.getSize());
423 // }
424
425 /**
426 * Combines two substitutions. The new pattern will contain all substitutions of masked and unmasked, assuming that
427 * the elements of masked indicated by this mask are already matched against unmasked.
428 *
429 * POST: the result will start with an exact copy of unmasked
430 *
431 * @param unmasked
432 * primary pattern substitution that is left intact.
433 * @param masked
434 * secondary pattern substitution that is transformed to the end of the result.
435 * @param useInheritance
436 * whether to use inheritance or copy umasked into result instead.
437 * @param asComplementer
438 * whether this mask maps from the masked Tuple to the tail of the result or to the unmasked one.
439 * @return new pattern that is a combination of unmasked and masked.
440 */
441 public Tuple combine(Tuple unmasked, Tuple masked, boolean useInheritance, boolean asComplementer) {
442
443 int combinedLength = asComplementer ? indices.length : masked.getSize() - indices.length;
444 if (!useInheritance)
445 combinedLength += unmasked.getSize();
446 Object combined[] = new Object[combinedLength];
447
448 int cPos = 0;
449 if (!useInheritance) {
450 for (int i = 0; i < unmasked.getSize(); ++i)
451 combined[cPos++] = unmasked.get(i);
452 }
453
454 if (asComplementer) {
455 for (int i = 0; i < indices.length; ++i)
456 combined[cPos++] = masked.get(indices[i]);
457 } else {
458 ensureIndicesSorted();
459 int mPos = 0;
460 for (int i = 0; i < masked.getSize(); ++i)
461 if (mPos < indicesSorted.length && i == indicesSorted[mPos])
462 mPos++;
463 else
464 combined[cPos++] = masked.get(i);
465 }
466
467 return useInheritance ? Tuples.leftInheritanceTupleOf(unmasked, combined) : Tuples.flatTupleOf(combined);
468 }
469
470 @Override
471 public int hashCode() {
472 final int PRIME = 31;
473 int result = sourceWidth;
474 for (int i : indices)
475 result = PRIME * result + i;
476 return result;
477 }
478
479 @Override
480 public boolean equals(Object obj) {
481 if (this == obj)
482 return true;
483 if (obj == null)
484 return false;
485 if (getClass() != obj.getClass())
486 return false;
487 final TupleMask other = (TupleMask) obj;
488 if (sourceWidth != other.sourceWidth)
489 return false;
490 if (indices.length != other.indices.length)
491 return false;
492 for (int k = 0; k < indices.length; k++)
493 if (indices[k] != other.indices[k])
494 return false;
495 return true;
496 }
497
498 @Override
499 public String toString() {
500 StringBuilder s = new StringBuilder();
501 s.append("M(" + sourceWidth + "->");
502 for (int i : indices) {
503 s.append(i);
504 s.append(',');
505 }
506 s.append(')');
507 return s.toString();
508 }
509
510 /**
511 * Returns the size of the masked tuples described by this mask
512 * @since 1.7
513 */
514 public int getSize() {
515 return indices.length;
516 }
517
518 /**
519 * Returns the size of the original tuples handled by this mask
520 * @since 1.7
521 */
522 public int getSourceWidth() {
523 return sourceWidth;
524 }
525
526
527 /**
528 * @return true iff this mask is a no-op
529 * @since 2.0
530 */
531 public boolean isIdentity() {
532 // Contract: if identity mask, a specialized subclass is constructed instead
533 return false;
534 }
535
536 /**
537 * Transforms the given list by applying the mask and putting all results into a set; keeping only a single element
538 * in case of the mapping result in duplicate values.
539 *
540 * @since 1.7
541 */
542 public <T> Set<T> transformUnique(List<T> original) {
543 Set<T> signature = new HashSet<>();
544 for (int i = 0; i < indices.length; ++i)
545 signature.add(original.get(indices[i]));
546 return signature;
547 }
548
549 /**
550 * @return the list of selected indices
551 * @since 2.1
552 */
553 public List<Integer> getIndicesAsList() {
554 List<Integer> result = new ArrayList<Integer>(indices.length);
555 for (int i = 0; i < indices.length; ++i)
556 result.add(indices[i]);
557 return result;
558 }
559
560}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java
new file mode 100644
index 00000000..5a0c79ff
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java
@@ -0,0 +1,56 @@
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.matchers.tuple;
10
11import java.util.Collections;
12import java.util.List;
13
14/**
15 * @author Gabor Bergmann
16 * @since 1.7
17 */
18public final class TupleMask0 extends TupleMask {
19
20 private final static int[] EMPTY_ARRAY = {};
21
22 /**
23 * PRE: indices.length == 0
24 */
25 TupleMask0(int sourceWidth) {
26 super(EMPTY_ARRAY, sourceWidth, EMPTY_ARRAY, true);
27 }
28
29 @Override
30 public <T> List<T> transform(List<T> original) {
31 return Collections.emptyList();
32 }
33
34 @Override
35 public Tuple transform(ITuple original) {
36 return Tuples.staticArityFlatTupleOf();
37 }
38
39 @Override
40 public TupleMask transform(TupleMask mask) {
41 return new TupleMask0(mask.sourceWidth);
42 }
43
44 @Override
45 public Tuple combine(Tuple unmasked, Tuple masked, boolean useInheritance, boolean asComplementer) {
46 if (asComplementer)
47 return unmasked;
48 else
49 return super.combine(unmasked, masked, useInheritance, asComplementer);
50 }
51
52 @Override
53 public boolean isIdentity() {
54 return 0 == sourceWidth;
55 }
56}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java
new file mode 100644
index 00000000..62746587
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java
@@ -0,0 +1,51 @@
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.matchers.tuple;
10
11import java.util.List;
12
13/**
14 * @author Gabor Bergmann
15 * @since 1.7
16 */
17public final class TupleMaskIdentity extends TupleMask {
18
19 TupleMaskIdentity(int sourceWidth) {
20 this(constructLinearSequence(sourceWidth), sourceWidth);
21 }
22 TupleMaskIdentity(int[] indices, int sourceWidth) {
23 super(indices, sourceWidth, indices, true);
24 }
25
26 @Override
27 public <T> List<T> transform(List<T> original) {
28 return original;
29 }
30
31 @Override
32 public Tuple transform(ITuple original) {
33 return original.toImmutable();
34 }
35
36 @Override
37 public TupleMask transform(TupleMask mask) {
38 return mask;
39 }
40
41 @Override
42 public Tuple revertFrom(ITuple masked) {
43 return masked.toImmutable();
44 }
45
46 @Override
47 public boolean isIdentity() {
48 return true;
49 }
50
51}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java
new file mode 100644
index 00000000..79193516
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java
@@ -0,0 +1,48 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.matchers.tuple;
10
11import java.util.Map;
12
13import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider;
14
15/**
16 * @author Zoltan Ujhelyi
17 * @since 1.7
18 */
19public class TupleValueProvider implements IValueProvider {
20
21 final ITuple tuple;
22 final Map<String, Integer> indexMapping;
23
24 /**
25 * Wraps a tuple with an index mapping
26 * @param tuple
27 * @param indexMapping
28 */
29 public TupleValueProvider(ITuple tuple, Map<String, Integer> indexMapping) {
30 super();
31 this.tuple = tuple;
32 this.indexMapping = indexMapping;
33 }
34
35 @Override
36 public Object getValue(String variableName) {
37 Integer index = indexMapping.get(variableName);
38 if (index == null) {
39 throw new IllegalArgumentException(String.format("Variable %s is not present in mapping.", variableName));
40 }
41 Object value = tuple.get(index);
42 if (value == null) {
43 throw new IllegalArgumentException(String.format("Variable %s is not found using index %d.", variableName, index));
44 }
45 return value;
46 }
47
48}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java
new file mode 100644
index 00000000..5e41d7d8
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java
@@ -0,0 +1,157 @@
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.matchers.tuple;
10
11/**
12 * Common static factory utilities for tuples.
13 *
14 * @author Gabor Bergmann
15 * @since 1.7
16 */
17public class Tuples {
18
19 private Tuples() {
20 // Empty utility class constructor
21 }
22
23 /**
24 * Creates a flat tuple consisting of the given elements.
25 * For low-arity tuples, specialized implementations
26 * (such as {@link FlatTuple2}) will be instantiated.
27 *
28 * <p> In case the exact arity is <i>statically</i> known,
29 * it may be more efficient for the client to instantiate
30 * the appropriate specialized implementation
31 * (via {@link #staticArityFlatTupleOf(Object, Object)} etc.
32 * or {@link #wideFlatTupleOf(Object...)}),
33 * instead of invoking this method.
34 * This method does a runtime arity check, and therefore
35 * also appropriate if the arity is determined at runtime.
36 */
37 public static Tuple flatTupleOf(Object... elements) {
38 switch (elements.length) {
39 case 0:
40 return FlatTuple0.INSTANCE;
41 case 1:
42 return new FlatTuple1(elements[0]);
43 case 2:
44 return new FlatTuple2(elements[0], elements[1]);
45 case 3:
46 return new FlatTuple3(elements[0], elements[1], elements[2]);
47 case 4:
48 return new FlatTuple4(elements[0], elements[1], elements[2], elements[3]);
49 default:
50 return new FlatTuple(elements);
51 }
52 }
53 /**
54 * Creates a left inheritance tuple that extends an ancestor tuple
55 * by the given "local" elements.
56 * For locally low-arity tuples, specialized implementations
57 * (such as {@link LeftInheritanceTuple2}) will be instantiated.
58 *
59 * <p> In case the exact arity is <i>statically</i> known,
60 * it may be more efficient for the client to instantiate
61 * the appropriate specialized implementation
62 * (via {@link #staticArityLeftInheritanceTupleOf(Object, Object)} etc.
63 * or {@link #wideLeftInheritanceTupleOf(Object...)}),
64 * instead of invoking this method.
65 * This method does a runtime arity check, and therefore
66 * also appropriate if the arity is determined at runtime.
67 */
68 public static Tuple leftInheritanceTupleOf(Tuple ancestor, Object... localElements) {
69 switch (localElements.length) {
70 case 0:
71 return ancestor;
72 case 1:
73 return new LeftInheritanceTuple1(ancestor, localElements[0]);
74 case 2:
75 return new LeftInheritanceTuple2(ancestor, localElements[0], localElements[1]);
76 case 3:
77 return new LeftInheritanceTuple3(ancestor, localElements[0], localElements[1], localElements[2]);
78 case 4:
79 return new LeftInheritanceTuple4(ancestor, localElements[0], localElements[1], localElements[2], localElements[3]);
80 default:
81 return new LeftInheritanceTuple(ancestor, localElements);
82 }
83 }
84
85 /**
86 * Creates a flat tuple consisting of no elements.
87 */
88 public static Tuple staticArityFlatTupleOf() {
89 return FlatTuple0.INSTANCE;
90 }
91 /**
92 * Creates a flat tuple consisting of the given single element.
93 */
94 public static Tuple staticArityFlatTupleOf(Object element) {
95 return new FlatTuple1(element);
96 }
97 /**
98 * Creates a flat tuple consisting of the given elements.
99 */
100 public static Tuple staticArityFlatTupleOf(Object element0, Object element1) {
101 return new FlatTuple2(element0, element1);
102 }
103 /**
104 * Creates a flat tuple consisting of the given elements.
105 */
106 public static Tuple staticArityFlatTupleOf(Object element0, Object element1, Object element2) {
107 return new FlatTuple3(element0, element1, element2);
108 }
109 /**
110 * Creates a flat tuple consisting of the given elements.
111 */
112 public static Tuple staticArityFlatTupleOf(Object element0, Object element1, Object element2, Object element3) {
113 return new FlatTuple4(element0, element1, element2, element3);
114 }
115 /**
116 * Creates a flat tuple consisting of the given elements.
117 * <p> Invoke this only if it is statically known that the tuple will be wide.
118 * Otherwise, use {@link #flatTupleOf(Object...)}.
119 */
120 public static Tuple wideFlatTupleOf(Object... elements) {
121 return new FlatTuple(elements);
122 }
123
124 /**
125 * Creates a left inheritance tuple consisting of the given single local element.
126 */
127 public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element) {
128 return new LeftInheritanceTuple1(ancestor, element);
129 }
130 /**
131 * Creates a left inheritance tuple consisting of the given local elements.
132 */
133 public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1) {
134 return new LeftInheritanceTuple2(ancestor, element0, element1);
135 }
136 /**
137 * Creates a left inheritance tuple consisting of the given local elements.
138 */
139 public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1, Object element2) {
140 return new LeftInheritanceTuple3(ancestor, element0, element1, element2);
141 }
142 /**
143 * Creates a left inheritance tuple consisting of the given local elements.
144 */
145 public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1, Object element2, Object element3) {
146 return new LeftInheritanceTuple4(ancestor, element0, element1, element2, element3);
147 }
148 /**
149 * Creates a left inheritance tuple consisting of the given local elements.
150 * <p> Invoke this only if it is statically known that the tuple will be wide.
151 * Otherwise, use {@link #leftInheritanceTupleOf(Tuple, Object...)}.
152 */
153 public static Tuple wideLeftInheritanceTupleOf(Tuple ancestor, Object... elements) {
154 return new LeftInheritanceTuple(ancestor, elements);
155 }
156
157}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java
new file mode 100644
index 00000000..f683d544
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java
@@ -0,0 +1,50 @@
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.matchers.tuple;
10
11import tools.refinery.viatra.runtime.matchers.util.Preconditions;
12
13/**
14 * This class provides a volatile tuple view with a given mask of a given tuple instance. If the masked tuple changes,
15 * the view updates as well.
16 *
17 * @author Zoltan Ujhelyi
18 * @since 1.7
19 *
20 */
21public class VolatileMaskedTuple extends VolatileTuple {
22
23 protected final TupleMask mask;
24 protected ITuple source;
25
26 public VolatileMaskedTuple(ITuple source, TupleMask mask) {
27 this.source = source;
28 this.mask = mask;
29 }
30
31 public VolatileMaskedTuple(TupleMask mask) {
32 this(null, mask);
33 }
34
35 public void updateTuple(ITuple newSource) {
36 source = newSource;
37 }
38
39 @Override
40 public Object get(int index) {
41 Preconditions.checkState(source != null, "Source tuple is not set.");
42 return mask.getValue(source, index);
43 }
44
45 @Override
46 public int getSize() {
47 return mask.getSize();
48 }
49
50}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java
new file mode 100644
index 00000000..92306c6e
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java
@@ -0,0 +1,47 @@
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.matchers.tuple;
10
11import tools.refinery.viatra.runtime.matchers.util.Preconditions;
12
13/**
14 * A masked tuple implementation that allows modifying the backing tuple.
15 * @author Zoltan Ujhelyi
16 * @since 1.7
17 *
18 */
19public class VolatileModifiableMaskedTuple extends VolatileMaskedTuple implements IModifiableTuple {
20
21 private IModifiableTuple modifiableTuple;
22
23 public VolatileModifiableMaskedTuple(IModifiableTuple source, TupleMask mask) {
24 super(source, mask);
25 modifiableTuple = source;
26 }
27
28 public VolatileModifiableMaskedTuple(TupleMask mask) {
29 this(null, mask);
30 }
31
32 @Override
33 public void updateTuple(ITuple newSource) {
34 Preconditions.checkArgument(newSource instanceof IModifiableTuple, "Provided tuple does not support updates");
35 this.updateTuple((IModifiableTuple)newSource);
36 }
37
38 public void updateTuple(IModifiableTuple newSource) {
39 super.updateTuple(newSource);
40 modifiableTuple = newSource;
41 }
42
43 @Override
44 public void set(int index, Object value) {
45 mask.set(modifiableTuple, index, value);
46 }
47}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java
new file mode 100644
index 00000000..699105a5
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java
@@ -0,0 +1,47 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017 Zoltan Ujhelyi, IncQuery Labs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.matchers.tuple;
11
12/**
13 * Mutable tuple without explicit modification commands. In practical terms, the values stored in a volatile tuple can
14 * be changed without any notification.
15 *
16 * @author Zoltan Ujhelyi
17 * @since 1.7
18 *
19 */
20public abstract class VolatileTuple extends AbstractTuple {
21
22 @Override
23 public boolean equals(Object obj) {
24 if (this == obj)
25 return true;
26 if (obj == null)
27 return false;
28 if (!(obj instanceof ITuple))
29 return false;
30 final ITuple other = (ITuple) obj;
31 return internalEquals(other);
32 }
33
34 @Override
35 public int hashCode() {
36 return doCalcHash();
37 }
38
39 /**
40 * Creates an immutable tuple from the values stored in the tuple. The created tuple will not be updated when the
41 * current tuple changes.
42 */
43 @Override
44 public Tuple toImmutable() {
45 return Tuples.flatTupleOf(getElements());
46 }
47}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java
new file mode 100644
index 00000000..338990ab
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java
@@ -0,0 +1,48 @@
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.matchers.util;
10
11/**
12 * The degree of accuracy of a cardinality estimate
13 * @author Gabor Bergmann
14 * @since 2.1
15 */
16public enum Accuracy {
17 EXACT_COUNT,
18 BEST_UPPER_BOUND,
19 BEST_LOWER_BOUND,
20 APPROXIMATION;
21
22 /**
23 * Partial order comparison.
24 */
25 public boolean atLeastAsPreciseAs(Accuracy other) {
26 switch (this) {
27 case EXACT_COUNT: return true;
28 case APPROXIMATION: return APPROXIMATION == other;
29 case BEST_UPPER_BOUND: return BEST_UPPER_BOUND == other || APPROXIMATION == other;
30 case BEST_LOWER_BOUND: return BEST_LOWER_BOUND == other || APPROXIMATION == other;
31 default: throw new IllegalArgumentException();
32 }
33 }
34
35 /**
36 * @return another accuracy value that is anti-monotonic to this one,
37 * i.e. an accuracy that should be used in the denominator to obtain a fraction with this accuracy
38 */
39 public Accuracy reciprocal() {
40 switch(this) {
41 case APPROXIMATION: return APPROXIMATION;
42 case BEST_UPPER_BOUND: return BEST_LOWER_BOUND;
43 case BEST_LOWER_BOUND: return BEST_UPPER_BOUND;
44 case EXACT_COUNT: return EXACT_COUNT;
45 default: throw new IllegalArgumentException();
46 }
47 }
48}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java
new file mode 100644
index 00000000..1b09aec6
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java
@@ -0,0 +1,23 @@
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.matchers.util;
11
12/**
13 * @author Gabor Bergmann
14 * @since 1.7
15 * An instance of clearable pattern memory.
16 */
17public interface Clearable {
18 /**
19 * Clear all partial matchings stored in memory
20 *
21 */
22 void clear();
23}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java
new file mode 100644
index 00000000..590a1ec3
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java
@@ -0,0 +1,188 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, 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.matchers.util;
10
11import java.util.Collection;
12import java.util.List;
13import java.util.Map;
14import java.util.Set;
15import java.util.TreeMap;
16import java.util.function.Function;
17
18/**
19 * Factory class used as an accessor to Collections implementations.
20 * @author istvanrath
21 */
22public final class CollectionsFactory
23{
24
25 /**
26 * Instantiates a new empty map.
27 * @since 1.7
28 */
29 public static <K, V> Map<K, V> createMap() {
30 return FRAMEWORK.createMap();
31 }
32
33 /**
34 * Instantiates a new map with the given initial contents.
35 * @since 1.7
36 */
37 public static <K, V> Map<K, V> createMap(Map<K, V> initial) {
38 return FRAMEWORK.createMap(initial);
39 }
40
41 /**
42 * Instantiates a new tree map.
43 * @since 2.3
44 */
45 public static <K, V> TreeMap<K, V> createTreeMap() {
46 return FRAMEWORK.createTreeMap();
47 }
48
49 /**
50 * Instantiates a new empty set.
51 * @since 1.7
52 */
53 public static <E> Set<E> createSet() {
54 return FRAMEWORK.createSet();
55 }
56
57 /**
58 * Instantiates a new set with the given initial contents.
59 * @since 1.7
60 */
61 public static <E> Set<E> createSet(Collection<E> initial) {
62 return FRAMEWORK.createSet(initial);
63 }
64
65 /**
66 * Instantiates an empty set; the key parameter is used to allow using this as a method reference as a
67 * {@link Function}, e.g. in {@link Map#computeIfAbsent(Object, Function)}.
68 *
69 * @param key
70 * the value of this parameter is ignored
71 * @since 2.0
72 */
73 public static <T> Set<T> emptySet(Object key) {
74 return FRAMEWORK.createSet();
75 }
76
77 /**
78 * Instantiates a new empty multiset.
79 * @since 1.7
80 */
81 public static <T> IMultiset<T> createMultiset() {
82 return FRAMEWORK.createMultiset();
83 }
84
85 /**
86 * Instantiates an empty multiset; the key parameter is used to allow using this as a method reference as a
87 * {@link Function}, e.g. in {@link Map#computeIfAbsent(Object, Function)}.
88 *
89 * @param key
90 * the value of this parameter is ignored
91 * @since 2.0
92 */
93 public static <T> IMultiset<T> emptyMultiset(Object key) {
94 return FRAMEWORK.createMultiset();
95 }
96
97 /**
98 * Instantiates a new empty delta bag.
99 * @since 1.7
100 */
101 public static <T> IDeltaBag<T> createDeltaBag() {
102 return FRAMEWORK.createDeltaBag();
103 }
104
105 /**
106 * Instantiates a new list that is optimized for registering observers / callbacks.
107 * @since 1.7
108 */
109 public static <O> List<O> createObserverList() {
110 return FRAMEWORK.createObserverList();
111 }
112
113 /**
114 * Instantiates a size-optimized multimap from keys to sets of values.
115 * <p>For a single key, many values can be associated according to the given bucket semantics.
116 * <p>The keys and values are stored as type fromKeys resp. ofValues;
117 * currently Object.class and Long.class are supported.
118 * @since 2.0
119 */
120 public static <K, V> IMultiLookup<K, V> createMultiLookup(
121 Class<? super K> fromKeys, MemoryType toBuckets, Class<? super V> ofValues) {
122 return FRAMEWORK.createMultiLookup(fromKeys, toBuckets, ofValues);
123 }
124
125 /**
126 * Instantiates a memory storing values.
127 * <p>For a single key, many values can be associated according to the given memory semantics.
128 * <p>The values are stored as type 'values';
129 * currently Object.class and Long.class are supported.
130 * @since 2.0
131 */
132 public static <T> IMemory<T> createMemory(
133 Class<? super T> values, MemoryType memoryType) {
134 return FRAMEWORK.createMemory(values, memoryType);
135 }
136
137 /**
138 * The type of {@link IMemory}
139 * @since 2.0
140 * TODO add delta as type
141 */
142 public enum MemoryType {
143 /**
144 * A single key-value pair is stored at most once
145 */
146 SETS,
147 /**
148 * Duplicate key-value pairs allowed
149 */
150 MULTISETS
151 }
152
153 /**
154 * The collections framework of the current configuration.
155 * @since 1.7
156 */
157 private static final ICollectionsFramework FRAMEWORK = new EclipseCollectionsFactory();
158
159 /**
160 * Interface abstracting over a collections technology that provides custom collection implementations.
161 * @since 1.7
162 */
163 public static interface ICollectionsFramework {
164
165 public abstract <K,V> Map<K,V> createMap();
166 public abstract <K,V> Map<K,V> createMap(Map<K,V> initial);
167 /**
168 * @since 2.3
169 */
170 public abstract <K, V> TreeMap<K, V> createTreeMap();
171 public abstract <E> Set<E> createSet();
172 public abstract <E> Set<E> createSet(Collection<E> initial);
173 public abstract <T> IMultiset<T> createMultiset();
174 public abstract <T> IDeltaBag<T> createDeltaBag();
175 public abstract <O> List<O> createObserverList();
176
177 /**
178 * @since 2.0
179 */
180 public abstract <K, V> IMultiLookup<K, V> createMultiLookup(
181 Class<? super K> fromKeys, MemoryType toBuckets, Class<? super V> ofValues);
182 /**
183 * @since 2.0
184 */
185 public abstract <T> IMemory<T> createMemory(Class<? super T> values, MemoryType memoryType);
186 }
187
188} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java
new file mode 100644
index 00000000..88f7ec00
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java
@@ -0,0 +1,61 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.matchers.util;
10
11/**
12 * Indicates whether a propagated update event signals the insertion or deletion of an element
13 *
14 * @author Gabor Bergmann
15 */
16public enum Direction {
17 INSERT, DELETE;
18
19 /**
20 * @since 2.4
21 */
22 public Direction opposite() {
23 switch (this) {
24 case INSERT:
25 return DELETE;
26 default:
27 return INSERT;
28 }
29 }
30
31 /**
32 * @since 2.4
33 */
34 public char asSign() {
35 switch (this) {
36 case INSERT:
37 return '+';
38 default:
39 return '-';
40 }
41 }
42
43 /**
44 * Returns the direction that is the product of this direction and the other direction.
45 *
46 * DELETE x DELETE = INSERT
47 * DELETE x INSERT = DELETE
48 * INSERT x DELETE = DELETE
49 * INSERT x INSERT = INSERT
50 * @since 2.4
51 */
52 public Direction multiply(final Direction other) {
53 switch (this) {
54 case DELETE:
55 return other.opposite();
56 default:
57 return other;
58 }
59 }
60
61}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java
new file mode 100644
index 00000000..e24b2448
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java
@@ -0,0 +1,86 @@
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.matchers.util;
11
12import java.util.Iterator;
13import java.util.Set;
14import java.util.function.BiConsumer;
15
16import org.eclipse.collections.impl.map.mutable.primitive.ObjectIntHashMap;
17
18/**
19 * Eclipse Collections-based multiset for tuples. Can contain duplicate occurrences of the same matching.
20 *
21 * <p>Inherits Eclipse Collections' Object-to-Int primitive hashmap and counts the number of occurrences of each value.
22 * Element is deleted if # of occurences drops to 0.
23 *
24 * @author Gabor Bergmann.
25 * @since 1.7
26 * @noreference
27 */
28public abstract class EclipseCollectionsBagMemory<T> extends ObjectIntHashMap<T> implements IMemory<T> {
29
30 public EclipseCollectionsBagMemory() {
31 super();
32 }
33
34 @Override
35 public int getCount(T value) {
36 return super.getIfAbsent(value, 0);
37 }
38 @Override
39 public int getCountUnsafe(Object value) {
40 return super.getIfAbsent(value, 0);
41 }
42 @Override
43 public boolean containsNonZero(T value) {
44 return super.containsKey(value);
45 }
46 @Override
47 public boolean containsNonZeroUnsafe(Object value) {
48 return super.containsKey(value);
49 }
50
51 @Override
52 public void clearAllOf(T value) {
53 super.remove(value);
54 }
55
56
57 @Override
58 public Iterator<T> iterator() {
59 return super.keySet().iterator();
60 }
61
62 @Override
63 public String toString() {
64 return "TM" + super.toString();
65 }
66
67 @Override
68 public Set<T> distinctValues() {
69 return super.keySet();
70 }
71
72 @Override
73 public void forEachEntryWithMultiplicities(BiConsumer<T, Integer> entryConsumer) {
74 super.forEachKeyValue(entryConsumer::accept);
75 }
76
77 @Override
78 public int hashCode() {
79 return IMemoryView.hashCode(this);
80 }
81 @Override
82 public boolean equals(Object obj) {
83 return IMemoryView.equals(this, obj);
84 }
85
86}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java
new file mode 100644
index 00000000..94ec33cd
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java
@@ -0,0 +1,41 @@
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.matchers.util;
10
11/**
12 * @author Gabor Bergmann
13 * @since 1.7
14 */
15public class EclipseCollectionsDeltaBag<T> extends EclipseCollectionsBagMemory<T> implements IDeltaBag<T> {
16
17 @Override
18 public boolean addOne(T value) {
19 return addSigned(value, +1);
20 }
21
22 @Override
23 public boolean addSigned(T value, int count) {
24 int oldCount = super.getIfAbsent(value, 0);
25 int newCount = oldCount + count;
26
27 boolean becomesZero = newCount == 0;
28 if (becomesZero)
29 super.removeKey(value);
30 else
31 super.put(value, newCount);
32
33 return becomesZero || oldCount == 0;
34 }
35
36
37 @Override
38 public boolean removeOne(T value) {
39 return addSigned(value, -1);
40 }
41}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java
new file mode 100644
index 00000000..5a623c9b
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java
@@ -0,0 +1,159 @@
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.matchers.util;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.List;
14import java.util.Map;
15import java.util.Set;
16import java.util.TreeMap;
17
18import org.eclipse.collections.api.map.MutableMap;
19import org.eclipse.collections.impl.factory.Maps;
20import org.eclipse.collections.impl.factory.Sets;
21import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.ICollectionsFramework;
22import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
23
24/**
25 * @author Gabor Bergmann
26 * @since 1.7
27 * @noreference This class is not intended to be referenced by clients.
28 */
29public class EclipseCollectionsFactory implements ICollectionsFramework {
30
31 @Override
32 public <K, V> Map<K, V> createMap() {
33 return Maps.mutable.empty();
34 }
35
36 @Override
37 public <K, V> Map<K, V> createMap(Map<K, V> initial) {
38 MutableMap<K, V> result = Maps.mutable.ofInitialCapacity(initial.size());
39 result.putAll(initial);
40 return result;
41 }
42
43 @Override
44 public <K, V> TreeMap<K, V> createTreeMap() {
45 // eclipse collections is doing the same
46 return new TreeMap<>();
47 }
48
49 @Override
50 public <E> Set<E> createSet() {
51 return Sets.mutable.empty();
52 }
53
54 @Override
55 public <E> Set<E> createSet(Collection<E> initial) {
56 return Sets.mutable.ofAll(initial);
57 }
58
59 @Override
60 public <T> IMultiset<T> createMultiset() {
61 return new EclipseCollectionsMultiset<T>();
62 }
63
64 @Override
65 public <T> IDeltaBag<T> createDeltaBag() {
66 return new EclipseCollectionsDeltaBag<T>();
67 }
68
69 @Override
70 public <O> List<O> createObserverList() {
71 return new ArrayList<O>(1); // keep concurrent modification exceptions for error detection
72 // Lists.mutable.empty
73
74 }
75
76 @Override
77 @SuppressWarnings({ "unchecked", "rawtypes" })
78 public <K, V> IMultiLookup<K, V> createMultiLookup(
79 Class<? super K> fromKeys,
80 MemoryType toBuckets,
81 Class<? super V> ofValues)
82 {
83 boolean longKeys = Long.class.equals(fromKeys);
84 boolean objectKeys = Object.class.equals(fromKeys);
85 if (! (longKeys || objectKeys)) throw new IllegalArgumentException(fromKeys.getName());
86 boolean longValues = Long.class.equals(ofValues);
87 boolean objectValues = Object.class.equals(ofValues);
88 if (! (longValues || objectValues)) throw new IllegalArgumentException(ofValues.getName());
89
90 if (longKeys) { // K == java.lang.Long
91 if (longValues) { // V == java.lang.Long
92 switch(toBuckets) {
93 case MULTISETS:
94 return (IMultiLookup<K, V>) new EclipseCollectionsMultiLookup.FromLongs.ToMultisets.OfLongs();
95 case SETS:
96 return (IMultiLookup<K, V>) new EclipseCollectionsMultiLookup.FromLongs.ToSets.OfLongs();
97 default:
98 throw new IllegalArgumentException(toBuckets.toString());
99 }
100 } else { // objectValues
101 switch(toBuckets) {
102 case MULTISETS:
103 return new EclipseCollectionsMultiLookup.FromLongs.ToMultisets.OfObjects();
104 case SETS:
105 return new EclipseCollectionsMultiLookup.FromLongs.ToSets.OfObjects();
106 default:
107 throw new IllegalArgumentException(toBuckets.toString());
108 }
109 }
110 } else { // objectKeys
111 if (longValues) { // V == java.lang.Long
112 switch(toBuckets) {
113 case MULTISETS:
114 return new EclipseCollectionsMultiLookup.FromObjects.ToMultisets.OfLongs();
115 case SETS:
116 return new EclipseCollectionsMultiLookup.FromObjects.ToSets.OfLongs();
117 default:
118 throw new IllegalArgumentException(toBuckets.toString());
119 }
120 } else { // objectValues
121 switch(toBuckets) {
122 case MULTISETS:
123 return new EclipseCollectionsMultiLookup.FromObjects.ToMultisets.OfObjects();
124 case SETS:
125 return new EclipseCollectionsMultiLookup.FromObjects.ToSets.OfObjects();
126 default:
127 throw new IllegalArgumentException(toBuckets.toString());
128 }
129 }
130 }
131 }
132
133 @Override
134 @SuppressWarnings("unchecked")
135 public <T> IMemory<T> createMemory(Class<? super T> values, MemoryType memoryType) {
136 if (Long.class.equals(values)) { // T == java.lang.Long
137 switch(memoryType) {
138 case MULTISETS:
139 return (IMemory<T>) new EclipseCollectionsLongMultiset();
140 case SETS:
141 return (IMemory<T>) new EclipseCollectionsLongSetMemory();
142 default:
143 throw new IllegalArgumentException(memoryType.toString());
144 }
145 } else { // objectValues
146 switch(memoryType) {
147 case MULTISETS:
148 return new EclipseCollectionsMultiset<>();
149 case SETS:
150 return new EclipseCollectionsSetMemory<>();
151 default:
152 throw new IllegalArgumentException(memoryType.toString());
153 }
154 }
155 }
156
157
158
159}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java
new file mode 100644
index 00000000..88773c5d
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java
@@ -0,0 +1,150 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.util;
10
11import java.util.Iterator;
12import java.util.Set;
13import java.util.function.BiConsumer;
14
15import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap;
16
17/**
18 * @author Gabor Bergmann
19 * @since 2.0
20 * <p> TODO refactor common methods with {@link EclipseCollectionsMultiset}
21 * <p> TODO refactor into LongBagMemory etc.
22 */
23public class EclipseCollectionsLongMultiset extends LongIntHashMap implements IMultiset<Long> {
24
25 @Override
26 public boolean addOne(Long value) {
27 int oldCount = super.getIfAbsent(value, 0);
28
29 super.put(value, oldCount + 1);
30
31 return oldCount == 0;
32 }
33
34 @Override
35 public boolean addSigned(Long value, int count) {
36 int oldCount = super.getIfAbsent(value, 0);
37 int newCount = oldCount + count;
38
39 boolean becomesZero = newCount == 0;
40 if (newCount < 0)
41 throw new IllegalStateException(String.format(
42 "Cannot remove %d occurrences of value '%s' as only %d would remain in %s",
43 count, value, newCount, this));
44 else if (becomesZero)
45 super.removeKey(value);
46 else // (newCount > 0)
47 super.put(value, newCount);
48
49 return becomesZero || oldCount == 0;
50 }
51
52 @Override
53 public boolean removeOne(Long value) {
54 return removeOneInternal(value, true);
55 }
56 /**
57 * @since 2.3
58 */
59 @Override
60 public boolean removeOneOrNop(Long value) {
61 return removeOneInternal(value, false);
62 }
63
64
65 /**
66 * @since 2.3
67 */
68 protected boolean removeOneInternal(Long value, boolean throwIfImpossible) {
69 int oldCount = super.getIfAbsent(value, 0);
70 if (oldCount == 0) {
71 if (throwIfImpossible) throw new IllegalStateException(String.format(
72 "Cannot remove value '%s' that is not contained in %s",
73 value, this));
74 else return false;
75 }
76
77 int rest = oldCount - 1;
78 boolean empty = rest == 0;
79
80 if (!empty) {
81 super.put(value, rest);
82 } else {
83 super.remove(value);
84 }
85
86 return empty;
87 }
88
89 @Override
90 public void clearAllOf(Long value) {
91 super.remove(value);
92 }
93
94 @Override
95 public int getCount(Long value) {
96 return super.getIfAbsent(value, 0);
97 }
98 @Override
99 public int getCountUnsafe(Object value) {
100 return value instanceof Long ? getCount((Long) value) : 0;
101 }
102
103 @Override
104 public boolean containsNonZero(Long value) {
105 return super.containsKey(value);
106 }
107
108 @Override
109 public boolean containsNonZeroUnsafe(Object value) {
110 return value instanceof Long && containsNonZero((Long) value);
111 }
112
113 @Override
114 public Iterator<Long> iterator() {
115 return EclipseCollectionsLongSetMemory.iteratorOf(super.keySet());
116 }
117
118 @Override
119 public boolean addPositive(Long value, int count) {
120 if (count < 0) {
121 throw new IllegalArgumentException("The count value must be positive!");
122 }
123
124 int oldCount = super.getIfAbsent(value, 0);
125
126 super.put(value, oldCount + count);
127
128 return oldCount == 0;
129 }
130
131 @Override
132 public Set<Long> distinctValues() {
133 return new EclipseCollectionsLongSetMemory.SetWrapper(super.keySet());
134 }
135
136 @Override
137 public void forEachEntryWithMultiplicities(BiConsumer<Long, Integer> entryConsumer) {
138 super.forEachKeyValue(entryConsumer::accept);
139 }
140
141 @Override
142 public int hashCode() {
143 return IMemoryView.hashCode(this);
144 }
145 @Override
146 public boolean equals(Object obj) {
147 return IMemoryView.equals(this, obj);
148 }
149
150}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java
new file mode 100644
index 00000000..fceb54fc
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java
@@ -0,0 +1,212 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.util;
10
11import java.util.Collection;
12import java.util.Iterator;
13import java.util.Set;
14
15import org.eclipse.collections.api.LongIterable;
16import org.eclipse.collections.api.iterator.LongIterator;
17import org.eclipse.collections.api.set.primitive.LongSet;
18import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet;
19
20/**
21 * @author Gabor Bergmann
22 * @since 2.0
23 */
24public class EclipseCollectionsLongSetMemory extends LongHashSet implements ISetMemory<Long> {
25
26 @Override
27 public boolean addOne(Long value) {
28 return super.add(value);
29 }
30
31 @Override
32 public boolean addSigned(Long value, int count) {
33 if (count == 1) return addOne(value);
34 else if (count == -1) return removeOne(value);
35 else throw new IllegalStateException();
36 }
37
38 @Override
39 public boolean removeOne(Long value) {
40 // Kept for binary compatibility
41 return ISetMemory.super.removeOne(value);
42 }
43
44 /**
45 * @since 2.3
46 */
47 @Override
48 public boolean removeOneOrNop(Long value) {
49 return super.remove(value);
50 }
51
52 @Override
53 public void clearAllOf(Long value) {
54 super.remove(value);
55 }
56
57 @Override
58 public int getCount(Long value) {
59 return super.contains(value) ? 1 : 0;
60 }
61
62 @Override
63 public int getCountUnsafe(Object value) {
64 return value instanceof Long ? getCount((Long) value) : 0;
65 }
66
67 @Override
68 public boolean containsNonZero(Long value) {
69 return super.contains(value);
70 }
71
72 @Override
73 public boolean containsNonZeroUnsafe(Object value) {
74 return value instanceof Long && containsNonZero((Long) value);
75 }
76
77 @Override
78 public Iterator<Long> iterator() {
79 return iteratorOf(this);
80 }
81
82 @Override
83 public Set<Long> distinctValues() {
84 return new SetWrapper(this);
85 }
86
87 @Override
88 public boolean isEmpty() {
89 return super.isEmpty();
90 }
91
92 /**
93 * Helper for iterating a LongIterable
94 */
95 public static Iterator<Long> iteratorOf(LongIterable wrapped) {
96 return new Iterator<Long>() {
97 LongIterator longIterator = wrapped.longIterator();
98
99 @Override
100 public boolean hasNext() {
101 return longIterator.hasNext();
102 }
103
104 @Override
105 public Long next() {
106 return longIterator.next();
107 }
108 };
109 }
110
111 @Override
112 public int hashCode() {
113 return IMemoryView.hashCode(this);
114 }
115 @Override
116 public boolean equals(Object obj) {
117 return IMemoryView.equals(this, obj);
118 }
119
120
121 /**
122 * Helper that presents a primitive collection as a Set view
123 * @author Gabor Bergmann
124 */
125 public static final class SetWrapper implements Set<Long> {
126 private LongSet wrapped;
127
128 /**
129 * @param wrapped
130 */
131 public SetWrapper(LongSet wrapped) {
132 this.wrapped = wrapped;
133 }
134
135 @Override
136 public int size() {
137 return wrapped.size();
138 }
139
140 @Override
141 public boolean isEmpty() {
142 return wrapped.isEmpty();
143 }
144
145 @Override
146 public boolean contains(Object o) {
147 return o instanceof Long && wrapped.contains((Long)o);
148 }
149
150 @Override
151 public Iterator<Long> iterator() {
152 return iteratorOf(wrapped);
153 }
154
155 @Override
156 public boolean containsAll(Collection<?> c) {
157 for (Object object : c) {
158 if (contains(object))
159 return true;
160 }
161 return false;
162 }
163
164 @Override
165 public Object[] toArray() {
166 return toArray(new Long[wrapped.size()]);
167 }
168
169 @Override
170 @SuppressWarnings("unchecked")
171 public <T> T[] toArray(T[] a) {
172 int k = 0;
173 LongIterator iterator = wrapped.longIterator();
174 while (iterator.hasNext())
175 a[k++] = (T) Long.valueOf(iterator.next());
176 return a;
177 }
178
179 @Override
180 public boolean add(Long e) {
181 throw new UnsupportedOperationException();
182 }
183
184 @Override
185 public boolean remove(Object o) {
186 throw new UnsupportedOperationException();
187 }
188
189 @Override
190 public boolean addAll(Collection<? extends Long> c) {
191 throw new UnsupportedOperationException();
192 }
193
194 @Override
195 public boolean retainAll(Collection<?> c) {
196 throw new UnsupportedOperationException();
197 }
198
199 @Override
200 public boolean removeAll(Collection<?> c) {
201 throw new UnsupportedOperationException();
202 }
203
204 @Override
205 public void clear() {
206 throw new UnsupportedOperationException();
207 }
208
209
210 }
211
212}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java
new file mode 100644
index 00000000..394135c9
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java
@@ -0,0 +1,226 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.util;
10
11import org.eclipse.collections.impl.map.mutable.UnifiedMap;
12import org.eclipse.collections.impl.map.mutable.primitive.LongObjectHashMap;
13import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedMultiset;
14import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedSet;
15
16import java.util.Set;
17import java.util.stream.Stream;
18
19
20
21/**
22 * Eclipse Collections-based realizations of {@link IMultiLookup}
23 *
24 * @author Gabor Bergmann
25 * @since 2.0
26 */
27class EclipseCollectionsMultiLookup {
28
29 private EclipseCollectionsMultiLookup() {/* Hidden utility class constructor */}
30
31 private static class MarkedSetImpl<Value> extends EclipseCollectionsSetMemory<Value> implements MarkedMemory.MarkedSet<Value> {}
32 private static class MarkedMultisetImpl<Value> extends EclipseCollectionsMultiset<Value> implements MarkedMemory.MarkedMultiset<Value> {}
33 private static class MarkedLongSetImpl extends EclipseCollectionsLongSetMemory implements MarkedMemory.MarkedSet<Long> {}
34 private static class MarkedLongMultisetImpl extends EclipseCollectionsLongMultiset implements MarkedMemory.MarkedMultiset<Long> {}
35
36 public abstract static class FromObjects<Key, Value, Bucket extends MarkedMemory<Value>>
37 extends UnifiedMap<Key, Object> implements IMultiLookupAbstract<Key, Value, Bucket> {
38
39 @Override
40 public boolean equals(Object obj) {
41 return IMultiLookup.equals(this, obj);
42 }
43 @Override
44 public int hashCode() {
45 return IMultiLookup.hashCode(this);
46 }
47
48
49 @Override
50 public Object lowLevelPutIfAbsent(Key key, Value value) {
51 return super.putIfAbsent(key, value);
52 }
53
54 @Override
55 public Object lowLevelGet(Key key) {
56 return super.get(key);
57 }
58
59 @Override
60 public Object lowLevelGetUnsafe(Object key) {
61 return super.get(key);
62 }
63
64 @Override
65 public Object lowLevelRemove(Key key) {
66 return super.remove(key);
67 }
68
69 @Override
70 public void lowLevelPut(Key key, Object valueOrBucket) {
71 super.put(key, valueOrBucket);
72 }
73 @Override
74 public Iterable<Object> lowLevelValues() {
75 return super.values();
76 }
77 @Override
78 public Set<Key> lowLevelKeySet() {
79 return super.keySet();
80 }
81 @Override
82 public int lowLevelSize() {
83 return super.size();
84 }
85
86 @Override
87 public Stream<Key> distinctKeysStream() {
88 // may be more efficient than the default spliterator
89 return super.keySet().stream();
90 }
91
92 public abstract static class ToSets<Key, Value> extends FromObjects<Key, Value, MarkedSet<Value>>
93 implements IMultiLookupAbstract.ToSetsAbstract<Key, Value>
94 {
95 public static class OfObjects<Key, Value> extends ToSets<Key, Value> {
96 @Override
97 public MarkedSet<Value> createMarkedSet() {
98 return new MarkedSetImpl<Value>();
99 }
100 }
101
102 public static class OfLongs<Key> extends ToSets<Key, Long> {
103 @Override
104 public MarkedSet<Long> createMarkedSet() {
105 return new MarkedLongSetImpl();
106 }
107 }
108
109 }
110
111 public abstract static class ToMultisets<Key, Value> extends FromObjects<Key, Value, MarkedMultiset<Value>>
112 implements IMultiLookupAbstract.ToMultisetsAbstract<Key, Value>
113 {
114 public static class OfObjects<Key, Value> extends ToMultisets<Key, Value> {
115 @Override
116 public MarkedMultiset<Value> createMarkedMultiset() {
117 return new MarkedMultisetImpl<Value>();
118 }
119 }
120
121 public static class OfLongs<Key> extends ToMultisets<Key, Long> {
122 @Override
123 public MarkedMultiset<Long> createMarkedMultiset() {
124 return new MarkedLongMultisetImpl();
125 }
126 }
127
128 }
129
130 }
131
132 public abstract static class FromLongs<Value, Bucket extends MarkedMemory<Value>>
133 extends LongObjectHashMap<Object> implements IMultiLookupAbstract<Long, Value, Bucket> {
134
135 @Override
136 public boolean equals(Object obj) {
137 return IMultiLookup.equals(this, obj);
138 }
139 @Override
140 public int hashCode() {
141 return IMultiLookup.hashCode(this);
142 }
143
144 @Override
145 public Object lowLevelPutIfAbsent(Long key, Value value) {
146 Object old = super.get(key);
147 if (old == null) super.put(key, value);
148 return old;
149 }
150
151 @Override
152 public Object lowLevelGet(Long key) {
153 return super.get(key);
154 }
155
156 @Override
157 public Object lowLevelGetUnsafe(Object key) {
158 return key instanceof Long ? super.get((Long)key) : null;
159 }
160
161 @Override
162 public Object lowLevelRemove(Long key) {
163 return super.remove(key);
164 }
165
166 @Override
167 public void lowLevelPut(Long key, Object valueOrBucket) {
168 super.put(key, valueOrBucket);
169 }
170 @Override
171 public Iterable<Object> lowLevelValues() {
172 return super.values();
173 }
174 @Override
175 public int lowLevelSize() {
176 return super.size();
177 }
178 @Override
179 public Iterable<Long> lowLevelKeySet() {
180 return () -> EclipseCollectionsLongSetMemory.iteratorOf(FromLongs.super.keysView());
181 }
182
183 public abstract static class ToSets<Value> extends FromLongs<Value, MarkedSet<Value>>
184 implements IMultiLookupAbstract.ToSetsAbstract<Long, Value>
185 {
186 public static class OfObjects<Value> extends ToSets<Value> {
187 @Override
188 public MarkedSet<Value> createMarkedSet() {
189 return new MarkedSetImpl<Value>();
190 }
191 }
192
193 public static class OfLongs extends ToSets<Long> {
194 @Override
195 public MarkedSet<Long> createMarkedSet() {
196 return new MarkedLongSetImpl();
197 }
198 }
199
200 }
201
202 public abstract static class ToMultisets<Value> extends FromLongs<Value, MarkedMultiset<Value>>
203 implements IMultiLookupAbstract.ToMultisetsAbstract<Long, Value>
204 {
205 public static class OfObjects<Value> extends ToMultisets<Value> {
206 @Override
207 public MarkedMultiset<Value> createMarkedMultiset() {
208 return new MarkedMultisetImpl<Value>();
209 }
210 }
211
212 public static class OfLongs extends ToMultisets<Long> {
213 @Override
214 public MarkedMultiset<Long> createMarkedMultiset() {
215 return new MarkedLongMultisetImpl();
216 }
217 }
218
219 }
220
221 }
222
223
224}
225
226
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java
new file mode 100644
index 00000000..46977c8b
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java
@@ -0,0 +1,93 @@
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.matchers.util;
10
11/**
12 * @author Gabor Bergmann
13 * @since 1.7
14 */
15public class EclipseCollectionsMultiset<T> extends EclipseCollectionsBagMemory<T> implements IMultiset<T> {
16
17 @Override
18 public boolean addOne(T value) {
19 int oldCount = super.getIfAbsent(value, 0);
20
21 super.put(value, oldCount + 1);
22
23 return oldCount == 0;
24 }
25
26 @Override
27 public boolean addPositive(T value, int count) {
28 if (count < 0) {
29 throw new IllegalArgumentException("The count value must be positive!");
30 }
31
32 int oldCount = super.getIfAbsent(value, 0);
33
34 super.put(value, oldCount + count);
35
36 return oldCount == 0;
37 }
38
39 @Override
40 public boolean addSigned(T value, int count) {
41 int oldCount = super.getIfAbsent(value, 0);
42 int newCount = oldCount + count;
43
44 boolean becomesZero = newCount == 0;
45 if (newCount < 0)
46 throw new IllegalStateException(String.format(
47 "Cannot remove %d occurrences of value '%s' as only %d would remain in %s",
48 count, value, newCount, this));
49 else if (becomesZero)
50 super.removeKey(value);
51 else // (newCount > 0)
52 super.put(value, newCount);
53
54 return becomesZero || oldCount == 0;
55 }
56
57
58 @Override
59 public boolean removeOne(T value) {
60 return removeOneInternal(value, true);
61 }
62
63 @Override
64 public boolean removeOneOrNop(T value) {
65 return removeOneInternal(value, false);
66 }
67
68 /**
69 * @since 2.3
70 */
71 protected boolean removeOneInternal(T value, boolean throwIfImpossible) {
72 int oldCount = super.getIfAbsent(value, 0);
73 if (oldCount == 0) {
74 if (throwIfImpossible) throw new IllegalStateException(String.format(
75 "Cannot remove value '%s' that is not contained in %s",
76 value, this));
77 else return false;
78 }
79
80 int rest = oldCount - 1;
81 boolean empty = rest == 0;
82
83 if (!empty) {
84 super.put(value, rest);
85 } else {
86 super.remove(value);
87 }
88
89 return empty;
90 }
91
92
93}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java
new file mode 100644
index 00000000..92f65246
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java
@@ -0,0 +1,94 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.util;
10
11import java.util.Set;
12
13import org.eclipse.collections.impl.set.mutable.UnifiedSet;
14
15/**
16 * @author Gabor Bergmann
17 * @since 2.0
18 */
19public class EclipseCollectionsSetMemory<Value> extends UnifiedSet<Value> implements ISetMemory<Value> {
20 @Override
21 public int getCount(Value value) {
22 return super.contains(value) ? 1 : 0;
23 }
24 @Override
25 public int getCountUnsafe(Object value) {
26 return super.contains(value) ? 1 : 0;
27 }
28 @Override
29 public boolean containsNonZero(Value value) {
30 return super.contains(value);
31 }
32
33 @Override
34 public boolean containsNonZeroUnsafe(Object value) {
35 return super.contains(value);
36 }
37
38 @Override
39 public boolean addOne(Value value) {
40 return super.add(value);
41 }
42
43 @Override
44 public boolean addSigned(Value value, int count) {
45 if (count == 1) return addOne(value);
46 else if (count == -1) return removeOne(value);
47 else throw new IllegalStateException();
48 }
49
50 @Override
51 public boolean removeOne(Value value) {
52 // Kept for binary compatibility
53 return ISetMemory.super.removeOne(value);
54 }
55
56 @Override
57 public boolean removeOneOrNop(Value value) {
58 return super.remove(value);
59 }
60
61 @Override
62 public void clearAllOf(Value value) {
63 super.remove(value);
64 }
65
66 @Override
67 public Set<Value> distinctValues() {
68 return this;
69 }
70
71 @Override
72 public Value theContainedVersionOf(Value value) {
73 return super.get(value);
74 }
75
76 @Override
77 @SuppressWarnings("unchecked")
78 public Value theContainedVersionOfUnsafe(Object value) {
79 if (super.contains(value))
80 return super.get((Value)value);
81 else return null;
82 }
83
84 @Override
85 public int hashCode() {
86 return IMemoryView.hashCode(this);
87 }
88 @Override
89 public boolean equals(Object obj) {
90 return IMemoryView.equals(this, obj);
91 }
92
93
94}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java
new file mode 100644
index 00000000..a17b3a3f
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java
@@ -0,0 +1,93 @@
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.matchers.util;
10
11import java.util.Collections;
12import java.util.Iterator;
13import java.util.Set;
14
15/**
16 * A singleton immutable empty memory.
17 * @author Gabor Bergmann
18 * @since 2.0
19 *
20 */
21public class EmptyMemory<T> implements IMemoryView<T> {
22
23 @SuppressWarnings("rawtypes")
24 private static final EmptyMemory INSTANCE = new EmptyMemory();
25
26 @SuppressWarnings("unchecked")
27 public static <T> EmptyMemory<T> instance() {
28 return INSTANCE;
29 }
30
31
32
33 /**
34 * Singleton; hidden constructor
35 */
36 private EmptyMemory() {
37 super();
38 }
39
40 @Override
41 public Iterator<T> iterator() {
42 return Collections.<T>emptySet().iterator();
43 }
44
45 @Override
46 public int getCount(T value) {
47 return 0;
48 }
49
50 @Override
51 public int getCountUnsafe(Object value) {
52 return 0;
53 }
54
55 @Override
56 public boolean containsNonZero(T value) {
57 return false;
58 }
59
60 @Override
61 public boolean containsNonZeroUnsafe(Object value) {
62 return false;
63 }
64
65 @Override
66 public int size() {
67 return 0;
68 }
69
70 @Override
71 public boolean isEmpty() {
72 return true;
73 }
74
75 @Override
76 public Set<T> distinctValues() {
77 return Collections.emptySet();
78 }
79
80 @Override
81 public int hashCode() {
82 return IMemoryView.hashCode(this);
83 }
84 @Override
85 public boolean equals(Object obj) {
86 return IMemoryView.equals(this, obj);
87 }
88
89 @Override
90 public String toString() {
91 return "{}";
92 }
93}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java
new file mode 100644
index 00000000..8c2e54ad
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java
@@ -0,0 +1,32 @@
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.matchers.util;
10
11import java.util.function.Supplier;
12
13/**
14 * A cache is a simple key-value pair that stores calculated values for specific key objects
15 *
16 * <p>
17 * <b>NOTE</b> These caches are not expected to be used outside query backend implementations
18 *
19 * @author Zoltan Ujhelyi
20 * @since 1.7
21 * @noreference This interface is not intended to be referenced by clients.
22 */
23public interface ICache {
24
25 /**
26 * Return a selected value for the key object. If the value is not available in the cache yet, the given provider is
27 * called once
28 * @since 2.0
29 */
30 <T> T getValue(Object key, Class<? extends T> clazz, Supplier<T> valueProvider);
31
32} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java
new file mode 100644
index 00000000..99a4cb3b
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java
@@ -0,0 +1,26 @@
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.matchers.util;
10
11/**
12 * An {@link IMemory} that represents the difference between two states of a set or {@link IMultiset}, and therefore
13 * may contain values with a negative multiplicity.
14 *
15 * @author Gabor Bergmann
16 * @since 1.7
17 */
18public interface IDeltaBag<T> extends IMemory<T> {
19
20 @Override
21 default boolean removeOneOrNop(T value) {
22 // makes no difference for delta bags
23 return removeOne(value);
24 }
25
26}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java
new file mode 100644
index 00000000..ea788e53
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java
@@ -0,0 +1,81 @@
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.matchers.util;
10
11/**
12 * A memory containing a positive or negative number of equal() copies for some values.
13 * During iterations, each distinct value is iterated only once.
14 *
15 * <p> Refined by: <ul>
16 * <li>{@link IMultiset}, which always contains values with a nonnegative multiplicity. </li>
17 * <li>{@link IDeltaBag}, which may contain values with negative multiplicity. </li>
18 * <li>{@link ISetMemory}, which is just a set (allowed multiplicities: 0 and 1). </li>
19 * </ul>
20 *
21 * @author Gabor Bergmann
22 * @since 1.7
23 * @noimplement This interface is not intended to be implemented by clients.
24 */
25public interface IMemory<T> extends IMemoryView<T>, Clearable {
26
27 /**
28 * Adds one value occurrence to the memory.
29 *
30 * @return true if the tuple was not present before in the memory, or
31 * (in case of {@link IDeltaBag}) is no longer present in the memory
32 */
33 boolean addOne(T value);
34
35 /**
36 * Adds the given number of occurrences to the memory. The count value may or may not be negative.
37 * <p> Precondition if {@link IMultiset}: at least the given amount of occurrences exist, if count is negative.
38 * <p> Precondition if {@link ISetMemory}: count is +1 or -1, the latter is only allowed if the set contains the value.
39 *
40 * @param count
41 * the number of occurrences
42 * @return true if the tuple was not present before in the memory, or is no longer present in the memory
43 * @throws IllegalStateException if {@link IMultiset} or {@link ISetMemory} and the number of occurrences in the memory would underflow to negative
44 */
45 boolean addSigned(T value, int count);
46
47 /**
48 * Removes one occurrence of the given value from the memory.
49 * <p> Precondition if {@link IMultiset} or {@link ISetMemory}: the value must have a positive amount of occurrences in the memory.
50 *
51 * @return true if this was the the last occurrence of the value, or
52 * (in case of {@link IDeltaBag}) is the first negative occurrence of the value
53 * @throws IllegalStateException if {@link IMultiset} or {@link ISetMemory} and value had no occurrences in the memory
54 */
55 boolean removeOne(T value);
56
57 /**
58 * Removes one occurrence of the given value from the memory, if possible.
59 *
60 * <p> Memory is unchanged and false is returned if
61 * {@link IMultiset} or {@link ISetMemory} and value had no occurrences in the memory
62 *
63 * @return true if this was the the last occurrence of the value, or
64 * (in case of {@link IDeltaBag}) is the first negative occurrence of the value
65 *
66 * @since 2.3
67 */
68 boolean removeOneOrNop(T value);
69
70 /**
71 * Removes all occurrences of the given value from the memory.
72 */
73 void clearAllOf(T value);
74
75 /**
76 * Empties out the memory.
77 */
78 @Override
79 void clear();
80
81} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java
new file mode 100644
index 00000000..add575c6
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java
@@ -0,0 +1,205 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.util;
10
11import java.util.Iterator;
12import java.util.Map;
13import java.util.Map.Entry;
14import java.util.Set;
15import java.util.function.BiConsumer;
16import java.util.stream.Stream;
17import java.util.stream.StreamSupport;
18
19/**
20 * A read-only view on a memory containing a positive or negative number of equal() copies for some values.
21 * During iterations, each distinct value is iterated only once.
22 *
23 * <p> See {@link IMemory}.
24 *
25 * <p> Implementors must provide semantic (not identity-based) hashCode() and equals() using the static helpers {@link #hashCode(IMemoryView)} and {@link #equals(IMemoryView, Object)} here.
26 *
27 * @author Gabor Bergmann
28 *
29 * @since 2.0
30 */
31public interface IMemoryView<T> extends Iterable<T> {
32
33 /**
34 * Returns the number of occurrences of the given value.
35 *
36 * @return the number of occurrences
37 */
38 int getCount(T value);
39
40 /**
41 * Returns the number of occurrences of the given value (which may be of any type).
42 *
43 * @return the number of occurrences
44 */
45 int getCountUnsafe(Object value);
46
47 /**
48 * @return true if the given value is contained with a nonzero multiplicity
49 */
50 boolean containsNonZero(T value);
51
52 /**
53 * @return true if the given value (which may be of any type) is contained with a nonzero multiplicity
54 */
55 boolean containsNonZeroUnsafe(Object value);
56
57 /**
58 * @return the number of distinct values
59 */
60 int size();
61
62 /**
63 *
64 * @return iff contains at least one value with non-zero occurrences
65 */
66 boolean isEmpty();
67
68 /**
69 * The set of distinct values
70 */
71 Set<T> distinctValues();
72
73
74 /**
75 * Where supported, returns the stored element that is equal to the given value, or null if none.
76 * Useful for canonicalization in case of non-identity equals().
77 *
78 * <p> For collections that do not support canonicalization, simply returns the argument if contained, null if none.
79 *
80 * @return a value equal to the argument if such a value is stored, or null if none
81 */
82 default T theContainedVersionOf(T value) {
83 if (containsNonZero(value)) return value; else return null;
84 }
85
86 /**
87 * Where supported, returns the stored element that is equal to the given value (of any type),
88 * or null if none.
89 * Useful for canonicalization in case of non-identity equals().
90 *
91 * <p> For collections that do not support canonicalization, simply returns the argument if contained, null if none.
92 *
93 * @return a value equal to the argument if such a value is stored, or null if none
94 */
95 @SuppressWarnings("unchecked")
96 default T theContainedVersionOfUnsafe(Object value) {
97 if (containsNonZeroUnsafe(value)) return (T) value; else return null;
98 }
99
100
101 /**
102 * @return an unmodifiable view of contained values with their multiplicities
103 */
104 default Iterable<Map.Entry<T, Integer>> entriesWithMultiplicities() {
105 return () -> {
106 Iterator<T> wrapped = distinctValues().iterator();
107 return new Iterator<Map.Entry<T, Integer>> () {
108 @Override
109 public boolean hasNext() {
110 return wrapped.hasNext();
111 }
112
113 @Override
114 public Map.Entry<T, Integer> next() {
115 T key = wrapped.next();
116 int count = getCount(key);
117 return new Map.Entry<T, Integer>(){
118 @Override
119 public T getKey() {
120 return key;
121 }
122
123 @Override
124 public Integer getValue() {
125 return count;
126 }
127
128 @Override
129 public Integer setValue(Integer value) {
130 throw new UnsupportedOperationException();
131 }
132
133 @Override
134 public String toString() {
135 return String.format("%d of %s", count, key);
136 }
137
138 };
139 }
140
141 };
142 };
143 }
144
145 /**
146 * Process contained values with their multiplicities
147 */
148 default void forEachEntryWithMultiplicities(BiConsumer<T, Integer> entryConsumer) {
149 for (T value : distinctValues()) {
150 entryConsumer.accept(value, getCount(value));
151 }
152 }
153
154
155 /**
156 * For compatibility with legacy code relying on element-to-integer maps.
157 * @return an unmodifiable view of contained values with their multiplicities
158 */
159 public default Map<T, Integer> asMap() {
160 return new MemoryViewBackedMapView<>(this);
161 }
162
163 /**
164 * For compatibility with legacy code relying on element-to-integer maps.
165 * @return an unmodifiable view of contained values with their multiplicities
166 */
167 public static <T> IMemoryView<T> fromMap(Map<T, Integer> wrapped) {
168 return new MapBackedMemoryView<>(wrapped);
169 }
170
171 /**
172 * @return a stream of values, iterable once
173 * @since 2.1
174 */
175 public default Stream<T> asStream() {
176 return StreamSupport.stream(spliterator(), false);
177 }
178
179 /**
180 * Provides semantic equality comparison.
181 */
182 public static <T> boolean equals(IMemoryView<T> self, Object obj) {
183 if (obj instanceof IMemoryView<?>) {
184 IMemoryView<?> other = (IMemoryView<?>) obj;
185 if (other.size() != self.size()) return false;
186 for (Entry<?, Integer> entry : other.entriesWithMultiplicities()) {
187 if ( !entry.getValue().equals(self.getCountUnsafe(entry.getKey())))
188 return false;
189 }
190 return true;
191 }
192 return false;
193 }
194
195 /**
196 * Provides semantic hashCode() comparison.
197 */
198 public static <T> int hashCode(IMemoryView<T> memory) {
199 int hashCode = 0;
200 for (T value : memory.distinctValues()) {
201 hashCode += value.hashCode() ^ Integer.hashCode(memory.getCount(value));
202 }
203 return hashCode;
204 }
205} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java
new file mode 100644
index 00000000..1ce1d2c9
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java
@@ -0,0 +1,216 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.util;
10
11import java.util.stream.Stream;
12
13import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
14
15/**
16 * A multi-map that associates sets / multisets / delta sets of values to each key.
17 *
18 * <p> Implementors must provide semantic (not identity-based) hashCode() and equals() using the static helpers {@link #hashCode(IMultiLookup)} and {@link #equals(IMultiLookup, Object)} here.
19 *
20 * @author Gabor Bergmann
21 * @since 2.0
22 * @noimplement This interface is not intended to be implemented by clients.
23 */
24public interface IMultiLookup<Key, Value> {
25
26 /**
27 * Returns true if this collection is empty, false otherwise.
28 * @since 2.2
29 */
30 boolean isEmpty();
31
32
33 /**
34 * Returns true if there are any values associated with the given key.
35 * @param key a key for which associated values are sought
36 * @since 2.3
37 */
38 boolean lookupExists(Key key);
39
40 /**
41 * Returns a (read-only) bucket of values associated with the given key.
42 * Clients must not modify the returned bucket.
43 * @param key a key for which associated values are sought
44 * @return null if key not found, a bucket of values otherwise
45 */
46 IMemoryView<Value> lookup(Key key);
47
48 /**
49 * Returns a (read-only) bucket of values associated with the given key.
50 * Clients must not modify the returned bucket.
51 * @param key a key for which associated values are sought
52 * @return a bucket of values, never null
53 */
54 default IMemoryView<Value> lookupOrEmpty(Key key) {
55 IMemoryView<Value> bucket = lookup(key);
56 return bucket == null ? EmptyMemory.instance() : bucket;
57 }
58
59 /**
60 * Returns a (read-only) bucket of values associated with the given key, while simultaneously removing them.
61 * Clients must not modify the returned bucket.
62 * @param key a key for which associated values are sought
63 * @return a bucket of values, never null
64 * @since 2.3
65 */
66 IMemoryView<Value> lookupAndRemoveAll(Key key);
67
68 /**
69 * Returns a (read-only) bucket of values associated with the given key, which can be of any type.
70 * Clients must not modify the returned bucket.
71 * @param key a key for which associated values are sought (may or may not be of Key type)
72 * @return null if key not found, a bucket of values otherwise
73 */
74 IMemoryView<Value> lookupUnsafe(Object key);
75
76 /**
77 * Returns a (read-only) bucket of values associated with the given key.
78 * Clients must not modify the returned bucket.
79 * @param key a key for which associated values are sought (may or may not be of Key type)
80 * @return a bucket of values, never null
81 */
82 default IMemoryView<Value> lookupUnsafeOrEmpty(Object key) {
83 IMemoryView<Value> bucket = lookupUnsafe(key);
84 return bucket == null ? EmptyMemory.instance() : bucket;
85 }
86
87
88
89 /**
90 * @return the set of distinct keys that have values associated.
91 */
92 Iterable<Key> distinctKeys();
93
94 /**
95 * @return the set of distinct keys that have values associated.
96 * @since 2.3
97 */
98 Stream<Key> distinctKeysStream();
99
100 /**
101 * @return the number of distinct keys that have values associated.
102 */
103 int countKeys();
104
105 /**
106 * Iterates once over each distinct value.
107 */
108 Iterable<Value> distinctValues();
109
110 /**
111 * Iterates once over each distinct value.
112 * @since 2.3
113 */
114 Stream<Value> distinctValuesStream();
115
116
117
118 /**
119 * How significant was the change? *
120 * @author Gabor Bergmann
121 */
122 public enum ChangeGranularity {
123 /**
124 * First key-value pair with given key inserted, or last pair with given key deleted.
125 * (In case of delta maps, also if last negative key-value pair with given key neutralized.)
126 */
127 KEY,
128 /**
129 * First occurrence of given key-value pair inserted, or last occurrence of the pair deleted, while key still has values associated.
130 * (In case of delta maps, also if last negative occurrence of key-value pair neutralized.)
131 */
132 VALUE,
133 /**
134 * Duplicate key-value pair inserted or deleted.
135 */
136 DUPLICATE
137 }
138
139 /**
140 * Adds key-value pair to the lookup structure, or fails if not possible.
141 * <p> If the addition would cause duplicates but the bucket type does not allow it ({@link MemoryType#SETS}),
142 * the operation throws an {@link IllegalStateException}.
143 * @return the granularity of the change
144 * @throws IllegalStateException if addition would cause duplication that is not permitted
145 */
146 public ChangeGranularity addPair(Key key, Value value);
147 /**
148 * Adds key-value pair to the lookup structure.
149 * <p> If the addition would cause duplicates but the bucket type does not allow it ({@link MemoryType#SETS}),
150 * the operation is silently ignored and {@link ChangeGranularity#DUPLICATE} is returned.
151 * @return the granularity of the change, or {@link ChangeGranularity#DUPLICATE} if addition would result in a duplicate and therefore ignored
152 * @since 2.3
153 */
154 public ChangeGranularity addPairOrNop(Key key, Value value);
155 /**
156 * Removes key-value pair from the lookup structure, or fails if not possible.
157 * <p> When attempting to remove a key-value pair with zero multiplicity from a non-delta bucket type
158 * ({@link MemoryType#SETS} or {@link MemoryType#MULTISETS}}), an {@link IllegalStateException} is thrown.
159 * @return the granularity of the change
160 * @throws IllegalStateException if removing non-existing element that is not permitted
161 */
162 public ChangeGranularity removePair(Key key, Value value);
163 /**
164 * Removes key-value pair from the lookup structure.
165 * <p> When attempting to remove a key-value pair with zero multiplicity from a non-delta bucket type
166 * ({@link MemoryType#SETS} or {@link MemoryType#MULTISETS}}),
167 * the operation is silently ignored and {@link ChangeGranularity#DUPLICATE} is returned.
168 * @return the granularity of the change
169 * @throws IllegalStateException if removing non-existing element that is not permitted
170 * @since 2.3
171 */
172 public ChangeGranularity removePairOrNop(Key key, Value value);
173
174 /**
175 * Updates multiplicity of key-value pair by a positive amount.
176 *
177 * <p> PRE: count > 0
178 *
179 * @return the granularity of the change
180 * @throws IllegalStateException if addition would cause duplication that is not permitted
181 */
182 public ChangeGranularity addPairPositiveMultiplicity(Key key, Value value, int count);
183
184 /**
185 * Empties out the lookup structure.
186 */
187 public void clear();
188
189 /**
190 * Provides semantic equality comparison.
191 */
192 public static <Key, Value> boolean equals(IMultiLookup<Key, Value> self, Object obj) {
193 if (obj instanceof IMultiLookup<?, ?>) {
194 IMultiLookup<?, ?> other = (IMultiLookup<?, ?>) obj;
195 if (other.countKeys() != self.countKeys()) return false;
196 for (Object key : other.distinctKeys()) {
197 if (! other.lookupUnsafe(key).equals(self.lookupUnsafe(key)))
198 return false;
199 }
200 return true;
201 }
202 return false;
203 }
204
205 /**
206 * Provides semantic hashCode() comparison.
207 */
208 public static <Key, Value> int hashCode(IMultiLookup<Key, Value> memory) {
209 int hashCode = 0;
210 for (Key key : memory.distinctKeys()) {
211 hashCode += key.hashCode() ^ memory.lookup(key).hashCode();
212 }
213 return hashCode;
214 }
215
216}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java
new file mode 100644
index 00000000..8b1944c1
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java
@@ -0,0 +1,485 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.util;
10
11import java.util.Collections;
12import java.util.Iterator;
13import java.util.NoSuchElementException;
14import java.util.Objects;
15import java.util.stream.Stream;
16import java.util.stream.StreamSupport;
17
18import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedSet;
19
20/**
21 * Specialized multimap implementation that saves memory
22 * by storing singleton value objects (multiplicity 1) instead of multiset buckets
23 * whenever there is only one value associated with a key.
24 *
25 * <p> See specialized {@link ToSetsAbstract}, {@link ToMultisetsAbstract} for various bucket types.
26 *
27 * <p> Implemented as a Key->Object map with invariant: <ul>
28 * <li> key maps to null if associated with no values;
29 * <li> key maps to a single Value iff it is associated with a single value of multiplicity +1;
30 * <li> key maps to Bucket otherwise
31 * </ul>
32 *
33 * Note that due to the above invariant, handling +1 and -1 are asymmetric in case of delta maps.
34 *
35 * <p> Not intended as an API, but rather as a 'base class' for implementors.
36 * Realized as an interface with default implementations, instead of an abstract class,
37 * to ensure that implementors can easily choose a base class such as UnifiedMap to augment.
38 *
39 * <p> Implementor should inherit from a Map<Key, Object>-like class (primitive map possible)
40 * and bind the lowLevel* methods accordingly.
41 *
42 * @noreference This interface is not intended to be referenced by clients.
43 * @noimplement This interface is not intended to be implemented by clients.
44 *
45 * @author Gabor Bergmann
46 * @since 2.0
47 *
48 *
49 */
50public interface IMultiLookupAbstract<Key, Value, Bucket extends MarkedMemory<Value>> extends IMultiLookup<Key, Value> {
51
52 // the following methods must be bound to a concrete Map<Key,Object>-like structure (primitive implementation allowed)
53
54 /**
55 * Implementor shall bind to the low-level get() or equivalent of the underlying Key-to-Object map
56 */
57 abstract Object lowLevelGet(Key key);
58
59 /**
60 * Implementor shall bind to the low-level get() or equivalent of the underlying Key-to-Object map
61 */
62 abstract Object lowLevelGetUnsafe(Object key);
63
64 /**
65 * Implementor shall bind to the low-level remove() or equivalent of the underlying Key-to-Object map
66 */
67 abstract Object lowLevelRemove(Key key);
68
69 /**
70 * Implementor shall bind to the low-level putIfAbsent() or equivalent of the underlying Key-to-Object map
71 */
72 abstract Object lowLevelPutIfAbsent(Key key, Value value);
73
74 /**
75 * Implementor shall bind to the low-level put() or equivalent of the underlying Key-to-Object map
76 */
77 abstract void lowLevelPut(Key key, Object valueOrBucket);
78
79 /**
80 * Implementor shall bind to the low-level values() or equivalent of the underlying Key-to-Object map
81 */
82 abstract Iterable<Object> lowLevelValues();
83
84 /**
85 * Implementor shall bind to the low-level keySet() or equivalent of the underlying Key-to-Object map
86 */
87 abstract Iterable<Key> lowLevelKeySet();
88
89 /**
90 * Implementor shall bind to the low-level size() or equivalent of the underlying Key-to-Object map
91 */
92 abstract int lowLevelSize();
93
94
95 // generic multi-lookup logic
96
97 @Override
98 default boolean lookupExists(Key key) {
99 Object object = lowLevelGet(key);
100 return null != object;
101 }
102
103 @Override
104 public default IMemoryView<Value> lookup(Key key) {
105 Object object = lowLevelGet(key);
106 if (object == null) return null;
107 if (object instanceof MarkedMemory) return (Bucket) object;
108 return yieldSingleton((Value)object);
109 }
110
111 @Override
112 default IMemoryView<Value> lookupAndRemoveAll(Key key) {
113 Object object = lowLevelRemove(key);
114 if (object == null) return EmptyMemory.instance();
115 if (object instanceof MarkedMemory) return (Bucket) object;
116 return yieldSingleton((Value)object);
117 }
118
119 @Override
120 public default IMemoryView<Value> lookupUnsafe(Object key) {
121 Object object = lowLevelGetUnsafe(key);
122 if (object == null) return null;
123 if (object instanceof MarkedMemory) return (Bucket) object;
124 return yieldSingleton((Value)object);
125 }
126
127 @Override
128 public default ChangeGranularity addPair(Key key, Value value) {
129 return addPairInternal(key, value, true);
130 }
131
132 @Override
133 default ChangeGranularity addPairOrNop(Key key, Value value) {
134 return addPairInternal(key, value, false);
135 }
136
137 public default ChangeGranularity addPairInternal(Key key, Value value, boolean throwIfImpossible) {
138 Object old = lowLevelPutIfAbsent(key, value);
139 boolean keyChange = (old == null);
140
141 if (keyChange) { // key was not present
142 return ChangeGranularity.KEY;
143 } else { // key was already present
144 Bucket bucket;
145 if (old instanceof MarkedMemory) { // ... as collection
146 bucket = (Bucket) old;
147 } else { // ... as singleton
148 if (!this.duplicatesAllowed() && Objects.equals(value, old)) {
149 if (throwIfImpossible)
150 throw new IllegalStateException();
151 else
152 return ChangeGranularity.DUPLICATE;
153 }
154 bucket = createSingletonBucket((Value) old);
155 lowLevelPut(key, bucket);
156 }
157 // will throw if forbidden duplicate, return false if allowed duplicate
158 if (addToBucket(bucket, value, throwIfImpossible)) {
159 // deltas may become empty or a singleton after addition!
160 if (negativesAllowed()) {
161 if (bucket.isEmpty()) {
162 lowLevelRemove(key);
163 return ChangeGranularity.KEY;
164 } else {
165 handleSingleton(key, bucket);
166 return ChangeGranularity.VALUE;
167 }
168 } else return ChangeGranularity.VALUE;
169 } else return ChangeGranularity.DUPLICATE;
170 }
171 }
172
173 @Override
174 // TODO deltas not supproted yet
175 default ChangeGranularity addPairPositiveMultiplicity(Key key, Value value, int count) {
176 if (count == 1) return addPair(key, value);
177 // count > 1, always end up with non-singleton bucket
178
179 Object old = lowLevelGet(key);
180 boolean keyChange = (old == null);
181
182 Bucket bucket;
183 if (keyChange) { // ... nothing associated to key yet
184 bucket = createSingletonBucket(value);
185 lowLevelPut(key, bucket);
186 --count; // one less to increment later
187 } else if (old instanceof MarkedMemory) { // ... as collection
188 bucket = (Bucket) old;
189 } else { // ... as singleton
190 bucket = createSingletonBucket((Value) old);
191 lowLevelPut(key, bucket);
192 }
193
194 boolean newValue = bucket.addSigned(value, count);
195
196 if (keyChange) return ChangeGranularity.KEY;
197 else if (newValue) return ChangeGranularity.VALUE;
198 else return ChangeGranularity.DUPLICATE;
199 }
200
201 @Override
202 public default ChangeGranularity removePair(Key key, Value value) {
203 return removePairInternal(key, value, true);
204 }
205
206 @Override
207 default ChangeGranularity removePairOrNop(Key key, Value value) {
208 return removePairInternal(key, value, false);
209 }
210
211 public default ChangeGranularity removePairInternal(Key key, Value value, boolean throwIfImpossible) {
212 Object old = lowLevelGet(key);
213 if (old instanceof MarkedMemory) { // ... as collection
214 @SuppressWarnings("unchecked")
215 Bucket bucket = (Bucket) old;
216 // will throw if removing non-existent, return false if removing duplicate
217 boolean valueChange = removeFromBucket(bucket, value, throwIfImpossible);
218 handleSingleton(key, bucket);
219 if (valueChange)
220 return ChangeGranularity.VALUE;
221 else
222 return ChangeGranularity.DUPLICATE;
223 } else if (value.equals(old)) { // matching singleton
224 lowLevelRemove(key);
225 return ChangeGranularity.KEY;
226 } else { // different singleton, will produce a delta if possible
227 if (negativesAllowed()) {
228 Bucket deltaBucket = createDeltaBucket((Value) old, value); // will throw if no deltas supported
229 lowLevelPut(key, deltaBucket);
230 return ChangeGranularity.VALUE; // no key change
231 } else {
232 if (throwIfImpossible)
233 throw new IllegalStateException();
234 else
235 return ChangeGranularity.DUPLICATE;
236 }
237 }
238 }
239
240 public default void handleSingleton(Key key, Bucket bucket) {
241 Value remainingSingleton = asSingleton(bucket);
242 if (remainingSingleton != null) { // only one remains
243 lowLevelPut(key, remainingSingleton);
244 }
245 }
246
247 @Override
248 public default Iterable<Value> distinctValues() {
249 return new Iterable<Value>() {
250 private final Iterator<Value> EMPTY_ITERATOR = Collections.<Value>emptySet().iterator();
251 @Override
252 public Iterator<Value> iterator() {
253 return new Iterator<Value>() {
254 Iterator<Object> bucketIterator = lowLevelValues().iterator();
255 Iterator<Value> elementIterator = EMPTY_ITERATOR;
256
257 @Override
258 public boolean hasNext() {
259 return (elementIterator.hasNext() || bucketIterator.hasNext());
260 }
261
262 @Override
263 public Value next() {
264 if (elementIterator.hasNext())
265 return elementIterator.next();
266 else if (bucketIterator.hasNext()) {
267 Object bucket = bucketIterator.next();
268 if (bucket instanceof MarkedMemory) {
269 elementIterator =
270 ((MarkedMemory) bucket).distinctValues().iterator();
271 return elementIterator.next();
272 } else {
273 elementIterator = EMPTY_ITERATOR;
274 return (Value) bucket;
275 }
276 } else
277 throw new NoSuchElementException();
278 }
279
280 /**
281 * Not implemented
282 */
283 @Override
284 public void remove() {
285 throw new UnsupportedOperationException();
286 }
287
288 };
289 }
290 };
291 }
292
293 @Override
294 default Stream<Value> distinctValuesStream() {
295 return StreamSupport.stream(distinctValues().spliterator(), false);
296 }
297
298 @Override
299 default Iterable<Key> distinctKeys() {
300 return lowLevelKeySet();
301 }
302
303 @Override
304 default Stream<Key> distinctKeysStream() {
305 return StreamSupport.stream(distinctKeys().spliterator(), false);
306 }
307
308 @Override
309 default int countKeys() {
310 return lowLevelSize();
311 }
312
313 // the following methods are customized for bucket type
314
315 /**
316 * @return iff negative multiplicites are allowed
317 */
318 abstract boolean negativesAllowed();
319
320 /**
321 * @return iff larger-than-1 multiplicites are allowed
322 * @since 2.3
323 */
324 abstract boolean duplicatesAllowed();
325
326 /**
327 * Increases the multiplicity of the value in the bucket.
328 * @return true iff non-duplicate
329 * @throws IllegalStateException if disallowed duplication and throwIfImpossible is specified
330 */
331 abstract boolean addToBucket(Bucket bucket, Value value, boolean throwIfImpossible);
332
333 /**
334 * Decreases the multiplicity of the value in the bucket.
335 * @return false if removing duplicate value
336 * @throws IllegalStateException if removing non-existing value (unless delta map) and throwIfImpossible is specified
337 */
338 abstract boolean removeFromBucket(Bucket bucket, Value value, boolean throwIfImpossible);
339
340 /**
341 * Checks whether the bucket is a singleton, i.e. it contains a single value with multiplicity +1
342 * @return the singleton value, or null if the bucket is not singleton
343 */
344 abstract Value asSingleton(Bucket bucket);
345
346 /**
347 * @return a new bucket consisting of a sole value
348 */
349 abstract Bucket createSingletonBucket(Value value);
350 /**
351 * @return a read-only bucket consisting of a sole value, to be returned to the user
352 */
353 default IMemoryView<Value> yieldSingleton(Value value) {
354 return new SingletonMemoryView<>(value);
355 }
356
357 /**
358 * @param positive the previously existing value, or null if the delta is to contain a single negative tuple
359 * @return a new bucket consisting of a delta of two values
360 * @throws IllegalStateException if deltas not supported
361 */
362 abstract Bucket createDeltaBucket(Value positive, Value negative);
363
364 /**
365 * A multi-lookup whose buckets are sets.
366 *
367 * <p> Not intended as an API, but rather as a 'base class' for implementors.
368 * Realized as an interface with default implementations, instead of an abstract class,
369 * to ensure that implementors can easily choose a base class such as UnifiedMap to augment.
370 *
371 * <p> Implementor should inherit from a Map<Key, Object>-like class (primitive map possible)
372 * and bind the lowLevel* methods accordingly.
373 *
374 * @noreference This interface is not intended to be referenced by clients.
375 * @noimplement This interface is not intended to be implemented by clients.
376 * @author Gabor Bergmann
377 */
378 public static interface ToSetsAbstract<Key, Value> extends IMultiLookupAbstract<Key, Value, MarkedMemory.MarkedSet<Value>> {
379 /**
380 * @return a fresh, empty marked set
381 */
382 public MarkedSet<Value> createMarkedSet();
383
384 @Override
385 public default boolean negativesAllowed() {
386 return false;
387 }
388 @Override
389 default boolean duplicatesAllowed() {
390 return false;
391 }
392
393 @Override
394 public default boolean addToBucket(MarkedSet<Value> bucket, Value value, boolean throwIfImpossible) {
395 if (bucket.addOne(value)) return true;
396 else if (throwIfImpossible) throw new IllegalStateException();
397 else return false;
398 }
399
400 @Override
401 public default boolean removeFromBucket(MarkedSet<Value> bucket, Value value, boolean throwIfImpossible) {
402 return throwIfImpossible ? bucket.removeOne(value) : bucket.removeOneOrNop(value);
403 }
404
405 @Override
406 public default Value asSingleton(MarkedSet<Value> bucket) {
407 return bucket.size() == 1 ? bucket.iterator().next() : null;
408 }
409
410 @Override
411 public default MarkedSet<Value> createSingletonBucket(Value value) {
412 MarkedSet<Value> result = createMarkedSet();
413 result.addOne(value);
414 return result;
415 }
416
417 @Override
418 public default MarkedSet<Value> createDeltaBucket(Value positive, Value negative) {
419 throw new IllegalStateException();
420 }
421 }
422
423 /**
424 * A multi-lookup whose buckets are multisets.
425 *
426 * <p> Not intended as an API, but rather as a 'base class' for implementors.
427 * Realized as an interface with default implementations, instead of an abstract class,
428 * to ensure that implementors can easily choose a base class such as UnifiedMap to augment.
429 *
430 * <p> Implementor should inherit from a Map<Key, Object>-like class (primitive map possible)
431 * and bind the lowLevel* methods accordingly.
432 *
433 * @noreference This interface is not intended to be referenced by clients.
434 * @noimplement This interface is not intended to be implemented by clients.
435 * @author Gabor Bergmann
436 */
437 public static interface ToMultisetsAbstract<Key, Value> extends IMultiLookupAbstract<Key, Value, MarkedMemory.MarkedMultiset<Value>> {
438 /**
439 * @return a fresh, empty marked multiset
440 */
441 public MarkedMemory.MarkedMultiset<Value> createMarkedMultiset();
442
443 @Override
444 public default boolean negativesAllowed() {
445 return false;
446 }
447 @Override
448 default boolean duplicatesAllowed() {
449 return true;
450 }
451
452 @Override
453 public default boolean addToBucket(MarkedMemory.MarkedMultiset<Value> bucket, Value value, boolean throwIfImpossible) {
454 return bucket.addOne(value);
455 }
456
457 @Override
458 public default boolean removeFromBucket(MarkedMemory.MarkedMultiset<Value> bucket, Value value, boolean throwIfImpossible) {
459 return throwIfImpossible ? bucket.removeOne(value) : bucket.removeOneOrNop(value);
460 }
461
462 @Override
463 public default Value asSingleton(MarkedMemory.MarkedMultiset<Value> bucket) {
464 if (bucket.size() != 1) return null;
465 Value candidate = bucket.iterator().next();
466 return bucket.getCount(candidate) == 1 ? candidate : null;
467 }
468
469 @Override
470 public default MarkedMemory.MarkedMultiset<Value> createSingletonBucket(Value value) {
471 MarkedMemory.MarkedMultiset<Value> result = createMarkedMultiset();
472 result.addOne(value);
473 return result;
474 }
475
476 @Override
477 public default MarkedMemory.MarkedMultiset<Value> createDeltaBucket(Value positive, Value negative) {
478 throw new IllegalStateException();
479 }
480 }
481
482
483 // TODO add ToDeltaBagsAbstract
484
485} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java
new file mode 100644
index 00000000..bdd5d597
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java
@@ -0,0 +1,30 @@
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.matchers.util;
10
11/**
12 * An {@link IMemory} that always contains values with a nonnegative multiplicity.
13 *
14 * <p> In case a write operation caused underflow, an {@link IllegalStateException} is thrown.
15 *
16 * @author Gabor Bergmann
17 * @since 1.7
18 */
19public interface IMultiset<T> extends IMemory<T> {
20
21 /**
22 * Adds the given number of occurrences to the memory. The count value must be a positive number.
23 *
24 * @param count
25 * the number of occurrences
26 * @return true if the tuple was not present before in the memory
27 */
28 boolean addPositive(T value, int count);
29
30} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java
new file mode 100644
index 00000000..cd25dc95
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java
@@ -0,0 +1,30 @@
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.matchers.util;
10
11import java.util.function.Function;
12import java.util.function.Supplier;
13
14import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
15
16/**
17 * A provider interface useful in various registry instances.
18 *
19 * @author Zoltan Ujhelyi
20 *
21 */
22public interface IProvider<T> extends Supplier<T>{
23
24 public final class ProvidedValueFunction implements Function<IProvider<PQuery>, PQuery> {
25 @Override
26 public PQuery apply(IProvider<PQuery> input) {
27 return (input == null) ? null : input.get();
28 }
29 }
30}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java
new file mode 100644
index 00000000..0c03da48
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java
@@ -0,0 +1,37 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.util;
10
11import java.util.function.BiConsumer;
12
13/**
14 * An {@link IMemory} that always contains values with a 0 or +1 multiplicity.
15 *
16 * <p> In case a write operation causes underflow or overflow, an {@link IllegalStateException} is thrown.
17 *
18 * @author Gabor Bergmann
19 * @since 2.0
20 */
21public interface ISetMemory<T> extends IMemory<T> {
22
23 @Override
24 default void forEachEntryWithMultiplicities(BiConsumer<T, Integer> entryConsumer) {
25 for (T t : this.distinctValues()) entryConsumer.accept(t, 1);
26 }
27
28
29 @Override
30 default boolean removeOne(T value) {
31 if (!removeOneOrNop(value))
32 throw new IllegalStateException();
33 return true;
34 }
35
36
37}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java
new file mode 100644
index 00000000..3be078bd
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java
@@ -0,0 +1,102 @@
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.matchers.util;
10
11import java.util.Iterator;
12import java.util.Map;
13import java.util.Map.Entry;
14import java.util.Set;
15import java.util.function.BiConsumer;
16
17/**
18 * Wraps a Map<T, Integer> (mapping elements to non-zero multiplicities) into an {@link IMemoryView}.
19 *
20 * @author Gabor Bergmann
21 * @since 2.0
22 */
23public class MapBackedMemoryView<T> implements IMemoryView<T> {
24
25 private Map<T, Integer> wrapped;
26
27 /**
28 * @param wrapped an equivalent map from contained objects to multiplicities
29 */
30 protected MapBackedMemoryView(Map<T, Integer> wrapped) {
31 super();
32 this.wrapped = wrapped;
33 }
34
35 @Override
36 public Iterator<T> iterator() {
37 return wrapped.keySet().iterator();
38 }
39
40 @Override
41 public int getCount(T value) {
42 return getCountUnsafe(value);
43 }
44
45 @Override
46 public int getCountUnsafe(Object value) {
47 Integer count = wrapped.get(value);
48 return count == null ? 0 : count;
49 }
50
51 @Override
52 public boolean containsNonZero(T value) {
53 return wrapped.containsKey(value);
54 }
55
56 @Override
57 public boolean containsNonZeroUnsafe(Object value) {
58 return wrapped.containsKey(value);
59 }
60
61 @Override
62 public int size() {
63 return wrapped.size();
64 }
65
66 @Override
67 public boolean isEmpty() {
68 return wrapped.isEmpty();
69 }
70
71 @Override
72 public Set<T> distinctValues() {
73 return wrapped.keySet();
74 }
75
76
77 @Override
78 public void forEachEntryWithMultiplicities(BiConsumer<T, Integer> entryConsumer) {
79 for (Entry<T, Integer> entry : wrapped.entrySet()) {
80 entryConsumer.accept(entry.getKey(), entry.getValue());
81 }
82 }
83
84 @Override
85 public Iterable<Entry<T, Integer>> entriesWithMultiplicities() {
86 return wrapped.entrySet();
87 }
88
89 @Override
90 public int hashCode() {
91 return IMemoryView.hashCode(this);
92 }
93 @Override
94 public boolean equals(Object obj) {
95 return IMemoryView.equals(this, obj);
96 }
97
98 @Override
99 public String toString() {
100 return wrapped.toString();
101 }
102}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java
new file mode 100644
index 00000000..d22dcbe7
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java
@@ -0,0 +1,21 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.util;
10
11/**
12 * Internal marker type, must only be instantiated inside implementors of IMultiLookupImpl
13 * @noimplement This interface is not intended to be implemented by clients.
14 * @since 2.0
15 */
16public interface MarkedMemory<Value> extends IMemory<Value> {
17
18 static interface MarkedSet<Value> extends MarkedMemory<Value> {}
19 static interface MarkedMultiset<Value> extends MarkedMemory<Value> {}
20 static interface MarkedDeltaBag<Value> extends MarkedMemory<Value> {}
21} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java
new file mode 100644
index 00000000..49711a89
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java
@@ -0,0 +1,117 @@
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.matchers.util;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.HashSet;
14import java.util.Map;
15import java.util.Set;
16
17/**
18 * A partial and read-only Map implementation, mapping elements to multiplicities backed by an {@link IMemoryView}.
19 *
20 * <p> Not implemented: write methods.
21 *
22 * <p> Inefficiently implemented: {@link #containsValue(Object)}, {@link #values()}, {@link #entrySet()}.
23 *
24 * @author Gabor Bergmann
25 * @since 2.0
26 */
27public class MemoryViewBackedMapView<T> implements Map<T, Integer> {
28
29 private static final String READ_ONLY = "Read only";
30 private final IMemoryView<T> wrapped;
31
32 /**
33 * @param wrapped a memory view whose contents are to be exposed as an element-to-integer map.
34 */
35 protected MemoryViewBackedMapView(IMemoryView<T> wrapped) {
36 super();
37 this.wrapped = wrapped;
38 }
39
40 @Override
41 public int size() {
42 return wrapped.size();
43 }
44
45 @Override
46 public boolean isEmpty() {
47 return wrapped.isEmpty();
48 }
49
50 @Override
51 public boolean containsKey(Object key) {
52 return wrapped.containsNonZeroUnsafe(key);
53 }
54
55 @Override
56 public boolean containsValue(Object value) {
57 if (value instanceof Integer) {
58 for (Entry<T, Integer> entry : wrapped.entriesWithMultiplicities()) {
59 if (entry.getValue().equals(value)) return true;
60 }
61 }
62 return false;
63 }
64
65 @Override
66 public Integer put(T key, Integer value) {
67 throw new UnsupportedOperationException(READ_ONLY);
68 }
69
70 @Override
71 public Integer get(Object key) {
72 int count = wrapped.getCountUnsafe(key);
73 if (count == 0) return null; else return count;
74 }
75
76 @Override
77 public Integer remove(Object key) {
78 throw new UnsupportedOperationException(READ_ONLY);
79 }
80
81 @Override
82 public void putAll(Map<? extends T, ? extends Integer> m) {
83 throw new UnsupportedOperationException(READ_ONLY);
84 }
85
86 @Override
87 public void clear() {
88 throw new UnsupportedOperationException(READ_ONLY);
89 }
90
91 @Override
92 public Set<T> keySet() {
93 return wrapped.distinctValues();
94 }
95
96 @Override
97 public Collection<Integer> values() {
98 Collection<Integer> result = new ArrayList<>();
99 wrapped.forEachEntryWithMultiplicities((value, count) -> result.add(count));
100 return result;
101 }
102
103 @Override
104 public Set<Entry<T, Integer>> entrySet() {
105 Set<Entry<T, Integer>> result = new HashSet<>();
106 for (Entry<T, Integer> entry : wrapped.entriesWithMultiplicities()) {
107 result.add(entry);
108 }
109 return result;
110 }
111
112
113 @Override
114 public String toString() {
115 return wrapped.toString();
116 }
117}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java
new file mode 100644
index 00000000..e9e5e3a0
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java
@@ -0,0 +1,208 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.matchers.util;
10
11import java.util.function.Supplier;
12
13/**
14 * This class was motivated by the similar Preconditions class from Guava to provide simple precondition checking
15 * functionality. However, as starting with version 2.0 the runtime of VIATRA Query should not depend on Guava, the
16 * relevant functionality of the Preconditions checking functionality will be implemented here.
17 *
18 * @author Zoltan Ujhelyi
19 * @since 2.0
20 *
21 */
22public final class Preconditions {
23
24 private Preconditions() {
25 /* Utility class constructor */ }
26
27 /**
28 * Ensures the truth of an expression involving one or more parameters to the calling method.
29 *
30 * @param expression
31 * a boolean expression
32 * @throws IllegalArgumentException
33 * if {@code expression} is false
34 */
35 public static void checkArgument(boolean expression) {
36 if (!expression) {
37 throw new IllegalArgumentException();
38 }
39 }
40
41 /**
42 * Ensures the truth of an expression involving one or more parameters to the calling method.
43 *
44 * @param expression
45 * a boolean expression
46 * @param errorMessage
47 * the exception message to use if the check fails
48 * @throws IllegalArgumentException
49 * if {@code expression} is false
50 */
51 public static void checkArgument(boolean expression, String errorMessage) {
52 if (!expression) {
53 throw new IllegalArgumentException(errorMessage);
54 }
55 }
56
57 /**
58 * Ensures the truth of an expression involving one or more parameters to the calling method.
59 *
60 * @param expression
61 * a boolean expression
62 * @param errorMessageTemplate
63 * a template for the exception message should the check fail using the Java Formatter syntax; the same
64 * as used by {@link String#format(String, Object...)}.
65 * @param errorMessageArgs
66 * the arguments to be substituted into the message template.
67 * @throws IllegalArgumentException
68 * if {@code expression} is false
69 * @throws NullPointerException
70 * if the check fails and either {@code errorMessageTemplate} or {@code errorMessageArgs} is null (don't
71 * let this happen)
72 */
73 public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
74 if (!expression) {
75 throw new IllegalArgumentException(String.format(errorMessageTemplate, errorMessageArgs));
76 }
77 }
78
79 /**
80 * Ensures the truth of an expression involving one or more parameters to the calling method.
81 *
82 * @param expression
83 * a boolean expression
84 * @param messageSupplier a supplier that is called to calculate the error message if necessary
85 * @throws IllegalArgumentException
86 * if {@code expression} is false
87 */
88 public static void checkArgument(boolean expression, Supplier<String> messageSupplier) {
89 if (!expression) {
90 throw new IllegalArgumentException(messageSupplier.get());
91 }
92 }
93
94 /**
95 * Ensures the truth of an expression involving one or more fields of a class.
96 *
97 * @param expression
98 * a boolean expression
99 * @throws IllegalStateException
100 * if {@code expression} is false
101 */
102 public static void checkState(boolean expression) {
103 if (!expression) {
104 throw new IllegalStateException();
105 }
106 }
107
108 /**
109 * Ensures the truth of an expression involving one or more fields of a class.
110 *
111 * @param expression
112 * a boolean expression
113 * @param errorMessage
114 * the exception message to use if the check fails
115 * @throws IllegalStateException
116 * if {@code expression} is false
117 */
118 public static void checkState(boolean expression, String errorMessage) {
119 if (!expression) {
120 throw new IllegalStateException(errorMessage);
121 }
122 }
123
124 /**
125 * Ensures the truth of an expression involving one or more fields of a class.
126 *
127 * @param expression
128 * a boolean expression
129 * @param errorMessageTemplate
130 * a template for the exception message should the check fail using the Java Formatter syntax; the same
131 * as used by {@link String#format(String, Object...)}.
132 * @param errorMessageArgs
133 * the arguments to be substituted into the message template.
134 * @throws IllegalStateException
135 * if {@code expression} is false
136 * @throws NullPointerException
137 * if the check fails and either {@code errorMessageTemplate} or {@code errorMessageArgs} is null (don't
138 * let this happen)
139 */
140 public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
141 if (!expression) {
142 throw new IllegalStateException(String.format(errorMessageTemplate, errorMessageArgs));
143 }
144 }
145
146 /**
147 * Ensures the truth of an expression involving one or more fields of a class.
148 *
149 * @param expression
150 * a boolean expression
151 * @param messageSupplier a supplier that is called to calculate the error message if necessary
152 * @throws IllegalStateException
153 * if {@code expression} is false
154 */
155 public static void checkState(boolean expression, Supplier<String> messageSupplier) {
156 if (!expression) {
157 throw new IllegalStateException(messageSupplier.get());
158 }
159 }
160
161 /**
162 * Ensures that an index is appropriate for a list or array of given size.
163 *
164 * @param index
165 * @param size
166 * @throws IndexOutOfBoundsException
167 * if index is negative or is greater or equal to size
168 */
169 public static void checkElementIndex(int index, int size) {
170 if (index < 0 || index >= size) {
171 throw new IndexOutOfBoundsException();
172 }
173 }
174
175 /**
176 * Ensures that an index is appropriate for a list or array of given size.
177 *
178 * @param index
179 * @param size
180 * @param errorMessageTemplate
181 * a template for the exception message should the check fail using the Java Formatter syntax; the same
182 * as used by {@link String#format(String, Object...)}.
183 * @param errorMessageArgs
184 * the arguments to be substituted into the message template.
185 * @throws IndexOutOfBoundsException
186 * if index is negative or is greater or equal to size
187 */
188 public static void checkElementIndex(int index, int size, String errorMessageTemplate, Object... errorMessageArgs) {
189 if (index < 0 || index >= size) {
190 throw new IndexOutOfBoundsException(String.format(errorMessageTemplate, errorMessageArgs));
191 }
192 }
193
194 /**
195 * Ensures that an index is appropriate for a list or array of given size.
196 *
197 * @param index
198 * @param size
199 * @param messageSupplier a supplier that is called to calculate the error message if necessary
200 * @throws IndexOutOfBoundsException
201 * if index is negative or is greater or equal to size
202 */
203 public static void checkElementIndex(int index, int size, Supplier<String> messageSupplier) {
204 if (index < 0 || index >= size) {
205 throw new IndexOutOfBoundsException(messageSupplier.get());
206 }
207 }
208}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java
new file mode 100644
index 00000000..c4e6b5af
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java
@@ -0,0 +1,44 @@
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.matchers.util;
10
11import java.util.HashMap;
12import java.util.Map;
13import java.util.function.Supplier;
14
15/**
16 * @author Zoltan Ujhelyi
17 * @since 1.7
18 * @noreference This class is not intended to be referenced by clients.
19 */
20public class PurgableCache implements ICache {
21
22 Map<Object, Object> storage = new HashMap<>();
23
24 @Override
25 @SuppressWarnings("unchecked")
26 public <T> T getValue(Object key, Class<? extends T> clazz, Supplier<T> valueProvider) {
27 if (storage.containsKey(key)) {
28 Object value = storage.get(key);
29 Preconditions.checkState(clazz.isInstance(value), "Cache stores for key %s a value of %s that is incompatible with the requested type %s", key, value, clazz);
30 return (T) value;
31 } else {
32 T value = valueProvider.get();
33 storage.put(key, value);
34 return value;
35 }
36 }
37
38 /**
39 * Removes all values stored in the cache
40 */
41 public void purge() {
42 storage.clear();
43 }
44}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java
new file mode 100644
index 00000000..3749fe06
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java
@@ -0,0 +1,90 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, 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 * Contributors:
10 * Gabor Bergmann - initial API and implementation
11 *******************************************************************************/
12package tools.refinery.viatra.runtime.matchers.util;
13
14import java.util.ArrayList;
15import java.util.List;
16import java.util.Set;
17import java.util.stream.Collectors;
18import java.util.stream.Stream;
19
20/**
21 * This class was motivated by the similar Sets class from Guava to provide simple set manipulation
22 * functionality. However, as starting with version 2.3 the runtime of VIATRA Query should not depend on Guava,
23 * not even internally, the relevant subset of Sets methods will be reimplemented here.
24 *
25 * <p> The current approach is to delegate to Eclipse Collections wherever possible.
26 * Such glue methods are useful so that downstream clients can avoid directly depending on Eclipse Collections.
27 *
28 * <p> Without an equivalent from Eclipse Collections, {@link #cartesianProduct(List)} is implemented here from scratch.
29 *
30 * @author Gabor Bergmann
31 * @since 2.3
32 */
33public final class Sets {
34
35 /**
36 * @since 2.4
37 */
38 public static <A> Set<A> newSet(Iterable<A> elements) {
39 return org.eclipse.collections.impl.factory.Sets.mutable.ofAll(elements);
40 }
41
42 public static <A> Set<A> intersection(Set<A> left, Set<A> right) {
43 return org.eclipse.collections.impl.factory.Sets.intersect(left, right);
44 }
45
46 public static <A> Set<A> difference(Set<A> left, Set<A> right) {
47 return org.eclipse.collections.impl.factory.Sets.difference(left, right);
48 }
49
50 public static <A> Set<A> union(Set<A> left, Set<A> right) {
51 return org.eclipse.collections.impl.factory.Sets.union(left, right);
52 }
53
54 public static <A> Set<? extends Set<A>> powerSet(Set<A> set) {
55 return org.eclipse.collections.impl.factory.Sets.powerSet(set);
56 }
57
58 public static <A> Set<List<A>> cartesianProduct(List<? extends Set<? extends A>> setsList) {
59
60 class Suffix { // simple immutable linked list
61 private A head;
62 private Suffix next;
63
64 public Suffix(A head, Suffix next) {
65 super();
66 this.head = head;
67 this.next = next;
68 }
69
70 public List<A> toList() {
71 ArrayList<A> result = new ArrayList<>();
72 for (Suffix cursor = this; cursor!=null; cursor = cursor.next)
73 result.add(cursor.head);
74 return result;
75 }
76 }
77
78 // build result lists from end to start, in the form of suffixes
79 Stream<Suffix> suffixes = Stream.of((Suffix) null /* empty suffix*/);
80 for (int i = setsList.size()-1; i>=0; --i) { // iterate sets in reverse order
81 Set<? extends A> set = setsList.get(i);
82 suffixes = suffixes.flatMap(suffix -> set.stream().map(newElement -> new Suffix(newElement, suffix)));
83 }
84
85
86 return suffixes.map(Suffix::toList).collect(Collectors.toSet());
87 }
88
89
90}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java
new file mode 100644
index 00000000..8f8bc228
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java
@@ -0,0 +1,60 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.matchers.util;
10
11import java.util.Objects;
12
13/**
14 * A piece of data associated with a direction.
15 *
16 * @author Tamas Szabo
17 * @since 2.4
18 */
19public class Signed<Payload extends Comparable<Payload>> {
20
21 private final Payload payload;
22 private final Direction direction;
23
24 public Signed(final Direction direction, final Payload payload) {
25 this.payload = payload;
26 this.direction = direction;
27 }
28
29 public Payload getPayload() {
30 return payload;
31 }
32
33 public Direction getDirection() {
34 return direction;
35 }
36
37 @Override
38 public int hashCode() {
39 return Objects.hash(direction, payload);
40 }
41
42 @Override
43 public boolean equals(final Object obj) {
44 if (this == obj) {
45 return true;
46 } else if (obj == null || this.getClass() != obj.getClass()) {
47 return false;
48 } else {
49 @SuppressWarnings("rawtypes")
50 final Signed other = (Signed) obj;
51 return direction == other.direction && Objects.equals(payload, other.payload);
52 }
53 }
54
55 @Override
56 public String toString() {
57 return this.direction.asSign() + this.payload.toString();
58 }
59
60} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java
new file mode 100644
index 00000000..cc5963f7
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java
@@ -0,0 +1,29 @@
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.matchers.util;
10
11/**
12 * A provider implementation that always returns the same object instance.
13 * @author Zoltan Ujhelyi
14 */
15public class SingletonInstanceProvider<T> implements IProvider<T>{
16
17 private T instance;
18
19 public SingletonInstanceProvider(T instance) {
20 Preconditions.checkArgument(instance != null, "Instance parameter must not be null.");
21 this.instance = instance;
22 }
23
24 @Override
25 public T get() {
26 return instance;
27 }
28
29} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java
new file mode 100644
index 00000000..b303f9ad
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java
@@ -0,0 +1,105 @@
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.matchers.util;
10
11import java.util.Collections;
12import java.util.Iterator;
13import java.util.NoSuchElementException;
14import java.util.Set;
15
16/**
17 * An immutable memory view that consists of a single non-null element with multiplicity 1.
18 * @author Gabor Bergmann
19 * @since 2.0
20 */
21public final class SingletonMemoryView<Value> implements IMemoryView<Value> {
22
23 private Value wrapped;
24 private static final int ONE_HASH = Integer.valueOf(1).hashCode();
25
26 public SingletonMemoryView(Value value) {
27 this.wrapped = value;
28 }
29
30 @Override
31 public Iterator<Value> iterator() {
32 return new Iterator<Value>() {
33 boolean hasNext = true;
34
35 @Override
36 public boolean hasNext() {
37 return hasNext;
38 }
39
40 @Override
41 public Value next() {
42 if (hasNext) {
43 hasNext = false;
44 return wrapped;
45 } else throw new NoSuchElementException();
46 }
47 };
48 }
49
50 @Override
51 public int getCount(Value value) {
52 return wrapped.equals(value) ? 1 : 0;
53 }
54
55 @Override
56 public int getCountUnsafe(Object value) {
57 return wrapped.equals(value) ? 1 : 0;
58 }
59
60 @Override
61 public boolean containsNonZero(Value value) {
62 return wrapped.equals(value);
63 }
64
65 @Override
66 public boolean containsNonZeroUnsafe(Object value) {
67 return wrapped.equals(value);
68 }
69
70 @Override
71 public int size() {
72 return 1;
73 }
74
75 @Override
76 public boolean isEmpty() {
77 return false;
78 }
79
80 @Override
81 public Set<Value> distinctValues() {
82 return Collections.singleton(wrapped);
83 }
84
85 @Override
86 public boolean equals(Object obj) {
87 if (obj instanceof IMemoryView<?>) {
88 IMemoryView<?> other = (IMemoryView<?>) obj;
89 if (1 != other.size()) return false;
90 if (1 != other.getCountUnsafe(wrapped)) return false;
91 return true;
92 }
93 return false;
94 }
95
96 @Override
97 public int hashCode() {
98 return wrapped.hashCode() ^ ONE_HASH;
99 }
100
101 @Override
102 public String toString() {
103 return "{" + wrapped + "}";
104 }
105}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java
new file mode 100644
index 00000000..90fcad4d
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java
@@ -0,0 +1,517 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.matchers.util;
10
11import java.util.Collections;
12import java.util.Map;
13import java.util.Map.Entry;
14import java.util.NavigableMap;
15import java.util.Set;
16import java.util.TreeMap;
17
18import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
20import tools.refinery.viatra.runtime.matchers.util.resumable.Resumable;
21import tools.refinery.viatra.runtime.matchers.util.resumable.UnmaskedResumable;
22import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
23import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines;
25
26/**
27 * A timely memory implementation that incrementally maintains the {@link Timeline}s of tuples. The memory is capable of
28 * lazy folding (see {@link Resumable}).
29 *
30 * @author Tamas Szabo
31 * @since 2.3
32 */
33public class TimelyMemory<Timestamp extends Comparable<Timestamp>> implements Clearable, UnmaskedResumable<Timestamp> {
34
35 protected final Map<Tuple, TreeMap<Timestamp, CumulativeCounter>> counters;
36 protected final Map<Tuple, Timeline<Timestamp>> timelines;
37 public final TreeMap<Timestamp, Map<Tuple, FoldingState>> foldingState;
38 protected final Set<Tuple> presentAtInfinity;
39 protected final boolean isLazy;
40 protected final Diff<Timestamp> EMPTY_DIFF = new Diff<Timestamp>();
41
42 public TimelyMemory() {
43 this(false);
44 }
45
46 public TimelyMemory(final boolean isLazy) {
47 this.counters = CollectionsFactory.createMap();
48 this.timelines = CollectionsFactory.createMap();
49 this.presentAtInfinity = CollectionsFactory.createSet();
50 this.isLazy = isLazy;
51 if (isLazy) {
52 this.foldingState = CollectionsFactory.createTreeMap();
53 } else {
54 this.foldingState = null;
55 }
56 }
57
58 @Override
59 public Set<Tuple> getResumableTuples() {
60 if (this.foldingState == null || this.foldingState.isEmpty()) {
61 return Collections.emptySet();
62 } else {
63 return this.foldingState.firstEntry().getValue().keySet();
64 }
65 }
66
67 @Override
68 public Timestamp getResumableTimestamp() {
69 if (this.foldingState == null || this.foldingState.isEmpty()) {
70 return null;
71 } else {
72 return this.foldingState.firstKey();
73 }
74 }
75
76 /**
77 * Registers the given folding state for the specified timestamp and tuple. If there is already a state stored, the
78 * two states will be merged together.
79 */
80 protected void addFoldingState(final Tuple tuple, final FoldingState state, final Timestamp timestamp) {
81 assert state.diff != 0;
82 final Map<Tuple, FoldingState> tupleMap = this.foldingState.computeIfAbsent(timestamp,
83 k -> CollectionsFactory.createMap());
84 tupleMap.compute(tuple, (k, v) -> {
85 return v == null ? state : v.merge(state);
86 });
87 }
88
89 @Override
90 public Map<Tuple, Diff<Timestamp>> resumeAt(final Timestamp timestamp) {
91 Timestamp current = this.getResumableTimestamp();
92 if (current == null) {
93 throw new IllegalStateException("There is othing to fold!");
94 } else if (current.compareTo(timestamp) != 0) {
95 // It can happen that already registered folding states end up having zero diffs,
96 // and we are instructed to continue folding at a timestamp that is higher
97 // than the lowest timestamp with a folding state.
98 // However, we only do garbage collection in doFoldingState, so now it is time to
99 // first clean up those states with zero diffs.
100 while (current != null && current.compareTo(timestamp) < 0) {
101 final Map<Tuple, FoldingState> tupleMap = this.foldingState.remove(current);
102 for (final Entry<Tuple, FoldingState> entry : tupleMap.entrySet()) {
103 final Tuple key = entry.getKey();
104 final FoldingState value = entry.getValue();
105 if (value.diff != 0) {
106 throw new IllegalStateException("Expected zero diff during garbage collection at " + current
107 + ", but the diff was " + value.diff + "!");
108 }
109 doFoldingStep(key, value, current);
110 }
111 current = this.getResumableTimestamp();
112 }
113 if (current == null || current.compareTo(timestamp) != 0) {
114 throw new IllegalStateException("Expected to continue folding at " + timestamp + "!");
115 }
116 }
117
118 final Map<Tuple, Diff<Timestamp>> diffMap = CollectionsFactory.createMap();
119 final Map<Tuple, FoldingState> tupleMap = this.foldingState.remove(timestamp);
120 for (final Entry<Tuple, FoldingState> entry : tupleMap.entrySet()) {
121 final Tuple key = entry.getKey();
122 final FoldingState value = entry.getValue();
123 diffMap.put(key, doFoldingStep(key, value, timestamp));
124 }
125
126 if (this.foldingState.get(timestamp) != null) {
127 throw new IllegalStateException(
128 "Folding at " + timestamp + " produced more folding work at the same timestamp!");
129 }
130
131 return diffMap;
132 }
133
134 protected Diff<Timestamp> doFoldingStep(final Tuple tuple, final FoldingState state, final Timestamp timestamp) {
135 final CumulativeCounter counter = getCounter(tuple, timestamp);
136 if (state.diff == 0) {
137 gcCounters(counter, tuple, timestamp);
138 return EMPTY_DIFF;
139 } else {
140 final Diff<Timestamp> resultDiff = new Diff<>();
141 final Timestamp nextTimestamp = this.counters.get(tuple).higherKey(timestamp);
142
143 final int oldCumulative = counter.cumulative;
144
145 counter.cumulative += state.diff;
146
147 computeDiffsLazy(state.diff < 0 ? Direction.DELETE : Direction.INSERT, oldCumulative, counter.cumulative,
148 timestamp, nextTimestamp, resultDiff);
149
150 gcCounters(counter, tuple, timestamp);
151 updateTimeline(tuple, resultDiff);
152
153 // prepare folding state for next timestamp
154 if (nextTimestamp != null) {
155 // propagate the incoming diff, not the diff stored in counter
156 addFoldingState(tuple, new FoldingState(state.diff), nextTimestamp);
157 }
158
159 return resultDiff;
160 }
161 }
162
163 /**
164 * On-demand initializes and returns the counter for the given tuple and timestamp.
165 */
166 protected CumulativeCounter getCounter(final Tuple tuple, final Timestamp timestamp) {
167 final TreeMap<Timestamp, CumulativeCounter> counterTimeline = this.counters.computeIfAbsent(tuple,
168 k -> CollectionsFactory.createTreeMap());
169
170 final CumulativeCounter counter = counterTimeline.computeIfAbsent(timestamp, k -> {
171 final Entry<Timestamp, CumulativeCounter> previousCounter = counterTimeline.lowerEntry(k);
172 final int previousCumulative = previousCounter == null ? 0 : previousCounter.getValue().cumulative;
173 return new CumulativeCounter(0, previousCumulative);
174 });
175
176 return counter;
177 }
178
179 /**
180 * Garbage collects the counter of the given tuple and timestamp if the new diff is zero.
181 */
182 protected void gcCounters(final CumulativeCounter counter, final Tuple tuple, final Timestamp timestamp) {
183 if (counter.diff == 0) {
184 final TreeMap<Timestamp, CumulativeCounter> counterMap = this.counters.get(tuple);
185 counterMap.remove(timestamp);
186 if (counterMap.isEmpty()) {
187 this.counters.remove(tuple);
188 }
189 }
190 }
191
192 /**
193 * Utility method that computes the timeline diffs in case of lazy memories. The diffs will be inserted into the
194 * input parameter. This method computes diffs for entire plateaus that spans from timestamp to nextTimestamp.
195 *
196 * Compared to the eager version of this method, the lazy version makes use of both the old and the new cumulative
197 * values because it can happen that the cumulative is incremented by a value that is larger than 1 (as folding
198 * states are merged together). This means that we cant decide whether the cumulative became positive by comparing
199 * the new value to 1.
200 */
201 protected void computeDiffsLazy(final Direction direction, final int oldCumulative, final int newCumulative,
202 final Timestamp timestamp, final Timestamp nextTimestamp, final Diff<Timestamp> diffs) {
203 if (direction == Direction.INSERT) {
204 if (newCumulative == 0) {
205 throw new IllegalStateException("Cumulative count can never be negative!");
206 } else {
207 if (oldCumulative == 0 /* current became positive */) {
208 // (1) either we sent out a DELETE before and now we need to cancel it,
209 // (2) or we just INSERT this for the first time
210 diffs.add(new Signed<>(Direction.INSERT, timestamp));
211 if (nextTimestamp != null) {
212 diffs.add(new Signed<>(Direction.DELETE, nextTimestamp));
213 }
214 } else /* current stays positive */ {
215 // nothing to do
216 }
217 }
218 } else {
219 if (newCumulative < 0) {
220 throw new IllegalStateException("Cumulative count can never be negative!");
221 } else {
222 if (newCumulative == 0 /* current became zero */) {
223 diffs.add(new Signed<>(Direction.DELETE, timestamp));
224 if (nextTimestamp != null) {
225 diffs.add(new Signed<>(Direction.INSERT, nextTimestamp));
226 }
227 } else /* current stays positive */ {
228 // nothing to do
229 }
230 }
231 }
232 }
233
234 /**
235 * Utility method that computes the timeline diffs in case of eager memories. The diffs will be inserted into the
236 * input parameter. This method computes diffs that describe momentary changes instead of plateaus. Returns a
237 * {@link SignChange} that describes how the sign has changed at the given timestamp.
238 */
239 protected SignChange computeDiffsEager(final Direction direction, final CumulativeCounter counter,
240 final SignChange signChangeAtPrevious, final Timestamp timestamp, final Diff<Timestamp> diffs) {
241 if (direction == Direction.INSERT) {
242 if (counter.cumulative == 0) {
243 throw new IllegalStateException("Cumulative count can never be negative!");
244 } else {
245 if (counter.cumulative == 1 /* current became positive */) {
246 if (signChangeAtPrevious != SignChange.BECAME_POSITIVE) {
247 // (1) either we sent out a DELETE before and now we need to cancel it,
248 // (2) or we just INSERT this for the first time
249 diffs.add(new Signed<>(Direction.INSERT, timestamp));
250 } else {
251 // we have already emitted this at the previous timestamp
252 // both previous and current became positive
253 throw new IllegalStateException(
254 "This would mean that the diff at current is 0 " + counter.diff);
255 }
256
257 // remember for next timestamp
258 return SignChange.BECAME_POSITIVE;
259 } else /* current stays positive */ {
260 if (signChangeAtPrevious == SignChange.BECAME_POSITIVE) {
261 // we sent out an INSERT before and now the timeline is positive already starting at previous
262 // we need to cancel the effect of this with a DELETE
263 diffs.add(new Signed<>(Direction.DELETE, timestamp));
264 } else {
265 // this is normal, both previous and current was positive and stays positive
266 }
267
268 // remember for next timestamp
269 return SignChange.IRRELEVANT;
270 }
271 }
272 } else {
273 if (counter.cumulative < 0) {
274 throw new IllegalStateException("Cumulative count can never be negative!");
275 } else {
276 if (counter.cumulative == 0 /* current became zero */) {
277 if (signChangeAtPrevious != SignChange.BECAME_ZERO) {
278 // (1) either we sent out a INSERT before and now we need to cancel it,
279 // (2) or we just DELETE this for the first time
280 diffs.add(new Signed<>(Direction.DELETE, timestamp));
281 } else {
282 // we have already emitted this at the previous timestamp
283 // both previous and current became zero
284 throw new IllegalStateException(
285 "This would mean that the diff at current is 0 " + counter.diff);
286 }
287
288 // remember for next timestamp
289 return SignChange.BECAME_ZERO;
290 } else /* current stays positive */ {
291 if (signChangeAtPrevious == SignChange.BECAME_ZERO) {
292 // we sent out a DELETE before and now the timeline is zero already starting at previous
293 // we need to cancel the effect of this with a INSERT
294 diffs.add(new Signed<>(Direction.INSERT, timestamp));
295 } else {
296 // this is normal, both previous and current was positive and stays positive
297 }
298
299 // remember for next timestamp
300 return SignChange.IRRELEVANT;
301 }
302 }
303 }
304 }
305
306 public Diff<Timestamp> put(final Tuple tuple, final Timestamp timestamp) {
307 if (this.isLazy) {
308 return putLazy(tuple, timestamp);
309 } else {
310 return putEager(tuple, timestamp);
311 }
312 }
313
314 public Diff<Timestamp> remove(final Tuple tuple, final Timestamp timestamp) {
315 if (this.isLazy) {
316 return removeLazy(tuple, timestamp);
317 } else {
318 return removeEager(tuple, timestamp);
319 }
320 }
321
322 protected Diff<Timestamp> putEager(final Tuple tuple, final Timestamp timestamp) {
323 final Diff<Timestamp> resultDiff = new Diff<>();
324 final CumulativeCounter counter = getCounter(tuple, timestamp);
325 ++counter.diff;
326
327 // before the INSERT timestamp, no change at all
328 // it cannot happen that those became positive in this round
329 SignChange signChangeAtPrevious = SignChange.IRRELEVANT;
330
331 final NavigableMap<Timestamp, CumulativeCounter> nextCounters = this.counters.get(tuple).tailMap(timestamp,
332 true);
333 for (final Entry<Timestamp, CumulativeCounter> currentEntry : nextCounters.entrySet()) {
334 final Timestamp currentTimestamp = currentEntry.getKey();
335 final CumulativeCounter currentCounter = currentEntry.getValue();
336 ++currentCounter.cumulative;
337 signChangeAtPrevious = computeDiffsEager(Direction.INSERT, currentCounter, signChangeAtPrevious,
338 currentTimestamp, resultDiff);
339 }
340
341 gcCounters(counter, tuple, timestamp);
342 updateTimeline(tuple, resultDiff);
343
344 return resultDiff;
345 }
346
347 protected Diff<Timestamp> putLazy(final Tuple tuple, final Timestamp timestamp) {
348 final CumulativeCounter counter = getCounter(tuple, timestamp);
349 counter.diff += 1;
350 // before the INSERT timestamp, no change at all
351 // it cannot happen that those became positive in this round
352 addFoldingState(tuple, new FoldingState(+1), timestamp);
353 return EMPTY_DIFF;
354 }
355
356 protected Diff<Timestamp> removeEager(final Tuple tuple, final Timestamp timestamp) {
357 final Diff<Timestamp> resultDiff = new Diff<>();
358 final CumulativeCounter counter = getCounter(tuple, timestamp);
359 --counter.diff;
360
361 // before the DELETE timestamp, no change at all
362 // it cannot happen that those became zero in this round
363 SignChange signChangeAtPrevious = SignChange.IRRELEVANT;
364
365 final NavigableMap<Timestamp, CumulativeCounter> nextCounters = this.counters.get(tuple).tailMap(timestamp,
366 true);
367 for (final Entry<Timestamp, CumulativeCounter> currentEntry : nextCounters.entrySet()) {
368 final Timestamp currentTimestamp = currentEntry.getKey();
369 final CumulativeCounter currentCounter = currentEntry.getValue();
370 --currentCounter.cumulative;
371 signChangeAtPrevious = computeDiffsEager(Direction.DELETE, currentCounter, signChangeAtPrevious,
372 currentTimestamp, resultDiff);
373 }
374
375 gcCounters(counter, tuple, timestamp);
376 updateTimeline(tuple, resultDiff);
377
378 return resultDiff;
379 }
380
381 protected Diff<Timestamp> removeLazy(final Tuple tuple, final Timestamp timestamp) {
382 final CumulativeCounter counter = getCounter(tuple, timestamp);
383 counter.diff -= 1;
384 // before the DELETE timestamp, no change at all
385 // it cannot happen that those became zero in this round
386 addFoldingState(tuple, new FoldingState(-1), timestamp);
387 return EMPTY_DIFF;
388 }
389
390 /**
391 * Updates and garbage collects the timeline of the given tuple based on the given timeline diff.
392 */
393 protected void updateTimeline(final Tuple tuple, final Diff<Timestamp> diff) {
394 if (!diff.isEmpty()) {
395 this.timelines.compute(tuple, (k, oldTimeline) -> {
396 this.presentAtInfinity.remove(tuple);
397 final Timeline<Timestamp> timeline = oldTimeline == null ? Timelines.createFrom(diff)
398 : oldTimeline.mergeAdditive(diff);
399 if (timeline.isPresentAtInfinity()) {
400 this.presentAtInfinity.add(tuple);
401 }
402 if (timeline.isEmpty()) {
403 return null;
404 } else {
405 return timeline;
406 }
407 });
408 }
409 }
410
411 /**
412 * @since 2.8
413 */
414 public Set<Tuple> getTuplesAtInfinity() {
415 return this.presentAtInfinity;
416 }
417
418 /**
419 * Returns the number of tuples that are present at the moment 'infinity'.
420 */
421 public int getCountAtInfinity() {
422 return this.presentAtInfinity.size();
423 }
424
425 /**
426 * Returns true if the given tuple is present at the moment 'infinity'.
427 */
428 public boolean isPresentAtInfinity(final Tuple tuple) {
429 final Timeline<Timestamp> timeline = this.timelines.get(tuple);
430 if (timeline == null) {
431 return false;
432 } else {
433 return timeline.isPresentAtInfinity();
434 }
435 }
436
437 public boolean isEmpty() {
438 return this.counters.isEmpty();
439 }
440
441 public int size() {
442 return this.counters.size();
443 }
444
445 public Set<Tuple> keySet() {
446 return this.counters.keySet();
447 }
448
449 public Map<Tuple, Timeline<Timestamp>> asMap() {
450 return this.timelines;
451 }
452
453 public Timeline<Timestamp> get(final ITuple tuple) {
454 return this.timelines.get(tuple);
455 }
456
457 @Override
458 public void clear() {
459 this.counters.clear();
460 this.timelines.clear();
461 if (this.foldingState != null) {
462 this.foldingState.clear();
463 }
464 }
465
466 public boolean containsKey(final ITuple tuple) {
467 return this.counters.containsKey(tuple);
468 }
469
470 @Override
471 public String toString() {
472 return this.counters + "\n" + this.timelines + "\n" + this.foldingState + "\n";
473 }
474
475 protected static final class CumulativeCounter {
476 protected int diff;
477 protected int cumulative;
478
479 protected CumulativeCounter(final int diff, final int cumulative) {
480 this.diff = diff;
481 this.cumulative = cumulative;
482 }
483
484 @Override
485 public String toString() {
486 return "{diff=" + this.diff + ", cumulative=" + this.cumulative + "}";
487 }
488
489 }
490
491 protected static final class FoldingState {
492 protected final int diff;
493
494 protected FoldingState(final int diff) {
495 this.diff = diff;
496 }
497
498 @Override
499 public String toString() {
500 return "{diff=" + this.diff + "}";
501 }
502
503 /**
504 * The returned result will never be null, even if the resulting diff is zero.
505 */
506 public FoldingState merge(final FoldingState that) {
507 Preconditions.checkArgument(that != null);
508 return new FoldingState(this.diff + that.diff);
509 }
510
511 }
512
513 protected enum SignChange {
514 BECAME_POSITIVE, BECAME_ZERO, IRRELEVANT;
515 }
516
517}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java
new file mode 100644
index 00000000..ea70e61d
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java
@@ -0,0 +1,36 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.matchers.util.resumable;
10
11import java.util.Map;
12
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
15
16/**
17 * A masked {@link Resumable} implementation, which maintains lazy folding per tuple signature.
18 *
19 * @author Tamas Szabo
20 * @since 2.4
21 */
22public interface MaskedResumable<Timestamp extends Comparable<Timestamp>> extends Resumable<Timestamp> {
23
24 /**
25 * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to
26 * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more
27 * folding to do towards higher timestamps.
28 */
29 public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp);
30
31 /**
32 * Returns the set of signatures for which lazy folding shall be resumed at the next timestamp.
33 */
34 public Iterable<Tuple> getResumableSignatures();
35
36}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java
new file mode 100644
index 00000000..2861df20
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java
@@ -0,0 +1,27 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.matchers.util.resumable;
10
11/**
12 * A resumable lazily folds its state towards higher timestamps. Folding shall be done in the increasing order of
13 * timestamps, and it shall be interrupted after each step. The resumable can then be instructed to resume the folding,
14 * one step at a time.
15 *
16 * @author Tamas Szabo
17 * @since 2.4
18 */
19public interface Resumable<Timestamp extends Comparable<Timestamp>> {
20
21 /**
22 * Returns the smallest timestamp where lazy folding shall be resumed, or null if there is no more folding to do in this
23 * resumable.
24 */
25 public Timestamp getResumableTimestamp();
26
27}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java
new file mode 100644
index 00000000..1671940b
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java
@@ -0,0 +1,36 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.matchers.util.resumable;
10
11import java.util.Map;
12
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
15
16/**
17 * A unmasked {@link Resumable} implementation, which maintains lazy folding without caring about tuple signatures.
18 *
19 * @author Tamas Szabo
20 * @since 2.4
21 */
22public interface UnmaskedResumable<Timestamp extends Comparable<Timestamp>> extends Resumable<Timestamp> {
23
24 /**
25 * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to
26 * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more
27 * folding to do towards higher timestamps.
28 */
29 public Map<Tuple, Diff<Timestamp>> resumeAt(final Timestamp timestamp);
30
31 /**
32 * Returns the set of tuples for which lazy folding shall be resumed at the next timestamp.
33 */
34 public Iterable<Tuple> getResumableTuples();
35
36}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java
new file mode 100644
index 00000000..0532d094
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java
@@ -0,0 +1,111 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.matchers.util.timeline;
10
11import java.util.ArrayList;
12import java.util.Iterator;
13import java.util.List;
14
15import tools.refinery.viatra.runtime.matchers.util.Direction;
16import tools.refinery.viatra.runtime.matchers.util.Signed;
17
18/**
19 * A compact timeline may cosist of an arbitrary amount of moments.
20 * It is backed by an {@link ArrayList}.
21 *
22 * @author Tamas Szabo
23 * @since 2.4
24 */
25public class CompactTimeline<Timestamp extends Comparable<Timestamp>> extends Timeline<Timestamp> {
26
27 protected final List<Timestamp> elements;
28
29 CompactTimeline() {
30 this.elements = new ArrayList<>();
31 }
32
33 CompactTimeline(final Timestamp timestamp) {
34 this();
35 this.elements.add(timestamp);
36 }
37
38 CompactTimeline(final List<Timestamp> timestamps) {
39 this.elements = new ArrayList<>(timestamps.size());
40 this.elements.addAll(timestamps);
41 }
42
43 CompactTimeline(final Diff<Timestamp> diff) {
44 this.elements = new ArrayList<>(diff.size());
45 Direction expected = Direction.INSERT;
46 for (Signed<Timestamp> signed : diff) {
47 if (!expected.equals(signed.getDirection())) {
48 throw new IllegalStateException(String.format("Expected direction (%s) constraint violated! %s @%s",
49 expected, diff, signed.getPayload()));
50 }
51 this.elements.add(signed.getPayload());
52 expected = expected.opposite();
53 }
54 }
55
56 @Override
57 public Signed<Timestamp> getSigned(final int index) {
58 final Direction direction = index % 2 == 0 ? Direction.INSERT : Direction.DELETE;
59 return new Signed<>(direction, this.getUnsigned(index));
60 }
61
62 @Override
63 public Timestamp getUnsigned(final int index) {
64 if (this.elements.size() <= index) {
65 throw new IllegalArgumentException(
66 "Timeline size (" + this.size() + ") is smaller than requested index " + index + "!");
67 } else {
68 return this.elements.get(index);
69 }
70 }
71
72 @Override
73 public int size() {
74 return this.elements.size();
75 }
76
77 @Override
78 public boolean isPresentAtInfinity() {
79 // if it has an odd length, then it ends with "INSERT"
80 return this.size() % 2 == 1;
81 }
82
83 @Override
84 public Iterable<Signed<Timestamp>> asChangeSequence() {
85 Iterable<Timestamp> outer = this.elements;
86 return () -> {
87 final Iterator<Timestamp> itr = outer.iterator();
88 return new Iterator<Signed<Timestamp>>() {
89 Direction direction = Direction.INSERT;
90
91 @Override
92 public boolean hasNext() {
93 return itr.hasNext();
94 }
95
96 @Override
97 public Signed<Timestamp> next() {
98 final Signed<Timestamp> result = new Signed<Timestamp>(direction, itr.next());
99 direction = direction.opposite();
100 return result;
101 }
102 };
103 };
104 }
105
106 @Override
107 public boolean isEmpty() {
108 return this.elements.isEmpty();
109 }
110
111}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java
new file mode 100644
index 00000000..cec6049e
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java
@@ -0,0 +1,55 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.matchers.util.timeline;
10
11import java.util.ArrayList;
12
13import tools.refinery.viatra.runtime.matchers.util.Signed;
14
15/**
16 * The description of a delta that specifies how a {@link Timeline} changes. It consists of {@link Signed} timestamps that
17 * depict the moments of insertions and deletions on the timeline.
18 *
19 * @author Tamas Szabo
20 * @since 2.4
21 * @param <Timestamp>
22 * the type representing the timestamps
23 */
24public class Diff<Timestamp extends Comparable<Timestamp>> extends ArrayList<Signed<Timestamp>> {
25
26 private static final long serialVersionUID = 3853460426655994160L;
27
28 public Diff() {
29
30 }
31
32 public void appendWithCancellation(Signed<Timestamp> item) {
33 if (this.isEmpty()) {
34 this.add(item);
35 } else {
36 final Signed<Timestamp> last = this.get(this.size() - 1);
37 final int lastMinusItem = last.getPayload().compareTo(item.getPayload());
38 if (lastMinusItem == 0) {
39 if (last.getDirection() != item.getDirection()) {
40 // cancellation
41 this.remove(this.size() - 1);
42 } else {
43 throw new IllegalStateException(
44 "Trying to insert or delete for the second time at the same timestamp! " + item);
45 }
46 } else if (lastMinusItem > 0) {
47 throw new IllegalStateException(
48 "Trying to append a timestamp that is smaller than the last one! " + last + " " + item);
49 } else {
50 this.add(item);
51 }
52 }
53 }
54
55} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java
new file mode 100644
index 00000000..526a95f5
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java
@@ -0,0 +1,73 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.matchers.util.timeline;
10
11import java.util.Collections;
12
13import tools.refinery.viatra.runtime.matchers.util.Direction;
14import tools.refinery.viatra.runtime.matchers.util.Signed;
15
16/**
17 * A timeline which solely consists of one timestamp value, representing a single insertion. Intuitively, a singleton
18 * timeline always represents a bump which starts at the given timestamp and lasts till plus infinity.
19 *
20 * @author Tamas Szabo
21 * @since 2.4
22 */
23public class SingletonTimeline<Timestamp extends Comparable<Timestamp>> extends Timeline<Timestamp> {
24
25 protected final Timestamp start;
26
27 SingletonTimeline(final Timestamp timestamp) {
28 this.start = timestamp;
29 }
30
31 SingletonTimeline(final Diff<Timestamp> diff) {
32 if (diff.size() != 1 || diff.get(0).getDirection() == Direction.DELETE) {
33 throw new IllegalArgumentException("There is only a single (insert) timestamp in the singleton timestamp!");
34 } else {
35 this.start = diff.get(0).getPayload();
36 }
37 }
38
39 @Override
40 public Signed<Timestamp> getSigned(final int index) {
41 return new Signed<>(Direction.INSERT, this.getUnsigned(index));
42 }
43
44 @Override
45 public Timestamp getUnsigned(final int index) {
46 if (index != 0) {
47 throw new IllegalArgumentException("There is only a single (insert) timestamp in the singleton timestamp!");
48 } else {
49 return this.start;
50 }
51 }
52
53 @Override
54 public int size() {
55 return 1;
56 }
57
58 @Override
59 public boolean isPresentAtInfinity() {
60 return true;
61 }
62
63 @Override
64 public Iterable<Signed<Timestamp>> asChangeSequence() {
65 return Collections.singletonList(this.getSigned(0));
66 }
67
68 @Override
69 public boolean isEmpty() {
70 return false;
71 }
72
73}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java
new file mode 100644
index 00000000..9214536c
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java
@@ -0,0 +1,146 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.matchers.util.timeline;
10
11import java.util.ArrayList;
12import java.util.Iterator;
13import java.util.List;
14
15import tools.refinery.viatra.runtime.matchers.util.Direction;
16import tools.refinery.viatra.runtime.matchers.util.Signed;
17
18/**
19 * A timeline describes the life cycle of a piece of data (typically a tuple in a relation) as a sequence of moments.
20 * Even moments represent appearances, odd moments represent disappearances. A timeline is immutable, once created, it
21 * is not possible to extend it with further moments.
22 *
23 * @author Tamas Szabo
24 * @since 2.4
25 */
26public abstract class Timeline<Timestamp extends Comparable<Timestamp>> {
27
28 public abstract Iterable<Signed<Timestamp>> asChangeSequence();
29
30 public abstract boolean isPresentAtInfinity();
31
32 public abstract boolean isEmpty();
33
34 public abstract int size();
35
36 public abstract Signed<Timestamp> getSigned(final int index);
37
38 public abstract Timestamp getUnsigned(final int index);
39
40 public Timeline<Timestamp> mergeMultiplicative(final Timeline<Timestamp> that) {
41 final List<Timestamp> result = new ArrayList<>();
42 int thisIdx = 0, thatIdx = 0;
43 Timestamp thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null;
44 Timestamp thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null;
45
46 while (thisNext != null || thatNext != null) {
47 int thisMinusThat = 0;
48 if (thisNext != null && thatNext != null) {
49 thisMinusThat = thisNext.compareTo(thatNext);
50 }
51 if (thisNext == null || thisMinusThat > 0) {
52 if (thisIdx % 2 == 1) {
53 result.add(thatNext);
54 }
55 thatIdx++;
56 thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null;
57 } else if (thatNext == null || thisMinusThat < 0) {
58 if (thatIdx % 2 == 1) {
59 result.add(thisNext);
60 }
61 thisIdx++;
62 thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null;
63 } else {
64 if (thisIdx % 2 == thatIdx % 2) {
65 result.add(thisNext);
66 }
67 thisIdx++;
68 thatIdx++;
69 thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null;
70 thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null;
71 }
72 }
73
74 return Timelines.createFrom(result);
75 }
76
77 /**
78 * Merges this timeline with the given timestamp diff. The expectation is that the resulting timeline starts with an
79 * insertion. The logic is similar to a merge sort; we iterate side-by-side over the timeline and the diff. During
80 * the merge, cancellation can happen if at the same timestamp we observe different signs at the corresponding
81 * timeline and diff elements.
82 */
83 public Timeline<Timestamp> mergeAdditive(final Diff<Timestamp> diff) {
84 final Iterator<Signed<Timestamp>> thisItr = this.asChangeSequence().iterator();
85 final Iterator<Signed<Timestamp>> diffItr = diff.iterator();
86 final List<Timestamp> result = new ArrayList<>();
87 Direction expected = Direction.INSERT;
88 Signed<Timestamp> thisNext = thisItr.hasNext() ? thisItr.next() : null;
89 Signed<Timestamp> diffNext = diffItr.hasNext() ? diffItr.next() : null;
90
91 while (thisNext != null || diffNext != null) {
92 int thisMinusDiff = 0;
93 if (thisNext != null && diffNext != null) {
94 thisMinusDiff = thisNext.getPayload().compareTo(diffNext.getPayload());
95 }
96
97 if (thisNext == null || thisMinusDiff > 0) {
98 if (!expected.equals(diffNext.getDirection())) {
99 throw new IllegalStateException(
100 String.format("Expected direction (%s) constraint violated! %s %s @%s", expected, this,
101 diff, diffNext.getPayload()));
102 }
103 result.add(diffNext.getPayload());
104 diffNext = diffItr.hasNext() ? diffItr.next() : null;
105 expected = expected.opposite();
106 } else if (diffNext == null || thisMinusDiff < 0) {
107 if (!expected.equals(thisNext.getDirection())) {
108 throw new IllegalStateException(
109 String.format("Expected direction (%s) constraint violated! %s %s @%s", expected, this,
110 diff, thisNext.getPayload()));
111 }
112 result.add(thisNext.getPayload());
113 thisNext = thisItr.hasNext() ? thisItr.next() : null;
114 expected = expected.opposite();
115 } else {
116 // they cancel out each other
117 if (diffNext.getDirection().equals(thisNext.getDirection())) {
118 throw new IllegalStateException(String.format("Changes do not cancel out each other! %s %s @%s",
119 this, diff, thisNext.getPayload()));
120 }
121 diffNext = diffItr.hasNext() ? diffItr.next() : null;
122 thisNext = thisItr.hasNext() ? thisItr.next() : null;
123 }
124 }
125
126 return Timelines.createFrom(result);
127 }
128
129 @Override
130 public String toString() {
131 final StringBuilder builder = new StringBuilder();
132 builder.append("[");
133 boolean first = true;
134 for (final Signed<Timestamp> element : this.asChangeSequence()) {
135 if (first) {
136 first = false;
137 } else {
138 builder.append(", ");
139 }
140 builder.append(element.toString());
141 }
142 builder.append("]");
143 return builder.toString();
144 }
145
146}
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java
new file mode 100644
index 00000000..747fda15
--- /dev/null
+++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java
@@ -0,0 +1,46 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.matchers.util.timeline;
10
11import java.util.List;
12
13/**
14 * Utility class for creating {@link Timeline}s.
15 * @author Tamas Szabo
16 * @since 2.4
17 */
18public final class Timelines<Timestamp extends Comparable<Timestamp>> {
19
20 public static <Timestamp extends Comparable<Timestamp>> Timeline<Timestamp> createEmpty() {
21 return new CompactTimeline<Timestamp>();
22 }
23
24 public static <Timestamp extends Comparable<Timestamp>> Timeline<Timestamp> createFrom(
25 final Diff<Timestamp> diffs) {
26 if (diffs.size() == 1) {
27 return new SingletonTimeline<Timestamp>(diffs);
28 } else {
29 return new CompactTimeline<Timestamp>(diffs);
30 }
31 }
32
33 public static <Timestamp extends Comparable<Timestamp>> Timeline<Timestamp> createFrom(
34 final List<Timestamp> timestamps) {
35 if (timestamps.size() == 1) {
36 return new SingletonTimeline<Timestamp>(timestamps.get(0));
37 } else {
38 return new CompactTimeline<Timestamp>(timestamps);
39 }
40 }
41
42 public static <Timestamp extends Comparable<Timestamp>> Timeline<Timestamp> createFrom(final Timestamp timestamp) {
43 return new SingletonTimeline<Timestamp>(timestamp);
44 }
45
46}
diff --git a/subprojects/viatra-runtime-rete-recipes/META-INF/MANIFEST.MF b/subprojects/viatra-runtime-rete-recipes/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..c732af46
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/META-INF/MANIFEST.MF
@@ -0,0 +1,18 @@
1Manifest-Version: 1.0
2Bundle-ManifestVersion: 2
3Bundle-Name: %pluginName
4Bundle-SymbolicName: viatra-runtime-rete-recipes;singleton:=true
5Automatic-Module-Name: viatra-runtime-rete-recipes
6Bundle-Version: 1.0.0.qualifier
7Bundle-ClassPath: viatra-runtime-rete-recipes.jar
8Bundle-Vendor: %providerName
9Bundle-Localization: plugin
10Bundle-RequiredExecutionEnvironment: JavaSE-1.7
11Export-Package: org.eclipse.viatra.query.runtime.rete.recipes,
12 org.eclipse.viatra.query.runtime.rete.recipes.impl,
13 org.eclipse.viatra.query.runtime.rete.recipes.util
14Require-Bundle: org.eclipse.core.runtime,
15 org.eclipse.emf.ecore;visibility:=reexport,
16 org.eclipse.xtext.xbase.lib,
17 org.eclipse.emf.ecore.xcore.lib
18Bundle-ActivationPolicy: lazy
diff --git a/subprojects/viatra-runtime-rete-recipes/META-INF/MANIFEST.MF.license b/subprojects/viatra-runtime-rete-recipes/META-INF/MANIFEST.MF.license
new file mode 100644
index 00000000..03d1d42b
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/META-INF/MANIFEST.MF.license
@@ -0,0 +1,4 @@
1Copyright (c) 2004-2014 Gabor Bergmann and Daniel Varro
2Copyright (c) 2023 The Refinery Authors <https://refinery.tools>
3
4SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/viatra-runtime-rete-recipes/about.html b/subprojects/viatra-runtime-rete-recipes/about.html
new file mode 100644
index 00000000..d1d5593a
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/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-rete-recipes/build.gradle.kts b/subprojects/viatra-runtime-rete-recipes/build.gradle.kts
new file mode 100644
index 00000000..1f33e2f9
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/build.gradle.kts
@@ -0,0 +1,62 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7import tools.refinery.gradle.utils.SonarPropertiesUtils
8
9plugins {
10 id("tools.refinery.gradle.java-library")
11 id("tools.refinery.gradle.mwe2")
12 id("tools.refinery.gradle.sonarqube")
13}
14
15dependencies {
16 api(project(":refinery-viatra-runtime-matchers"))
17 api(libs.ecore)
18 mwe2(libs.ecore.codegen)
19 mwe2(libs.mwe.utils)
20 mwe2(libs.mwe2.lib)
21 mwe2(libs.slf4j.simple)
22 mwe2(libs.xtext.core)
23 mwe2(libs.xtext.xbase)
24}
25
26sourceSets {
27 main {
28 java.srcDir("src/main/emf-gen")
29 }
30}
31
32tasks {
33 val generateEPackage by registering(JavaExec::class) {
34 mainClass.set("org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher")
35 classpath(configurations.mwe2)
36 inputs.file("src/main/java/tools/refinery/viatra/runtime/rete/recipes/GenerateReteRecipes.mwe2")
37 inputs.file("src/main/resources/model/recipes.ecore")
38 inputs.file("src/main/resources/model/rete-recipes.genmodel")
39 outputs.file("build.properties")
40 outputs.file("META-INF/MANIFEST.MF")
41 outputs.file("plugin.xml")
42 outputs.file("plugin.properties")
43 outputs.dir("src/main/emf-gen")
44 args("src/main/java/tools/refinery/viatra/runtime/rete/recipes/GenerateReteRecipes.mwe2",
45 "-p", "rootPath=/$projectDir")
46 }
47
48 for (taskName in listOf("compileJava", "processResources", "generateEclipseSourceFolders")) {
49 named(taskName) {
50 dependsOn(generateEPackage)
51 }
52 }
53
54 clean {
55 delete("src/main/emf-gen")
56 }
57}
58
59sonarqube.properties {
60 SonarPropertiesUtils.addToList(properties, "sonar.exclusions", "src/main/emf-gen/**")
61}
62
diff --git a/subprojects/viatra-runtime-rete-recipes/build.properties b/subprojects/viatra-runtime-rete-recipes/build.properties
new file mode 100644
index 00000000..97e4d6bd
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/build.properties
@@ -0,0 +1,15 @@
1# Copyright (c) 2004-2014 Gabor Bergmann and Daniel Varro
2# This program and the accompanying materials are made available under the
3# terms of the Eclipse Public License v. 2.0 which is available at
4# http://www.eclipse.org/legal/epl-v20.html.
5#
6# SPDX-License-Identifier: EPL-2.0
7
8bin.includes = viatra-runtime-rete-recipes.jar,\
9 model/,\
10 META-INF/,\
11 plugin.xml,\
12 plugin.properties
13jars.compile.order = viatra-runtime-rete-recipes.jar
14source.viatra-runtime-rete-recipes.jar = src-gen/
15output.viatra-runtime-rete-recipes.jar = bin/
diff --git a/subprojects/viatra-runtime-rete-recipes/plugin.properties b/subprojects/viatra-runtime-rete-recipes/plugin.properties
new file mode 100644
index 00000000..43a7236d
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/plugin.properties
@@ -0,0 +1,9 @@
1# Copyright (c) 2004-2014 Gabor Bergmann and Daniel Varro
2# This program and the accompanying materials are made available under the
3# terms of the Eclipse Public License v. 2.0 which is available at
4# http://www.eclipse.org/legal/epl-v20.html.
5#
6# SPDX-License-Identifier: EPL-2.0
7
8pluginName = Rete-recipes Model
9providerName = www.example.org
diff --git a/subprojects/viatra-runtime-rete-recipes/plugin.xml b/subprojects/viatra-runtime-rete-recipes/plugin.xml
new file mode 100644
index 00000000..e4917f5d
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/plugin.xml
@@ -0,0 +1,23 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<?eclipse version="3.0"?>
3
4<!--
5 Copyright (c) 2004-2014 Gabor Bergmann and Daniel Varro
6 This program and the accompanying materials are made available under the
7 terms of the Eclipse Public License v. 2.0 which is available at
8 http://www.eclipse.org/legal/epl-v20.html.
9
10 SPDX-License-Identifier: EPL-2.0
11-->
12
13<plugin>
14
15 <extension point="org.eclipse.emf.ecore.generated_package">
16 <!-- @generated rete-recipes -->
17 <package
18 uri="https://refinery.tools/emf/2023/ViatraReteRecipes"
19 class="tools.refinery.viatra.runtime.rete.recipes.RecipesPackage"
20 genModel="src/main/resources/model/rete-recipes.genmodel"/>
21 </extension>
22
23</plugin>
diff --git a/subprojects/viatra-runtime-rete-recipes/src/main/java/tools/refinery/viatra/runtime/rete/recipes/GenerateReteRecipes.mwe2 b/subprojects/viatra-runtime-rete-recipes/src/main/java/tools/refinery/viatra/runtime/rete/recipes/GenerateReteRecipes.mwe2
new file mode 100644
index 00000000..424cc9f5
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/src/main/java/tools/refinery/viatra/runtime/rete/recipes/GenerateReteRecipes.mwe2
@@ -0,0 +1,25 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6module tools.refinery.viatra.runtime.rete.recipes.GenerateReteRecipes
7
8Workflow {
9 bean = org.eclipse.emf.mwe.utils.StandaloneSetup {
10 projectMapping = {
11 projectName = "tools.refinery.refinery-viatra-runtime-rete-recipes"
12 path = "."
13 }
14 }
15
16 component = org.eclipse.emf.mwe.utils.DirectoryCleaner {
17 directory = "src/main/emf-gen"
18 }
19
20 component = org.eclipse.emf.mwe2.ecore.EcoreGenerator {
21 generateCustomClasses = false
22 genModel = "platform:/resource/tools.refinery.refinery-viatra-runtime-rete-recipes/src/main/resources/model/rete-recipes.genmodel"
23 srcPath = "platform:/resource/tools.refinery.refinery-viatra-runtime-rete-recipes/src/main/emf-gen"
24 }
25}
diff --git a/subprojects/viatra-runtime-rete-recipes/src/main/java/tools/refinery/viatra/runtime/rete/recipes/helper/RecipeRecognizer.java b/subprojects/viatra-runtime-rete-recipes/src/main/java/tools/refinery/viatra/runtime/rete/recipes/helper/RecipeRecognizer.java
new file mode 100644
index 00000000..2c252593
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/src/main/java/tools/refinery/viatra/runtime/rete/recipes/helper/RecipeRecognizer.java
@@ -0,0 +1,204 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.recipes.helper;
10
11import org.eclipse.emf.common.util.EList;
12import org.eclipse.emf.ecore.EAttribute;
13import org.eclipse.emf.ecore.EClass;
14import org.eclipse.emf.ecore.EObject;
15import org.eclipse.emf.ecore.EStructuralFeature;
16import org.eclipse.emf.ecore.util.EcoreUtil;
17import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
18import tools.refinery.viatra.runtime.rete.recipes.RecipesPackage;
19import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
20
21import java.util.*;
22
23/**
24 * Stores a set of known <em>canonical</em> recipes, each representing a disjoint equivalence class of recipes, modulo
25 * {@link #isEquivalentRecipe(ReteNodeRecipe, ReteNodeRecipe)}.
26 *
27 * @author Gabor Bergmann
28 * @since 1.3
29 *
30 */
31public class RecipeRecognizer {
32 private static long nextRecipeEquivalenceClassID = 0;
33
34 /**
35 * if EcoreUtil.equals(recipe1, recipe2), only one of them will be included here
36 */
37 Map<EClass, Set<ReteNodeRecipe>> canonicalRecipesByClass = new HashMap<>();
38 Map<Long, ReteNodeRecipe> canonicalRecipeByEquivalenceClassID = new HashMap<>();
39
40 private IQueryRuntimeContext runtimeContext;
41
42 /**
43 * @param can be null; if provided, further equivalences can be detected based on {@link IQueryRuntimeContext#wrapElement(Object)}
44 * @since 1.6
45 */
46 public RecipeRecognizer(IQueryRuntimeContext runtimeContext) {
47 this.runtimeContext = runtimeContext;
48 }
49 public RecipeRecognizer() {
50 this(null);
51 }
52
53 /**
54 * Recognizes when an equivalent canonical recipe is already known.
55 *
56 * @return an equivalent canonical recipe, or the null if no known equivalent found
57 */
58 public ReteNodeRecipe peekCanonicalRecipe(final ReteNodeRecipe recipe) {
59 // equivalence class already known
60 for (Long classID : recipe.getEquivalenceClassIDs()) {
61 ReteNodeRecipe knownRecipe = canonicalRecipeByEquivalenceClassID.get(classID);
62 if (knownRecipe != null)
63 return knownRecipe;
64 }
65
66 // equivalence class not known, but maybe equivalent recipe still
67 // available
68 Collection<ReteNodeRecipe> sameClassRecipes = getSameClassCanonicalRecipes(recipe);
69 for (ReteNodeRecipe knownRecipe : sameClassRecipes) {
70 if (isEquivalentRecipe(recipe, knownRecipe)) {
71 // FOUND EQUIVALENT RECIPE
72 recipe.getEquivalenceClassIDs().add(knownRecipe.getEquivalenceClassIDs().get(0));
73 return knownRecipe;
74 }
75 }
76
77 return null;
78 }
79
80 /**
81 * This recipe will be remembered as a canonical recipe. Method maintains both internal data structures and the
82 * equivalence class attribute of the recipe. PRECONDITION: {@link #peekCanonicalRecipe(ReteNodeRecipe)} must return
83 * null or the recipe itself
84 */
85 public void makeCanonical(final ReteNodeRecipe recipe) {
86 // this is a canonical recipe, chosen representative of its new
87 // equivalence class
88 if (recipe.getEquivalenceClassIDs().isEmpty()) {
89 recipe.getEquivalenceClassIDs().add(nextRecipeEquivalenceClassID++);
90 }
91 for (Long classID : recipe.getEquivalenceClassIDs()) {
92 canonicalRecipeByEquivalenceClassID.put(classID, recipe);
93 }
94 getSameClassCanonicalRecipes(recipe).add(recipe);
95 }
96
97 /**
98 * Ensures that there is an equivalent canonical recipe; if none is known yet, this recipe will be remembered as
99 * canonical.
100 *
101 * @return an equivalent canonical recipe; the argument recipe itself (which is made canonical) if no known
102 * equivalent found
103 */
104 public ReteNodeRecipe canonicalizeRecipe(final ReteNodeRecipe recipe) {
105 ReteNodeRecipe knownRecipe = peekCanonicalRecipe(recipe);
106 if (knownRecipe == null) {
107 knownRecipe = recipe;
108 makeCanonical(recipe);
109 }
110 return knownRecipe;
111 }
112
113 /**
114 * @return true iff recipe is a canonical recipe
115 */
116 public boolean isKnownCanonicalRecipe(final ReteNodeRecipe recipe) {
117 if (recipe.getEquivalenceClassIDs().isEmpty()) {
118 return false;
119 }
120 ReteNodeRecipe knownRecipe = canonicalRecipeByEquivalenceClassID.get(recipe.getEquivalenceClassIDs().get(0));
121 return recipe == knownRecipe;
122 }
123
124 private Set<ReteNodeRecipe> getSameClassCanonicalRecipes(final ReteNodeRecipe recipe) {
125 Set<ReteNodeRecipe> sameClassRecipes = canonicalRecipesByClass.get(recipe.eClass());
126 if (sameClassRecipes == null) {
127 sameClassRecipes = new HashSet<>();
128 canonicalRecipesByClass.put(recipe.eClass(), sameClassRecipes);
129 }
130 return sameClassRecipes;
131 }
132
133 private boolean isEquivalentRecipe(ReteNodeRecipe recipe, ReteNodeRecipe knownRecipe) {
134 return new EqualityHelper(runtimeContext).equals(recipe, knownRecipe);
135 }
136
137 // TODO reuse in more cases later, e.g. switching join node parents, etc.
138 private static class EqualityHelper extends EcoreUtil.EqualityHelper {
139
140
141
142
143 private static final long serialVersionUID = -8841971394686015188L;
144
145 private static final EAttribute RETE_NODE_RECIPE_EQUIVALENCE_CLASS_IDS =
146 RecipesPackage.eINSTANCE.getReteNodeRecipe_EquivalenceClassIDs();
147 private static final EAttribute CONSTANT_RECIPE_CONSTANT_VALUES =
148 RecipesPackage.eINSTANCE.getConstantRecipe_ConstantValues();
149 private static final EAttribute DISCRIMINATOR_BUCKET_RECIPE_BUCKET_KEY =
150 RecipesPackage.eINSTANCE.getDiscriminatorBucketRecipe_BucketKey();
151
152 private IQueryRuntimeContext runtimeContext;
153
154 public EqualityHelper(IQueryRuntimeContext runtimeContext) {
155 this.runtimeContext = runtimeContext;
156 }
157
158 @Override
159 protected boolean haveEqualFeature(EObject eObject1, EObject eObject2, EStructuralFeature feature) {
160 // ignore differences in this attribute, as it may only be assigned
161 // after the equivalence check
162 if (RETE_NODE_RECIPE_EQUIVALENCE_CLASS_IDS.equals(feature))
163 return true;
164
165 if (runtimeContext != null) {
166 // constant values
167 if (/*CONSTANT_RECIPE_CONSTANT_VALUES.equals(feature) ||*/ DISCRIMINATOR_BUCKET_RECIPE_BUCKET_KEY.equals(feature)) {
168 // use runtime context to map to canonical wrapped form
169 // this is costly for constant recipes (TODO improve this), but essential for discriminator buckets
170
171 Object val1 = eObject1.eGet(feature);
172 Object val2 = eObject2.eGet(feature);
173
174 if (val1 != null && val2 != null) {
175 return runtimeContext.wrapElement(val1).equals(runtimeContext.wrapElement(val2));
176 } else {
177 return val1 == null && val2 == null;
178 }
179
180 }
181 }
182
183 // fallback to general comparison
184 return super.haveEqualFeature(eObject1, eObject2, feature);
185 }
186
187 @Override
188 public boolean equals(EObject eObject1, EObject eObject2) {
189 // short-circuit if already known to be equivalent
190 if (eObject1 instanceof ReteNodeRecipe) {
191 if (eObject2 instanceof ReteNodeRecipe) {
192 EList<Long> eqClassIDs1 = ((ReteNodeRecipe) eObject1).getEquivalenceClassIDs();
193 EList<Long> eqClassIDs2 = ((ReteNodeRecipe) eObject2).getEquivalenceClassIDs();
194
195 if (!Collections.disjoint(eqClassIDs1, eqClassIDs2))
196 return true;
197 }
198 }
199
200 // fallback to general comparison
201 return super.equals(eObject1, eObject2);
202 }
203 }
204}
diff --git a/subprojects/viatra-runtime-rete-recipes/src/main/java/tools/refinery/viatra/runtime/rete/recipes/helper/RecipesHelper.java b/subprojects/viatra-runtime-rete-recipes/src/main/java/tools/refinery/viatra/runtime/rete/recipes/helper/RecipesHelper.java
new file mode 100644
index 00000000..3070fcf5
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/src/main/java/tools/refinery/viatra/runtime/rete/recipes/helper/RecipesHelper.java
@@ -0,0 +1,81 @@
1/**
2 * Copyright (c) 2004-2014 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.rete.recipes.helper;
10
11import org.eclipse.emf.common.util.EList;
12import tools.refinery.viatra.runtime.rete.recipes.*;
13
14import java.util.Collection;
15
16/**
17 * Static helper class for easy construction of recipes.
18 *
19 * @author Bergmann Gabor
20 */
21public class RecipesHelper {
22 private static final RecipesFactory FACTORY = RecipesFactory.eINSTANCE;
23
24 private RecipesHelper() {/* Utility class constructor */}
25
26 /**
27 * @since 2.0
28 */
29 public static Mask mask(final int sourceArity, final Collection<Integer> sourceIndices) {
30 Mask mask = RecipesHelper.FACTORY.createMask();
31 mask.setSourceArity(sourceArity);
32 mask.getSourceIndices().addAll(sourceIndices);
33 return mask;
34 }
35
36 public static Mask mask(final int sourceArity, final int... sourceIndices) {
37 Mask mask = RecipesHelper.FACTORY.createMask();
38 mask.setSourceArity(sourceArity);
39 final EList<Integer> maskIndeces = mask.getSourceIndices();
40 for (int index : sourceIndices) {
41 maskIndeces.add(index);
42 }
43 return mask;
44 }
45
46 public static ProjectionIndexerRecipe projectionIndexerRecipe(final ReteNodeRecipe parent, final Mask mask) {
47 ProjectionIndexerRecipe recipe = RecipesHelper.FACTORY.createProjectionIndexerRecipe();
48 recipe.setParent(parent);
49 recipe.setMask(mask);
50 return recipe;
51 }
52
53 public static ExpressionDefinition expressionDefinition(final Object evaluator) {
54 ExpressionDefinition definition = RecipesHelper.FACTORY.createExpressionDefinition();
55 definition.setEvaluator(evaluator);
56 return definition;
57 }
58
59 public static InputRecipe inputRecipe(final Object inputKey, final String inputKeyID, final int arity) {
60 InputRecipe recipe = RecipesHelper.FACTORY.createInputRecipe();
61 recipe.setInputKey(inputKey);
62 recipe.setKeyArity(arity);
63 recipe.setKeyID(inputKeyID);
64 recipe.setTraceInfo(inputKeyID);
65 return recipe;
66 }
67
68 /**
69 * Mask can be null in case no tuple reordering or trimming is needed
70 */
71 public static InputFilterRecipe inputFilterRecipe(final ReteNodeRecipe parent, final Object inputKey,
72 final String inputKeyID, final Mask mask) {
73 InputFilterRecipe it = RecipesHelper.FACTORY.createInputFilterRecipe();
74 it.setParent(parent);
75 it.setInputKey(inputKey);
76 it.setKeyID(inputKeyID);
77 it.setTraceInfo(inputKeyID);
78 it.setMask(mask);
79 return it;
80 }
81}
diff --git a/subprojects/viatra-runtime-rete-recipes/src/main/resources/model/recipes.ecore b/subprojects/viatra-runtime-rete-recipes/src/main/resources/model/recipes.ecore
new file mode 100644
index 00000000..4eda701e
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/src/main/resources/model/recipes.ecore
@@ -0,0 +1,400 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="recipes" nsURI="https://refinery.tools/emf/2023/ViatraReteRecipes"
4 nsPrefix="recipes">
5 <eClassifiers xsi:type="ecore:EClass" name="ReteRecipe">
6 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
7 <details key="documentation" value="Container for Rete recipes."/>
8 </eAnnotations>
9 <eStructuralFeatures xsi:type="ecore:EReference" name="recipeNodes" upperBound="-1"
10 eType="#//ReteNodeRecipe" containment="true" resolveProxies="false"/>
11 </eClassifiers>
12 <eClassifiers xsi:type="ecore:EClass" name="ReteNodeRecipe" abstract="true">
13 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
14 <details key="documentation" value="Abstract base class for model elements that represent &quot;Rete node recipes&quot;,&#xD;&#xA;that is DTOs that carry information for Rete network construction.&#xD;&#xA;&#xD;&#xA;@noimplement"/>
15 </eAnnotations>
16 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
17 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
18 <details key="documentation" value=" The width of tuples contained by this node."/>
19 <details key="body" value="throw new &lt;%java.lang.UnsupportedOperationException%>();"/>
20 </eAnnotations>
21 </eOperations>
22 <eStructuralFeatures xsi:type="ecore:EAttribute" name="traceInfo" unique="false"
23 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString">
24 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
25 <details key="documentation" value="Temporary construct for storing traceability information."/>
26 </eAnnotations>
27 </eStructuralFeatures>
28 <eStructuralFeatures xsi:type="ecore:EAttribute" name="equivalenceClassIDs" upperBound="-1"
29 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//ELong" transient="true">
30 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
31 <details key="documentation" value="If two recipes were found equivalent, a matching equivalence ID can be assigned to them by {@link RecipeRecognizer}. &#xD;&#xA;If two recipes share (at least one) equivalence ID, they are known to be equivalent.&#xD;&#xA;&#xD;&#xA;&lt;p>&#xD;&#xA;A difference in this attribute only does not preclude two recipe elements to be considered equal. &#xD;&#xA;If they are shown to be equivalent using deeper analysis, equivalence ids can be set so that the equivalence is recognized more easily the next time.&#xD;&#xA;&#xD;&#xA;@since 1.3"/>
32 </eAnnotations>
33 </eStructuralFeatures>
34 </eClassifiers>
35 <eClassifiers xsi:type="ecore:EClass" name="SingleParentNodeRecipe" abstract="true"
36 eSuperTypes="#//ReteNodeRecipe">
37 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
38 <details key="documentation" value="Abstract base class for single-parent node recipes."/>
39 </eAnnotations>
40 <eStructuralFeatures xsi:type="ecore:EReference" name="parent" eType="#//ReteNodeRecipe"/>
41 </eClassifiers>
42 <eClassifiers xsi:type="ecore:EClass" name="AlphaRecipe" abstract="true" eSuperTypes="#//SingleParentNodeRecipe">
43 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
44 <details key="documentation" value="Abstract base class for alpha node recipes."/>
45 </eAnnotations>
46 </eClassifiers>
47 <eClassifiers xsi:type="ecore:EClass" name="MultiParentNodeRecipe" abstract="true"
48 eSuperTypes="#//ReteNodeRecipe">
49 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
50 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
51 <details key="body" value="&lt;%org.eclipse.emf.common.util.EList%>&lt;&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%>> _parents = this.getParents();&#xA;&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%> _get = _parents.get(0);&#xA;return _get.getArity();"/>
52 </eAnnotations>
53 </eOperations>
54 <eStructuralFeatures xsi:type="ecore:EReference" name="parents" upperBound="-1"
55 eType="#//ReteNodeRecipe"/>
56 </eClassifiers>
57 <eClassifiers xsi:type="ecore:EClass" name="MonotonicityInfo">
58 <eStructuralFeatures xsi:type="ecore:EReference" name="coreMask" eType="#//Mask"
59 containment="true" resolveProxies="false"/>
60 <eStructuralFeatures xsi:type="ecore:EReference" name="posetMask" eType="#//Mask"
61 containment="true" resolveProxies="false"/>
62 <eStructuralFeatures xsi:type="ecore:EAttribute" name="posetComparator" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EJavaObject"/>
63 </eClassifiers>
64 <eClassifiers xsi:type="ecore:EClass" name="UniquenessEnforcerRecipe" eSuperTypes="#//MultiParentNodeRecipe #//RederivableNodeRecipe">
65 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
66 <details key="documentation" value="Represents nodes that enforce tuple uniqueness, i.e. filter out&#xA;duplicate input tuples for output."/>
67 </eAnnotations>
68 </eClassifiers>
69 <eClassifiers xsi:type="ecore:EClass" name="ProductionRecipe" eSuperTypes="#//MultiParentNodeRecipe #//RederivableNodeRecipe">
70 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
71 <details key="documentation" value="The production node represents the output of the Rete network,&#xA;from which the results of a query can be read."/>
72 </eAnnotations>
73 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
74 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
75 <details key="body" value="return this.getMappedIndices().size();"/>
76 </eAnnotations>
77 </eOperations>
78 <eStructuralFeatures xsi:type="ecore:EReference" name="mappedIndices" upperBound="-1"
79 eType="#//StringIndexMapEntry" containment="true" resolveProxies="false">
80 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
81 <details key="documentation" value="String -> Index map.&#xA;Indicates the positions of parameters."/>
82 </eAnnotations>
83 </eStructuralFeatures>
84 <eStructuralFeatures xsi:type="ecore:EAttribute" name="pattern" unique="false"
85 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EJavaObject">
86 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
87 <details key="documentation" value="Traceability link to defining pattern object (from EMFPatternLanguage)&#xA;TODO unused?"/>
88 </eAnnotations>
89 </eStructuralFeatures>
90 <eStructuralFeatures xsi:type="ecore:EAttribute" name="patternFQN" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
91 </eClassifiers>
92 <eClassifiers xsi:type="ecore:EClass" name="IndexerRecipe" abstract="true" eSuperTypes="#//SingleParentNodeRecipe">
93 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
94 <details key="documentation" value="Represents a node that indexes the contents of a parent based on a projection defined by a Mask."/>
95 </eAnnotations>
96 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
97 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
98 <details key="body" value="&lt;%tools.refinery.viatra.runtime.rete.recipes.Mask%> _mask = this.getMask();&#xA;return _mask.getSourceArity();"/>
99 </eAnnotations>
100 </eOperations>
101 <eStructuralFeatures xsi:type="ecore:EReference" name="mask" eType="#//Mask" containment="true"
102 resolveProxies="false"/>
103 </eClassifiers>
104 <eClassifiers xsi:type="ecore:EClass" name="ProjectionIndexerRecipe" eSuperTypes="#//IndexerRecipe">
105 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
106 <details key="documentation" value="Represents helper nodes that provide projection indexing for Beta nodes and user queries."/>
107 </eAnnotations>
108 </eClassifiers>
109 <eClassifiers xsi:type="ecore:EClass" name="AggregatorIndexerRecipe" eSuperTypes="#//IndexerRecipe">
110 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
111 <details key="documentation" value="Attached to an aggregator node, provides the aggregated values for outer join."/>
112 </eAnnotations>
113 </eClassifiers>
114 <eClassifiers xsi:type="ecore:EClass" name="BetaRecipe" abstract="true" eSuperTypes="#//ReteNodeRecipe">
115 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
116 <details key="documentation" value="Abstract base class for Beta node recipes."/>
117 </eAnnotations>
118 <eStructuralFeatures xsi:type="ecore:EReference" name="leftParent" eType="#//ProjectionIndexerRecipe"
119 containment="true" resolveProxies="false"/>
120 <eStructuralFeatures xsi:type="ecore:EReference" name="rightParent" eType="#//IndexerRecipe"
121 containment="true" resolveProxies="false">
122 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
123 <details key="documentation" value=" can be an AggregatorIndexer"/>
124 </eAnnotations>
125 </eStructuralFeatures>
126 </eClassifiers>
127 <eClassifiers xsi:type="ecore:EClass" name="Mask">
128 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
129 <details key="documentation" value="A mask defines the set of tuple variables that need to be taken into consideration for operations."/>
130 </eAnnotations>
131 <eStructuralFeatures xsi:type="ecore:EAttribute" name="sourceIndices" unique="false"
132 upperBound="-1" eType="#//Index">
133 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
134 <details key="documentation" value="The indices that are relevant for tuple operations."/>
135 </eAnnotations>
136 </eStructuralFeatures>
137 <eStructuralFeatures xsi:type="ecore:EAttribute" name="sourceArity" unique="false"
138 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
139 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
140 <details key="documentation" value="The arity of tuples."/>
141 </eAnnotations>
142 </eStructuralFeatures>
143 </eClassifiers>
144 <eClassifiers xsi:type="ecore:EDataType" name="Index" instanceClassName="java.lang.Integer">
145 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
146 <details key="documentation" value="Indexes tell which variable of tuples are relevant for a given operation.&#xA;TODO: is this necessary at all?"/>
147 </eAnnotations>
148 </eClassifiers>
149 <eClassifiers xsi:type="ecore:EClass" name="StringIndexMapEntry" instanceClassName="java.util.Map$Entry">
150 <eStructuralFeatures xsi:type="ecore:EAttribute" name="key" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
151 <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" unique="false" eType="#//Index"/>
152 </eClassifiers>
153 <eClassifiers xsi:type="ecore:EClass" name="InputRecipe" eSuperTypes="#//ReteNodeRecipe">
154 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
155 <details key="documentation" value="Represents input nodes for the Rete network, i.e. nodes&#xA;that generate input tuples for processing."/>
156 </eAnnotations>
157 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
158 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
159 <details key="body" value="return getKeyArity();"/>
160 </eAnnotations>
161 </eOperations>
162 <eStructuralFeatures xsi:type="ecore:EAttribute" name="inputKey" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EJavaObject"
163 transient="true"/>
164 <eStructuralFeatures xsi:type="ecore:EAttribute" name="keyID" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString">
165 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
166 <details key="documentation" value="Temporary construct for identifying types over the wire.&#xA;TODO improve type references"/>
167 </eAnnotations>
168 </eStructuralFeatures>
169 <eStructuralFeatures xsi:type="ecore:EAttribute" name="keyArity" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
170 </eClassifiers>
171 <eClassifiers xsi:type="ecore:EClass" name="ConstantRecipe" eSuperTypes="#//ReteNodeRecipe">
172 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
173 <details key="documentation" value="Simple node that stores constant values."/>
174 </eAnnotations>
175 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
176 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
177 <details key="body" value="return this.getConstantValues().size();"/>
178 </eAnnotations>
179 </eOperations>
180 <eStructuralFeatures xsi:type="ecore:EAttribute" name="constantValues" unique="false"
181 upperBound="-1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EJavaObject">
182 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
183 <details key="documentation" value="Stores constant values. May be empty.&#xA;&#xA;TODO store constants as strings instead? (for easier serialization)"/>
184 </eAnnotations>
185 </eStructuralFeatures>
186 </eClassifiers>
187 <eClassifiers xsi:type="ecore:EClass" name="TransitiveClosureRecipe" eSuperTypes="#//AlphaRecipe">
188 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
189 <details key="documentation" value="Represents transitive closure."/>
190 </eAnnotations>
191 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
192 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
193 <details key="body" value="return 2;"/>
194 </eAnnotations>
195 </eOperations>
196 </eClassifiers>
197 <eClassifiers xsi:type="ecore:EClass" name="FilterRecipe" abstract="true" eSuperTypes="#//AlphaRecipe">
198 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
199 <details key="documentation" value="Abstract base class for nodes that implement filtering operations."/>
200 </eAnnotations>
201 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
202 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
203 <details key="body" value="&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%> _parent = this.getParent();&#xA;return _parent.getArity();"/>
204 </eAnnotations>
205 </eOperations>
206 </eClassifiers>
207 <eClassifiers xsi:type="ecore:EClass" name="InequalityFilterRecipe" eSuperTypes="#//FilterRecipe">
208 <eStructuralFeatures xsi:type="ecore:EAttribute" name="subject" unique="false"
209 eType="#//Index"/>
210 <eStructuralFeatures xsi:type="ecore:EAttribute" name="inequals" unique="false"
211 upperBound="-1" eType="#//Index"/>
212 </eClassifiers>
213 <eClassifiers xsi:type="ecore:EClass" name="EqualityFilterRecipe" eSuperTypes="#//FilterRecipe">
214 <eStructuralFeatures xsi:type="ecore:EAttribute" name="indices" unique="false"
215 upperBound="-1" eType="#//Index"/>
216 </eClassifiers>
217 <eClassifiers xsi:type="ecore:EClass" name="TransparentRecipe" eSuperTypes="#//FilterRecipe"/>
218 <eClassifiers xsi:type="ecore:EClass" name="TrimmerRecipe" eSuperTypes="#//AlphaRecipe">
219 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
220 <details key="documentation" value="Implements projection without uniqueness checking."/>
221 </eAnnotations>
222 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
223 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
224 <details key="body" value="&lt;%tools.refinery.viatra.runtime.rete.recipes.Mask%> _mask = this.getMask();&#xA;&lt;%org.eclipse.emf.common.util.EList%>&lt;&lt;%java.lang.Integer%>> _sourceIndices = _mask.getSourceIndices();&#xA;return _sourceIndices.size();"/>
225 </eAnnotations>
226 </eOperations>
227 <eStructuralFeatures xsi:type="ecore:EReference" name="mask" eType="#//Mask" containment="true"
228 resolveProxies="false"/>
229 </eClassifiers>
230 <eClassifiers xsi:type="ecore:EClass" name="ExpressionDefinition">
231 <eStructuralFeatures xsi:type="ecore:EAttribute" name="evaluator" unique="false"
232 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EJavaObject"/>
233 </eClassifiers>
234 <eClassifiers xsi:type="ecore:EClass" name="ExpressionEnforcerRecipe" abstract="true"
235 eSuperTypes="#//AlphaRecipe">
236 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
237 <details key="documentation" value="type RuntimeExpressionEvaluator wraps tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator&#xA;class RuntimeExpressionDefinition extends ExpressionDefinition {&#xA;&#x9;RuntimeExpressionEvaluator evaluator&#xA;}"/>
238 </eAnnotations>
239 <eStructuralFeatures xsi:type="ecore:EReference" name="expression" eType="#//ExpressionDefinition"
240 containment="true" resolveProxies="false">
241 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
242 <details key="documentation" value="Provides traceability to expression representation."/>
243 </eAnnotations>
244 </eStructuralFeatures>
245 <eStructuralFeatures xsi:type="ecore:EReference" name="mappedIndices" upperBound="-1"
246 eType="#//StringIndexMapEntry" containment="true" resolveProxies="false">
247 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
248 <details key="documentation" value="String -> Index map.&#xA;Maps variable names in the expression to tuple indices."/>
249 </eAnnotations>
250 </eStructuralFeatures>
251 <eStructuralFeatures xsi:type="ecore:EAttribute" name="cacheOutput" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
252 </eClassifiers>
253 <eClassifiers xsi:type="ecore:EClass" name="CheckRecipe" eSuperTypes="#//ExpressionEnforcerRecipe">
254 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
255 <details key="documentation" value="Computes the result of the boolean expression evaluation and&#xA;only passes tuples for which the result is true."/>
256 </eAnnotations>
257 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
258 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
259 <details key="body" value="&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%> _parent = this.getParent();&#xA;return _parent.getArity();"/>
260 </eAnnotations>
261 </eOperations>
262 </eClassifiers>
263 <eClassifiers xsi:type="ecore:EClass" name="EvalRecipe" eSuperTypes="#//ExpressionEnforcerRecipe">
264 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
265 <details key="documentation" value="Computes the result of expression evaluation and suffixes the result&#xA;to output tuples as the last element."/>
266 </eAnnotations>
267 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
268 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
269 <details key="body" value="&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%> _parent = this.getParent();&#xA;int _arity = _parent.getArity();&#xA;return (1 + _arity);"/>
270 </eAnnotations>
271 </eOperations>
272 <eStructuralFeatures xsi:type="ecore:EAttribute" name="unwinding" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean">
273 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
274 <details key="documentation" value="@since 2.4"/>
275 </eAnnotations>
276 </eStructuralFeatures>
277 </eClassifiers>
278 <eClassifiers xsi:type="ecore:EClass" name="IndexerBasedAggregatorRecipe" abstract="true"
279 eSuperTypes="#//ReteNodeRecipe">
280 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
281 <details key="documentation" value="Represents a (compound) node that performs an aggregation operation.&#xA;Parent must be a ProjectionIndexer, which defines how tuples are to be aggregated.&#xA;Usable only through an Join with an AggregatorIndexer as the right parent"/>
282 </eAnnotations>
283 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
284 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
285 <details key="body" value="&lt;%tools.refinery.viatra.runtime.rete.recipes.ProjectionIndexerRecipe%> _parent = this.getParent();&#xA;&lt;%tools.refinery.viatra.runtime.rete.recipes.Mask%> _mask = _parent.getMask();&#xA;&lt;%org.eclipse.emf.common.util.EList%>&lt;&lt;%java.lang.Integer%>> _sourceIndices = _mask.getSourceIndices();&#xA;int _size = _sourceIndices.size();&#xA;return (1 + _size);"/>
286 </eAnnotations>
287 </eOperations>
288 <eStructuralFeatures xsi:type="ecore:EReference" name="parent" eType="#//ProjectionIndexerRecipe"
289 containment="true" resolveProxies="false"/>
290 </eClassifiers>
291 <eClassifiers xsi:type="ecore:EClass" name="CountAggregatorRecipe" eSuperTypes="#//IndexerBasedAggregatorRecipe">
292 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
293 <details key="documentation" value="The count aggregator node represents a &quot;count find&quot; operation."/>
294 </eAnnotations>
295 </eClassifiers>
296 <eClassifiers xsi:type="ecore:EClass" name="JoinRecipe" eSuperTypes="#//BetaRecipe">
297 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
298 <details key="documentation" value="The most basic beta operation, the join node performs a join operation over two input tuple sets."/>
299 </eAnnotations>
300 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
301 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
302 <details key="body" value="&lt;%tools.refinery.viatra.runtime.rete.recipes.ProjectionIndexerRecipe%> _leftParent = this.getLeftParent();&#xA;int _arity = _leftParent.getArity();&#xA;&lt;%tools.refinery.viatra.runtime.rete.recipes.IndexerRecipe%> _rightParent = this.getRightParent();&#xA;int _arity_1 = _rightParent.getArity();&#xA;int _plus = (_arity + _arity_1);&#xA;&lt;%tools.refinery.viatra.runtime.rete.recipes.IndexerRecipe%> _rightParent_1 = this.getRightParent();&#xA;&lt;%tools.refinery.viatra.runtime.rete.recipes.Mask%> _mask = _rightParent_1.getMask();&#xA;&lt;%org.eclipse.emf.common.util.EList%>&lt;&lt;%java.lang.Integer%>> _sourceIndices = _mask.getSourceIndices();&#xA;int _size = _sourceIndices.size();&#xA;return (_plus - _size);"/>
303 </eAnnotations>
304 </eOperations>
305 <eStructuralFeatures xsi:type="ecore:EReference" name="rightParentComplementaryMask"
306 eType="#//Mask" containment="true" resolveProxies="false"/>
307 </eClassifiers>
308 <eClassifiers xsi:type="ecore:EClass" name="ExistenceJoinRecipe" abstract="true"
309 eSuperTypes="#//BetaRecipe">
310 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
311 <details key="documentation" value="Existence joins are TODO&#xA;&#xA;See http://en.wikipedia.org/wiki/Relational_algebra"/>
312 </eAnnotations>
313 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
314 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
315 <details key="body" value="&lt;%tools.refinery.viatra.runtime.rete.recipes.ProjectionIndexerRecipe%> _leftParent = this.getLeftParent();&#xA;return _leftParent.getArity();"/>
316 </eAnnotations>
317 </eOperations>
318 </eClassifiers>
319 <eClassifiers xsi:type="ecore:EClass" name="SemiJoinRecipe" eSuperTypes="#//ExistenceJoinRecipe">
320 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
321 <details key="documentation" value="A semi-join is TODO&#xA;&#xA;See http://en.wikipedia.org/wiki/Relational_algebra"/>
322 </eAnnotations>
323 </eClassifiers>
324 <eClassifiers xsi:type="ecore:EClass" name="AntiJoinRecipe" eSuperTypes="#//ExistenceJoinRecipe">
325 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
326 <details key="documentation" value="An anti-join is TODO&#xA;&#xA;See http://en.wikipedia.org/wiki/Relational_algebra"/>
327 </eAnnotations>
328 </eClassifiers>
329 <eClassifiers xsi:type="ecore:EClass" name="InputFilterRecipe" eSuperTypes="#//FilterRecipe">
330 <eStructuralFeatures xsi:type="ecore:EAttribute" name="inputKey" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EJavaObject"
331 transient="true"/>
332 <eStructuralFeatures xsi:type="ecore:EAttribute" name="keyID" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString">
333 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
334 <details key="documentation" value="Temporary construct for identifying types over the wire.&#xA;TODO improve type references"/>
335 </eAnnotations>
336 </eStructuralFeatures>
337 <eStructuralFeatures xsi:type="ecore:EReference" name="mask" eType="#//Mask" containment="true"
338 resolveProxies="false"/>
339 </eClassifiers>
340 <eClassifiers xsi:type="ecore:EClass" name="SingleColumnAggregatorRecipe" eSuperTypes="#//AlphaRecipe #//RederivableNodeRecipe">
341 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
342 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
343 <details key="body" value="&lt;%tools.refinery.viatra.runtime.rete.recipes.MonotonicityInfo%> info = getOptionalMonotonicityInfo();&#xA;if (info == null) {&#xA;&#x9;return 1 + getGroupByMask().getSourceIndices().size();&#xA;} else {&#x9;&#xA;&#x9;return info.getCoreMask().getSourceIndices().size() + info.getPosetMask().getSourceIndices().size();&#xA;}"/>
344 </eAnnotations>
345 </eOperations>
346 <eStructuralFeatures xsi:type="ecore:EAttribute" name="multisetAggregationOperator"
347 eType="#//AggregationOperator"/>
348 <eStructuralFeatures xsi:type="ecore:EAttribute" name="aggregableIndex" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
349 <eStructuralFeatures xsi:type="ecore:EReference" name="groupByMask" lowerBound="1"
350 eType="#//Mask" containment="true"/>
351 </eClassifiers>
352 <eClassifiers xsi:type="ecore:EDataType" name="AggregationOperator" instanceTypeName="tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator&lt;?, ?, ?>"/>
353 <eClassifiers xsi:type="ecore:EClass" name="DiscriminatorDispatcherRecipe" eSuperTypes="#//SingleParentNodeRecipe">
354 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
355 <details key="documentation" value="Node that sends tuples off to different buckets (attached as children) based on the value of a given column."/>
356 </eAnnotations>
357 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
358 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
359 <details key="body" value="&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%> _parent = this.getParent();&#xA;return _parent.getArity();"/>
360 </eAnnotations>
361 </eOperations>
362 <eStructuralFeatures xsi:type="ecore:EAttribute" name="discriminationColumnIndex"
363 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
364 </eClassifiers>
365 <eClassifiers xsi:type="ecore:EClass" name="DiscriminatorBucketRecipe" eSuperTypes="#//SingleParentNodeRecipe">
366 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
367 <details key="documentation" value="A bucket holds a filtered set of tuples of its parent DiscriminatorDispatcher; exactly those that have the given bucket key at their discrimination column."/>
368 </eAnnotations>
369 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
370 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
371 <details key="body" value="&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%> _parent = this.getParent();&#xA;return _parent.getArity();"/>
372 </eAnnotations>
373 </eOperations>
374 <eStructuralFeatures xsi:type="ecore:EAttribute" name="bucketKey" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EJavaObject"/>
375 </eClassifiers>
376 <eClassifiers xsi:type="ecore:EClass" name="RederivableNodeRecipe" abstract="true">
377 <eStructuralFeatures xsi:type="ecore:EAttribute" name="deleteRederiveEvaluation"
378 eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="false"/>
379 <eStructuralFeatures xsi:type="ecore:EReference" name="optionalMonotonicityInfo"
380 eType="#//MonotonicityInfo" containment="true"/>
381 </eClassifiers>
382 <eClassifiers xsi:type="ecore:EClass" name="RelationEvaluationRecipe" eSuperTypes="#//MultiParentNodeRecipe">
383 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
384 <details key="documentation" value="@since 2.8"/>
385 </eAnnotations>
386 <eStructuralFeatures xsi:type="ecore:EReference" name="evaluator" eType="#//ExpressionDefinition"/>
387 </eClassifiers>
388 <eClassifiers xsi:type="ecore:EClass" name="RepresentativeElectionRecipe" eSuperTypes="#//AlphaRecipe">
389 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
390 <details key="documentation" value="Represents represenative election."/>
391 </eAnnotations>
392 <eOperations name="getArity" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt">
393 <eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
394 <details key="body" value="return 2;"/>
395 </eAnnotations>
396 </eOperations>
397 <eStructuralFeatures xsi:type="ecore:EAttribute" name="connectivity" eType="#//Connectivity"/>
398 </eClassifiers>
399 <eClassifiers xsi:type="ecore:EDataType" name="Connectivity" instanceClassName="tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.Connectivity"/>
400</ecore:EPackage>
diff --git a/subprojects/viatra-runtime-rete-recipes/src/main/resources/model/recipes.ecore.license b/subprojects/viatra-runtime-rete-recipes/src/main/resources/model/recipes.ecore.license
new file mode 100644
index 00000000..03d1d42b
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/src/main/resources/model/recipes.ecore.license
@@ -0,0 +1,4 @@
1Copyright (c) 2004-2014 Gabor Bergmann and Daniel Varro
2Copyright (c) 2023 The Refinery Authors <https://refinery.tools>
3
4SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/viatra-runtime-rete-recipes/src/main/resources/model/rete-recipes.genmodel b/subprojects/viatra-runtime-rete-recipes/src/main/resources/model/rete-recipes.genmodel
new file mode 100644
index 00000000..6b44fde6
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/src/main/resources/model/rete-recipes.genmodel
@@ -0,0 +1,171 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<genmodel:GenModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
3 xmlns:genmodel="http://www.eclipse.org/emf/2002/GenModel" copyrightText="Copyright (c) 2004-2014 Gabor Bergmann and Daniel Varro&#xA;Copyright (c) 2023 The Refinery Authors &lt;https://refinery.tools>&#xA;This program and the accompanying materials are made available under the&#xA;terms of the Eclipse Public License v. 2.0 which is available at&#xA;http://www.eclipse.org/legal/epl-v20.html.&#xA;&#xA;SPDX-License-Identifier: EPL-2.0"
4 modelDirectory="/tools.refinery.refinery-viatra-runtime-rete-recipes/src/main/emf-gen"
5 modelPluginID="viatra-runtime-rete-recipes" runtimeJar="true" forceOverwrite="true"
6 modelName="Rete-recipes" updateClasspath="false" nonNLSMarkers="true" rootExtendsClass="org.eclipse.emf.ecore.impl.MinimalEObjectImpl$Container"
7 testsDirectory="" importerID="org.eclipse.emf.importer.ecore" containmentProxies="true"
8 complianceLevel="7.0" language="en" operationReflection="true">
9 <genAnnotations source="http://www.eclipse.org/emf/2002/GenModel/exporter/org.eclipse.xsd.ecore.exporter">
10 <genAnnotations source="selectedPackages">
11 <details key="http://www.eclipse.org/emf/2002/Ecore" value="Ecore.xsd"/>
12 </genAnnotations>
13 <details key="directoryURI" value="."/>
14 </genAnnotations>
15 <genAnnotations source="http://www.eclipse.org/emf/2002/GenModel/exporter/org.eclipse.xsd.ecore.exporter.xmi">
16 <genAnnotations source="selectedPackages">
17 <details key="http://www.eclipse.org/emf/2002/Ecore" value="EcoreXMI.xsd"/>
18 </genAnnotations>
19 <details key="directoryURI" value="."/>
20 </genAnnotations>
21 <foreignModel>recipes.ecore</foreignModel>
22 <modelPluginVariables>org.eclipse.xtext.xbase.lib</modelPluginVariables>
23 <modelPluginVariables>org.eclipse.emf.ecore.xcore.lib</modelPluginVariables>
24 <genPackages prefix="Recipes" basePackage="tools.refinery.viatra.runtime.rete" disposableProviderFactory="true"
25 ecorePackage="recipes.ecore#/">
26 <genDataTypes ecoreDataType="recipes.ecore#//Index"/>
27 <genDataTypes ecoreDataType="recipes.ecore#//AggregationOperator"/>
28 <genDataTypes ecoreDataType="recipes.ecore#//Connectivity"/>
29 <genClasses ecoreClass="recipes.ecore#//ReteRecipe">
30 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//ReteRecipe/recipeNodes"/>
31 </genClasses>
32 <genClasses image="false" ecoreClass="recipes.ecore#//ReteNodeRecipe">
33 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//ReteNodeRecipe/traceInfo"/>
34 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//ReteNodeRecipe/equivalenceClassIDs"/>
35 <genOperations ecoreOperation="recipes.ecore#//ReteNodeRecipe/getArity" body="throw new &lt;%java.lang.UnsupportedOperationException%>();"/>
36 </genClasses>
37 <genClasses image="false" ecoreClass="recipes.ecore#//SingleParentNodeRecipe">
38 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference recipes.ecore#//SingleParentNodeRecipe/parent"/>
39 </genClasses>
40 <genClasses image="false" ecoreClass="recipes.ecore#//AlphaRecipe"/>
41 <genClasses image="false" ecoreClass="recipes.ecore#//MultiParentNodeRecipe">
42 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference recipes.ecore#//MultiParentNodeRecipe/parents"/>
43 <genOperations ecoreOperation="recipes.ecore#//MultiParentNodeRecipe/getArity"
44 body="&lt;%org.eclipse.emf.common.util.EList%>&lt;&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%>> _parents = this.getParents();&#xA;&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%> _get = _parents.get(0);&#xA;return _get.getArity();"/>
45 </genClasses>
46 <genClasses ecoreClass="recipes.ecore#//MonotonicityInfo">
47 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//MonotonicityInfo/coreMask"/>
48 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//MonotonicityInfo/posetMask"/>
49 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//MonotonicityInfo/posetComparator"/>
50 </genClasses>
51 <genClasses ecoreClass="recipes.ecore#//UniquenessEnforcerRecipe"/>
52 <genClasses ecoreClass="recipes.ecore#//ProductionRecipe">
53 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//ProductionRecipe/mappedIndices"/>
54 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//ProductionRecipe/pattern"/>
55 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//ProductionRecipe/patternFQN"/>
56 <genOperations ecoreOperation="recipes.ecore#//ProductionRecipe/getArity" body="return this.getMappedIndices().size();"/>
57 </genClasses>
58 <genClasses image="false" ecoreClass="recipes.ecore#//IndexerRecipe">
59 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//IndexerRecipe/mask"/>
60 <genOperations ecoreOperation="recipes.ecore#//IndexerRecipe/getArity" body="&lt;%tools.refinery.viatra.runtime.rete.recipes.Mask%> _mask = this.getMask();&#xA;return _mask.getSourceArity();"/>
61 </genClasses>
62 <genClasses ecoreClass="recipes.ecore#//ProjectionIndexerRecipe"/>
63 <genClasses ecoreClass="recipes.ecore#//AggregatorIndexerRecipe"/>
64 <genClasses image="false" ecoreClass="recipes.ecore#//BetaRecipe">
65 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//BetaRecipe/leftParent"/>
66 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//BetaRecipe/rightParent"/>
67 </genClasses>
68 <genClasses ecoreClass="recipes.ecore#//Mask">
69 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//Mask/sourceIndices"/>
70 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//Mask/sourceArity"/>
71 </genClasses>
72 <genClasses ecoreClass="recipes.ecore#//StringIndexMapEntry">
73 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//StringIndexMapEntry/key"/>
74 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//StringIndexMapEntry/value"/>
75 </genClasses>
76 <genClasses ecoreClass="recipes.ecore#//InputRecipe">
77 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//InputRecipe/inputKey"/>
78 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//InputRecipe/keyID"/>
79 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//InputRecipe/keyArity"/>
80 <genOperations ecoreOperation="recipes.ecore#//InputRecipe/getArity" body="return getKeyArity();"/>
81 </genClasses>
82 <genClasses ecoreClass="recipes.ecore#//ConstantRecipe">
83 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//ConstantRecipe/constantValues"/>
84 <genOperations ecoreOperation="recipes.ecore#//ConstantRecipe/getArity" body="return this.getConstantValues().size();"/>
85 </genClasses>
86 <genClasses ecoreClass="recipes.ecore#//TransitiveClosureRecipe">
87 <genOperations ecoreOperation="recipes.ecore#//TransitiveClosureRecipe/getArity"
88 body="return 2;"/>
89 </genClasses>
90 <genClasses image="false" ecoreClass="recipes.ecore#//FilterRecipe">
91 <genOperations ecoreOperation="recipes.ecore#//FilterRecipe/getArity" body="&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%> _parent = this.getParent();&#xA;return _parent.getArity();"/>
92 </genClasses>
93 <genClasses ecoreClass="recipes.ecore#//InequalityFilterRecipe">
94 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//InequalityFilterRecipe/subject"/>
95 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//InequalityFilterRecipe/inequals"/>
96 </genClasses>
97 <genClasses ecoreClass="recipes.ecore#//EqualityFilterRecipe">
98 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//EqualityFilterRecipe/indices"/>
99 </genClasses>
100 <genClasses ecoreClass="recipes.ecore#//TransparentRecipe"/>
101 <genClasses ecoreClass="recipes.ecore#//TrimmerRecipe">
102 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//TrimmerRecipe/mask"/>
103 <genOperations ecoreOperation="recipes.ecore#//TrimmerRecipe/getArity" body="&lt;%tools.refinery.viatra.runtime.rete.recipes.Mask%> _mask = this.getMask();&#xA;&lt;%org.eclipse.emf.common.util.EList%>&lt;&lt;%java.lang.Integer%>> _sourceIndices = _mask.getSourceIndices();&#xA;return _sourceIndices.size();"/>
104 </genClasses>
105 <genClasses ecoreClass="recipes.ecore#//ExpressionDefinition">
106 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//ExpressionDefinition/evaluator"/>
107 </genClasses>
108 <genClasses image="false" ecoreClass="recipes.ecore#//ExpressionEnforcerRecipe">
109 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//ExpressionEnforcerRecipe/expression"/>
110 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//ExpressionEnforcerRecipe/mappedIndices"/>
111 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//ExpressionEnforcerRecipe/cacheOutput"/>
112 </genClasses>
113 <genClasses ecoreClass="recipes.ecore#//CheckRecipe">
114 <genOperations ecoreOperation="recipes.ecore#//CheckRecipe/getArity" body="&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%> _parent = this.getParent();&#xA;return _parent.getArity();"/>
115 </genClasses>
116 <genClasses ecoreClass="recipes.ecore#//EvalRecipe">
117 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//EvalRecipe/unwinding"/>
118 <genOperations ecoreOperation="recipes.ecore#//EvalRecipe/getArity" body="&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%> _parent = this.getParent();&#xA;int _arity = _parent.getArity();&#xA;return (1 + _arity);"/>
119 </genClasses>
120 <genClasses image="false" ecoreClass="recipes.ecore#//IndexerBasedAggregatorRecipe">
121 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//IndexerBasedAggregatorRecipe/parent"/>
122 <genOperations ecoreOperation="recipes.ecore#//IndexerBasedAggregatorRecipe/getArity"
123 body="&lt;%tools.refinery.viatra.runtime.rete.recipes.ProjectionIndexerRecipe%> _parent = this.getParent();&#xA;&lt;%tools.refinery.viatra.runtime.rete.recipes.Mask%> _mask = _parent.getMask();&#xA;&lt;%org.eclipse.emf.common.util.EList%>&lt;&lt;%java.lang.Integer%>> _sourceIndices = _mask.getSourceIndices();&#xA;int _size = _sourceIndices.size();&#xA;return (1 + _size);"/>
124 </genClasses>
125 <genClasses ecoreClass="recipes.ecore#//CountAggregatorRecipe"/>
126 <genClasses ecoreClass="recipes.ecore#//JoinRecipe">
127 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//JoinRecipe/rightParentComplementaryMask"/>
128 <genOperations ecoreOperation="recipes.ecore#//JoinRecipe/getArity" body="&lt;%tools.refinery.viatra.runtime.rete.recipes.ProjectionIndexerRecipe%> _leftParent = this.getLeftParent();&#xA;int _arity = _leftParent.getArity();&#xA;&lt;%tools.refinery.viatra.runtime.rete.recipes.IndexerRecipe%> _rightParent = this.getRightParent();&#xA;int _arity_1 = _rightParent.getArity();&#xA;int _plus = (_arity + _arity_1);&#xA;&lt;%tools.refinery.viatra.runtime.rete.recipes.IndexerRecipe%> _rightParent_1 = this.getRightParent();&#xA;&lt;%tools.refinery.viatra.runtime.rete.recipes.Mask%> _mask = _rightParent_1.getMask();&#xA;&lt;%org.eclipse.emf.common.util.EList%>&lt;&lt;%java.lang.Integer%>> _sourceIndices = _mask.getSourceIndices();&#xA;int _size = _sourceIndices.size();&#xA;return (_plus - _size);"/>
129 </genClasses>
130 <genClasses image="false" ecoreClass="recipes.ecore#//ExistenceJoinRecipe">
131 <genOperations ecoreOperation="recipes.ecore#//ExistenceJoinRecipe/getArity"
132 body="&lt;%tools.refinery.viatra.runtime.rete.recipes.ProjectionIndexerRecipe%> _leftParent = this.getLeftParent();&#xA;return _leftParent.getArity();"/>
133 </genClasses>
134 <genClasses ecoreClass="recipes.ecore#//SemiJoinRecipe"/>
135 <genClasses ecoreClass="recipes.ecore#//AntiJoinRecipe"/>
136 <genClasses ecoreClass="recipes.ecore#//InputFilterRecipe">
137 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//InputFilterRecipe/inputKey"/>
138 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//InputFilterRecipe/keyID"/>
139 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//InputFilterRecipe/mask"/>
140 </genClasses>
141 <genClasses ecoreClass="recipes.ecore#//SingleColumnAggregatorRecipe">
142 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//SingleColumnAggregatorRecipe/multisetAggregationOperator"/>
143 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//SingleColumnAggregatorRecipe/aggregableIndex"/>
144 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//SingleColumnAggregatorRecipe/groupByMask"/>
145 <genOperations ecoreOperation="recipes.ecore#//SingleColumnAggregatorRecipe/getArity"
146 body="&lt;%tools.refinery.viatra.runtime.rete.recipes.MonotonicityInfo%> info = getOptionalMonotonicityInfo();&#xA;if (info == null) {&#xA;&#x9;return 1 + getGroupByMask().getSourceIndices().size();&#xA;} else {&#x9;&#xA;&#x9;return info.getCoreMask().getSourceIndices().size() + info.getPosetMask().getSourceIndices().size();&#xA;}"/>
147 </genClasses>
148 <genClasses ecoreClass="recipes.ecore#//DiscriminatorDispatcherRecipe">
149 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//DiscriminatorDispatcherRecipe/discriminationColumnIndex"/>
150 <genOperations ecoreOperation="recipes.ecore#//DiscriminatorDispatcherRecipe/getArity"
151 body="&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%> _parent = this.getParent();&#xA;return _parent.getArity();"/>
152 </genClasses>
153 <genClasses ecoreClass="recipes.ecore#//DiscriminatorBucketRecipe">
154 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//DiscriminatorBucketRecipe/bucketKey"/>
155 <genOperations ecoreOperation="recipes.ecore#//DiscriminatorBucketRecipe/getArity"
156 body="&lt;%tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe%> _parent = this.getParent();&#xA;return _parent.getArity();"/>
157 </genClasses>
158 <genClasses image="false" ecoreClass="recipes.ecore#//RederivableNodeRecipe">
159 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//RederivableNodeRecipe/deleteRederiveEvaluation"/>
160 <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference recipes.ecore#//RederivableNodeRecipe/optionalMonotonicityInfo"/>
161 </genClasses>
162 <genClasses ecoreClass="recipes.ecore#//RelationEvaluationRecipe">
163 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference recipes.ecore#//RelationEvaluationRecipe/evaluator"/>
164 </genClasses>
165 <genClasses ecoreClass="recipes.ecore#//RepresentativeElectionRecipe">
166 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute recipes.ecore#//RepresentativeElectionRecipe/connectivity"/>
167 <genOperations ecoreOperation="recipes.ecore#//RepresentativeElectionRecipe/getArity"
168 body="return 2;"/>
169 </genClasses>
170 </genPackages>
171</genmodel:GenModel>
diff --git a/subprojects/viatra-runtime-rete-recipes/src/main/resources/model/rete-recipes.genmodel.license b/subprojects/viatra-runtime-rete-recipes/src/main/resources/model/rete-recipes.genmodel.license
new file mode 100644
index 00000000..03d1d42b
--- /dev/null
+++ b/subprojects/viatra-runtime-rete-recipes/src/main/resources/model/rete-recipes.genmodel.license
@@ -0,0 +1,4 @@
1Copyright (c) 2004-2014 Gabor Bergmann and Daniel Varro
2Copyright (c) 2023 The Refinery Authors <https://refinery.tools>
3
4SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/viatra-runtime-rete/about.html b/subprojects/viatra-runtime-rete/about.html
new file mode 100644
index 00000000..d1d5593a
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/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-rete/build.gradle.kts b/subprojects/viatra-runtime-rete/build.gradle.kts
new file mode 100644
index 00000000..7e795a90
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/build.gradle.kts
@@ -0,0 +1,15 @@
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 implementation(project(":refinery-viatra-runtime"))
13 implementation(project(":refinery-viatra-runtime-rete-recipes"))
14 implementation(libs.slf4j.log4j)
15}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/AbstractColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/AbstractColumnAggregatorNode.java
new file mode 100644
index 00000000..2588bde1
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/AbstractColumnAggregatorNode.java
@@ -0,0 +1,474 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.rete.aggregation;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Objects;
16
17import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
18import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
20import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
22import tools.refinery.viatra.runtime.matchers.util.Clearable;
23import tools.refinery.viatra.runtime.matchers.util.Direction;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
25import tools.refinery.viatra.runtime.rete.index.Indexer;
26import tools.refinery.viatra.runtime.rete.index.StandardIndexer;
27import tools.refinery.viatra.runtime.rete.network.Node;
28import tools.refinery.viatra.runtime.rete.network.Receiver;
29import tools.refinery.viatra.runtime.rete.network.ReteContainer;
30import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
31import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
32import tools.refinery.viatra.runtime.rete.single.SingleInputNode;
33
34/**
35 * Groups incoming tuples by the given mask, and aggregates values at a specific index in each group.
36 * <p>
37 * Direct children are not supported, use via outer join indexers instead.
38 * <p>
39 * There are both timeless and timely implementations.
40 *
41 * @author Tamas Szabo
42 * @since 2.2
43 *
44 */
45public abstract class AbstractColumnAggregatorNode<Domain, Accumulator, AggregateResult> extends SingleInputNode
46 implements Clearable, IAggregatorNode {
47
48 /**
49 * @since 1.6
50 */
51 protected final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator;
52
53 /**
54 * @since 1.6
55 */
56 protected final TupleMask groupMask;
57
58 /**
59 * @since 1.6
60 */
61 protected final TupleMask columnMask;
62
63 /**
64 * @since 1.6
65 */
66 protected final int sourceWidth;
67
68 /**
69 * @since 1.6
70 */
71 protected final IQueryRuntimeContext runtimeContext;
72
73 protected final AggregateResult NEUTRAL;
74
75 protected AggregatorOuterIndexer aggregatorOuterIndexer;
76
77 @SuppressWarnings("rawtypes")
78 protected AbstractColumnAggregatorNode.AggregatorOuterIdentityIndexer[] aggregatorOuterIdentityIndexers;
79
80 /**
81 * Creates a new column aggregator node.
82 *
83 * @param reteContainer
84 * the RETE container of the node
85 * @param operator
86 * the aggregation operator
87 * @param deleteRederiveEvaluation
88 * true if the node should run in DRED mode, false otherwise
89 * @param groupMask
90 * the mask that masks a tuple to obtain the key that we are grouping-by
91 * @param columnMask
92 * the mask that masks a tuple to obtain the tuple element(s) that we are aggregating over
93 * @param posetComparator
94 * the poset comparator for the column, if known, otherwise it can be null
95 * @since 1.6
96 */
97 public AbstractColumnAggregatorNode(final ReteContainer reteContainer,
98 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
99 final TupleMask groupMask, final TupleMask columnMask) {
100 super(reteContainer);
101 this.operator = operator;
102 this.groupMask = groupMask;
103 this.columnMask = columnMask;
104 this.sourceWidth = groupMask.indices.length;
105 this.runtimeContext = reteContainer.getNetwork().getEngine().getRuntimeContext();
106 this.NEUTRAL = operator.getAggregate(operator.createNeutral());
107 reteContainer.registerClearable(this);
108 }
109
110 /**
111 * Creates a new column aggregator node.
112 *
113 * @param reteContainer
114 * the RETE container of the node
115 * @param operator
116 * the aggregation operator
117 * @param groupMask
118 * the mask that masks a tuple to obtain the key that we are grouping-by
119 * @param aggregatedColumn
120 * the index of the column that the aggregator node is aggregating over
121 */
122 public AbstractColumnAggregatorNode(final ReteContainer reteContainer,
123 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
124 final TupleMask groupMask, final int aggregatedColumn) {
125 this(reteContainer, operator, groupMask, TupleMask.selectSingle(aggregatedColumn, groupMask.sourceWidth));
126 }
127
128 @Override
129 public CommunicationTracker getCommunicationTracker() {
130 return this.reteContainer.getCommunicationTracker();
131 }
132
133 @Override
134 public void pullInto(Collection<Tuple> collector, boolean flush) {
135 // DIRECT CHILDREN NOT SUPPORTED
136 throw new UnsupportedOperationException();
137 }
138
139 @Override
140 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
141 // DIRECT CHILDREN NOT SUPPORTED
142 throw new UnsupportedOperationException();
143 }
144
145 @Override
146 public void appendChild(Receiver receiver) {
147 // DIRECT CHILDREN NOT SUPPORTED
148 throw new UnsupportedOperationException();
149 }
150
151 @Override
152 public Indexer getAggregatorOuterIndexer() {
153 if (aggregatorOuterIndexer == null) {
154 aggregatorOuterIndexer = new AggregatorOuterIndexer();
155 this.getCommunicationTracker().registerDependency(this, aggregatorOuterIndexer);
156 }
157 return aggregatorOuterIndexer;
158 }
159
160 @Override
161 public Indexer getAggregatorOuterIdentityIndexer(final int resultPositionInSignature) {
162 if (aggregatorOuterIdentityIndexers == null) {
163 aggregatorOuterIdentityIndexers = new AbstractColumnAggregatorNode.AggregatorOuterIdentityIndexer[sourceWidth
164 + 1];
165 }
166 if (aggregatorOuterIdentityIndexers[resultPositionInSignature] == null) {
167 aggregatorOuterIdentityIndexers[resultPositionInSignature] = new AggregatorOuterIdentityIndexer(
168 resultPositionInSignature);
169 this.getCommunicationTracker().registerDependency(this,
170 aggregatorOuterIdentityIndexers[resultPositionInSignature]);
171 }
172 return aggregatorOuterIdentityIndexers[resultPositionInSignature];
173 }
174
175 /**
176 * @since 2.4
177 */
178 public void propagateAggregateResultUpdate(final Tuple group, final AggregateResult oldValue,
179 final AggregateResult newValue, final Timestamp timestamp) {
180 if (!Objects.equals(oldValue, newValue)) {
181 propagate(Direction.DELETE, group, oldValue, timestamp);
182 propagate(Direction.INSERT, group, newValue, timestamp);
183 }
184 }
185
186 /**
187 * @since 2.4
188 */
189 @SuppressWarnings("unchecked")
190 public void propagate(final Direction direction, final Tuple group, final AggregateResult value,
191 final Timestamp timestamp) {
192 final Tuple tuple = tupleFromAggregateResult(group, value);
193
194 if (aggregatorOuterIndexer != null) {
195 aggregatorOuterIndexer.propagate(direction, tuple, group, timestamp);
196 }
197 if (aggregatorOuterIdentityIndexers != null) {
198 for (final AggregatorOuterIdentityIndexer aggregatorOuterIdentityIndexer : aggregatorOuterIdentityIndexers) {
199 if (aggregatorOuterIdentityIndexer != null) {
200 aggregatorOuterIdentityIndexer.propagate(direction, tuple, group, timestamp);
201 }
202 }
203 }
204 }
205
206 public abstract Tuple getAggregateTuple(final Tuple key);
207
208 /**
209 * @since 2.4
210 */
211 public abstract Map<Tuple, Timeline<Timestamp>> getAggregateTupleTimeline(final Tuple key);
212
213 public abstract AggregateResult getAggregateResult(final Tuple key);
214
215 /**
216 * @since 2.4
217 */
218 public abstract Map<AggregateResult, Timeline<Timestamp>> getAggregateResultTimeline(final Tuple key);
219
220 protected Tuple tupleFromAggregateResult(final Tuple groupTuple, final AggregateResult aggregateResult) {
221 if (aggregateResult == null) {
222 return null;
223 } else {
224 return Tuples.staticArityLeftInheritanceTupleOf(groupTuple, runtimeContext.wrapElement(aggregateResult));
225 }
226 }
227
228 /**
229 * A special non-iterable index that retrieves the aggregated, packed result (signature+aggregate) for the original
230 * signature.
231 *
232 * @author Gabor Bergmann
233 * @author Tamas Szabo
234 *
235 */
236 protected class AggregatorOuterIndexer extends StandardIndexer {
237
238 /**
239 * @since 2.4
240 */
241 protected NetworkStructureChangeSensitiveLogic logic;
242
243 public AggregatorOuterIndexer() {
244 super(AbstractColumnAggregatorNode.this.reteContainer, TupleMask.omit(sourceWidth, sourceWidth + 1));
245 this.parent = AbstractColumnAggregatorNode.this;
246 this.logic = createLogic();
247 }
248
249 @Override
250 public void networkStructureChanged() {
251 super.networkStructureChanged();
252 this.logic = createLogic();
253 }
254
255 @Override
256 public Collection<Tuple> get(final Tuple signature) {
257 return this.logic.get(signature);
258 }
259
260 @Override
261 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
262 return this.logic.getTimeline(signature);
263 }
264
265 /**
266 * @since 2.4
267 */
268 public void propagate(final Direction direction, final Tuple tuple, final Tuple group,
269 final Timestamp timestamp) {
270 if (tuple != null) {
271 propagate(direction, tuple, group, true, timestamp);
272 }
273 }
274
275 @Override
276 public Node getActiveNode() {
277 return AbstractColumnAggregatorNode.this;
278 }
279
280 /**
281 * @since 2.4
282 */
283 protected NetworkStructureChangeSensitiveLogic createLogic() {
284 if (this.reteContainer.isTimelyEvaluation()
285 && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
286 return this.TIMELY;
287 } else {
288 return this.TIMELESS;
289 }
290 }
291
292 private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() {
293
294 @Override
295 public Collection<Tuple> get(final Tuple signature) {
296 final Tuple aggregateTuple = getAggregateTuple(signature);
297 if (aggregateTuple == null) {
298 return null;
299 } else {
300 return Collections.singleton(aggregateTuple);
301 }
302 }
303
304 @Override
305 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
306 throw new UnsupportedOperationException();
307 }
308
309 };
310
311 private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() {
312
313 @Override
314 public Collection<Tuple> get(final Tuple signatureWithResult) {
315 return TIMELESS.get(signatureWithResult);
316 }
317
318 @Override
319 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
320 final Map<Tuple, Timeline<Timestamp>> aggregateTuples = getAggregateTupleTimeline(signature);
321 if (aggregateTuples.isEmpty()) {
322 return null;
323 } else {
324 return aggregateTuples;
325 }
326 }
327
328 };
329
330 }
331
332 /**
333 * A special non-iterable index that checks a suspected aggregate value for a given signature. The signature for
334 * this index is the original 'group by' masked tuple, with the suspected result inserted at position
335 * resultPositionInSignature.
336 *
337 * @author Gabor Bergmann
338 * @author Tamas Szabo
339 *
340 */
341 protected class AggregatorOuterIdentityIndexer extends StandardIndexer {
342
343 protected final int resultPositionInSignature;
344 protected final TupleMask pruneResult;
345 protected final TupleMask reorderMask;
346 /**
347 * @since 2.4
348 */
349 protected NetworkStructureChangeSensitiveLogic logic;
350
351 public AggregatorOuterIdentityIndexer(final int resultPositionInSignature) {
352 super(AbstractColumnAggregatorNode.this.reteContainer,
353 TupleMask.displace(sourceWidth, resultPositionInSignature, sourceWidth + 1));
354 this.resultPositionInSignature = resultPositionInSignature;
355 this.pruneResult = TupleMask.omit(resultPositionInSignature, sourceWidth + 1);
356 if (resultPositionInSignature == sourceWidth) {
357 this.reorderMask = null;
358 } else {
359 this.reorderMask = mask;
360 }
361 this.logic = createLogic();
362 }
363
364 @Override
365 public void networkStructureChanged() {
366 super.networkStructureChanged();
367 this.logic = createLogic();
368 }
369
370 @Override
371 public Collection<Tuple> get(final Tuple signatureWithResult) {
372 return this.logic.get(signatureWithResult);
373 }
374
375 /**
376 * @since 2.4
377 */
378 @Override
379 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
380 return this.logic.getTimeline(signature);
381 }
382
383 /**
384 * @since 2.4
385 */
386 public void propagate(final Direction direction, final Tuple tuple, final Tuple group,
387 final Timestamp timestamp) {
388 if (tuple != null) {
389 propagate(direction, reorder(tuple), group, true, timestamp);
390 }
391 }
392
393 private Tuple reorder(final Tuple signatureWithResult) {
394 Tuple transformed;
395 if (reorderMask == null) {
396 transformed = signatureWithResult;
397 } else {
398 transformed = reorderMask.transform(signatureWithResult);
399 }
400 return transformed;
401 }
402
403 @Override
404 public Node getActiveNode() {
405 return this.parent;
406 }
407
408 /**
409 * @since 2.4
410 */
411 protected NetworkStructureChangeSensitiveLogic createLogic() {
412 if (this.reteContainer.isTimelyEvaluation()
413 && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
414 return this.TIMELY;
415 } else {
416 return this.TIMELESS;
417 }
418 }
419
420 private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() {
421
422 @Override
423 public Collection<Tuple> get(final Tuple signatureWithResult) {
424 final Tuple prunedSignature = pruneResult.transform(signatureWithResult);
425 final AggregateResult result = getAggregateResult(prunedSignature);
426 if (result != null && Objects.equals(signatureWithResult.get(resultPositionInSignature), result)) {
427 return Collections.singleton(signatureWithResult);
428 } else {
429 return null;
430 }
431 }
432
433 @Override
434 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
435 throw new UnsupportedOperationException();
436 }
437
438 };
439
440 private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() {
441
442 @Override
443 public Collection<Tuple> get(final Tuple signatureWithResult) {
444 return TIMELESS.get(signatureWithResult);
445 }
446
447 @Override
448 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signatureWithResult) {
449 final Tuple prunedSignature = pruneResult.transform(signatureWithResult);
450 final Map<AggregateResult, Timeline<Timestamp>> result = getAggregateResultTimeline(prunedSignature);
451 for (final Entry<AggregateResult, Timeline<Timestamp>> entry : result.entrySet()) {
452 if (Objects.equals(signatureWithResult.get(resultPositionInSignature), entry.getKey())) {
453 return Collections.singletonMap(signatureWithResult, entry.getValue());
454 }
455 }
456 return null;
457 }
458
459 };
460
461 }
462
463 /**
464 * @since 2.4
465 */
466 protected static abstract class NetworkStructureChangeSensitiveLogic {
467
468 public abstract Collection<Tuple> get(final Tuple signatureWithResult);
469
470 public abstract Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature);
471
472 }
473
474}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/ColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/ColumnAggregatorNode.java
new file mode 100644
index 00000000..4480aed8
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/ColumnAggregatorNode.java
@@ -0,0 +1,369 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.rete.aggregation;
10
11import java.util.Map;
12import java.util.Map.Entry;
13
14import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
15import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19import tools.refinery.viatra.runtime.matchers.util.Direction;
20import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
21import tools.refinery.viatra.runtime.rete.network.PosetAwareReceiver;
22import tools.refinery.viatra.runtime.rete.network.RederivableNode;
23import tools.refinery.viatra.runtime.rete.network.ReteContainer;
24import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
25import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
26import tools.refinery.viatra.runtime.rete.network.communication.timeless.RecursiveCommunicationGroup;
27import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
28import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
29import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.PosetAwareMailbox;
30
31/**
32 * Timeless implementation of the column aggregator node.
33 * <p>
34 * The node is capable of operating in the delete and re-derive mode. In this mode, it is also possible to equip the
35 * node with an {@link IPosetComparator} to identify monotone changes; thus, ensuring that a fix-point can be reached
36 * during the evaluation.
37 *
38 * @author Gabor Bergmann
39 * @author Tamas Szabo
40 * @since 1.4
41 */
42public class ColumnAggregatorNode<Domain, Accumulator, AggregateResult>
43 extends AbstractColumnAggregatorNode<Domain, Accumulator, AggregateResult>
44 implements RederivableNode, PosetAwareReceiver {
45
46 /**
47 * @since 1.6
48 */
49 protected final IPosetComparator posetComparator;
50
51 /**
52 * @since 1.6
53 */
54 protected final boolean deleteRederiveEvaluation;
55
56 // invariant: neutral values are not stored
57 /**
58 * @since 1.6
59 */
60 protected final Map<Tuple, Accumulator> memory;
61 /**
62 * @since 1.6
63 */
64 protected final Map<Tuple, Accumulator> rederivableMemory;
65
66 /**
67 * @since 1.7
68 */
69 protected CommunicationGroup currentGroup;
70
71 /**
72 * Creates a new column aggregator node.
73 *
74 * @param reteContainer
75 * the RETE container of the node
76 * @param operator
77 * the aggregation operator
78 * @param deleteRederiveEvaluation
79 * true if the node should run in DRED mode, false otherwise
80 * @param groupMask
81 * the mask that masks a tuple to obtain the key that we are grouping-by
82 * @param columnMask
83 * the mask that masks a tuple to obtain the tuple element(s) that we are aggregating over
84 * @param posetComparator
85 * the poset comparator for the column, if known, otherwise it can be null
86 * @since 1.6
87 */
88 public ColumnAggregatorNode(final ReteContainer reteContainer,
89 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
90 final boolean deleteRederiveEvaluation, final TupleMask groupMask, final TupleMask columnMask,
91 final IPosetComparator posetComparator) {
92 super(reteContainer, operator, groupMask, columnMask);
93 this.memory = CollectionsFactory.createMap();
94 this.rederivableMemory = CollectionsFactory.createMap();
95 this.deleteRederiveEvaluation = deleteRederiveEvaluation;
96 this.posetComparator = posetComparator;
97 // mailbox MUST be instantiated after the fields are all set
98 this.mailbox = instantiateMailbox();
99 }
100
101 /**
102 * Creates a new column aggregator node.
103 *
104 * @param reteContainer
105 * the RETE container of the node
106 * @param operator
107 * the aggregation operator
108 * @param groupMask
109 * the mask that masks a tuple to obtain the key that we are grouping-by
110 * @param aggregatedColumn
111 * the index of the column that the aggregator node is aggregating over
112 */
113 public ColumnAggregatorNode(final ReteContainer reteContainer,
114 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
115 final TupleMask groupMask, final int aggregatedColumn) {
116 this(reteContainer, operator, false, groupMask, TupleMask.selectSingle(aggregatedColumn, groupMask.sourceWidth),
117 null);
118 }
119
120 @Override
121 public boolean isInDRedMode() {
122 return this.deleteRederiveEvaluation;
123 }
124
125 @Override
126 protected Mailbox instantiateMailbox() {
127 if (groupMask != null && columnMask != null && posetComparator != null) {
128 return new PosetAwareMailbox(this, this.reteContainer);
129 } else {
130 return new BehaviorChangingMailbox(this, this.reteContainer);
131 }
132 }
133
134 @Override
135 public TupleMask getCoreMask() {
136 return groupMask;
137 }
138
139 @Override
140 public TupleMask getPosetMask() {
141 return columnMask;
142 }
143
144 @Override
145 public IPosetComparator getPosetComparator() {
146 return posetComparator;
147 }
148
149 @Override
150 public void rederiveOne() {
151 final Entry<Tuple, Accumulator> entry = rederivableMemory.entrySet().iterator().next();
152 final Tuple group = entry.getKey();
153 final Accumulator accumulator = entry.getValue();
154 rederivableMemory.remove(group);
155 memory.put(group, accumulator);
156 // unregister the node if there is nothing left to be re-derived
157 if (this.rederivableMemory.isEmpty()) {
158 ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this);
159 }
160 final AggregateResult value = operator.getAggregate(accumulator);
161 propagateAggregateResultUpdate(group, NEUTRAL, value, Timestamp.ZERO);
162 }
163
164 @Override
165 public void updateWithPosetInfo(final Direction direction, final Tuple update, final boolean monotone) {
166 if (this.deleteRederiveEvaluation) {
167 updateWithDeleteAndRederive(direction, update, monotone);
168 } else {
169 updateDefault(direction, update, Timestamp.ZERO);
170 }
171 }
172
173 @Override
174 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
175 updateWithPosetInfo(direction, update, false);
176 }
177
178 /**
179 * @since 2.4
180 */
181 protected void updateDefault(final Direction direction, final Tuple update, final Timestamp timestamp) {
182 final Tuple key = groupMask.transform(update);
183 final Tuple value = columnMask.transform(update);
184 @SuppressWarnings("unchecked")
185 final Domain aggregableValue = (Domain) runtimeContext.unwrapElement(value.get(0));
186 final boolean isInsertion = direction == Direction.INSERT;
187
188 final Accumulator oldMainAccumulator = getMainAccumulator(key);
189 final AggregateResult oldValue = operator.getAggregate(oldMainAccumulator);
190
191 final Accumulator newMainAccumulator = operator.update(oldMainAccumulator, aggregableValue, isInsertion);
192 storeIfNotNeutral(key, newMainAccumulator, memory);
193 final AggregateResult newValue = operator.getAggregate(newMainAccumulator);
194
195 propagateAggregateResultUpdate(key, oldValue, newValue, timestamp);
196 }
197
198 /**
199 * @since 2.4
200 */
201 protected void updateWithDeleteAndRederive(final Direction direction, final Tuple update, final boolean monotone) {
202 final Tuple group = groupMask.transform(update);
203 final Tuple value = columnMask.transform(update);
204 @SuppressWarnings("unchecked")
205 final Domain aggregableValue = (Domain) runtimeContext.unwrapElement(value.get(0));
206 final boolean isInsertion = direction == Direction.INSERT;
207
208 Accumulator oldMainAccumulator = memory.get(group);
209 Accumulator oldRederivableAccumulator = rederivableMemory.get(group);
210
211 if (direction == Direction.INSERT) {
212 // INSERT
213 if (oldRederivableAccumulator != null) {
214 // the group is in the re-derivable memory
215 final Accumulator newRederivableAccumulator = operator.update(oldRederivableAccumulator,
216 aggregableValue, isInsertion);
217 storeIfNotNeutral(group, newRederivableAccumulator, rederivableMemory);
218 if (rederivableMemory.isEmpty()) {
219 // there is nothing left to be re-derived
220 // this can happen if the accumulator became neutral in response to the INSERT
221 ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this);
222 }
223 } else {
224 // the group is in the main memory
225 // at this point, it can happen that we need to initialize with a neutral accumulator
226 if (oldMainAccumulator == null) {
227 oldMainAccumulator = operator.createNeutral();
228 }
229
230 final AggregateResult oldValue = operator.getAggregate(oldMainAccumulator);
231 final Accumulator newMainAccumulator = operator.update(oldMainAccumulator, aggregableValue,
232 isInsertion);
233 storeIfNotNeutral(group, newMainAccumulator, memory);
234 final AggregateResult newValue = operator.getAggregate(newMainAccumulator);
235 propagateAggregateResultUpdate(group, oldValue, newValue, Timestamp.ZERO);
236 }
237 } else {
238 // DELETE
239 if (oldRederivableAccumulator != null) {
240 // the group is in the re-derivable memory
241 if (oldMainAccumulator != null) {
242 issueError("[INTERNAL ERROR] Inconsistent state for " + update
243 + " because it is present both in the main and re-derivable memory in the ColumnAggregatorNode "
244 + this + " for pattern(s) " + getTraceInfoPatternsEnumerated(), null);
245 }
246 try {
247 final Accumulator newRederivableAccumulator = operator.update(oldRederivableAccumulator,
248 aggregableValue, isInsertion);
249 storeIfNotNeutral(group, newRederivableAccumulator, rederivableMemory);
250 if (rederivableMemory.isEmpty()) {
251 // there is nothing left to be re-derived
252 // this can happen if the accumulator became neutral in response to the DELETE
253 ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this);
254 }
255 } catch (final NullPointerException ex) {
256 issueError("[INTERNAL ERROR] Deleting a domain element in " + update
257 + " which did not exist before in ColumnAggregatorNode " + this + " for pattern(s) "
258 + getTraceInfoPatternsEnumerated(), ex);
259 }
260 } else {
261 // the group is in the main memory
262 // at this point, it can happen that we need to initialize with a neutral accumulator
263 if (oldMainAccumulator == null) {
264 oldMainAccumulator = operator.createNeutral();
265 }
266
267 final AggregateResult oldValue = operator.getAggregate(oldMainAccumulator);
268 final Accumulator newMainAccumulator = operator.update(oldMainAccumulator, aggregableValue,
269 isInsertion);
270 final AggregateResult newValue = operator.getAggregate(newMainAccumulator);
271
272 if (monotone) {
273 storeIfNotNeutral(group, newMainAccumulator, memory);
274 propagateAggregateResultUpdate(group, oldValue, newValue, Timestamp.ZERO);
275 } else {
276 final boolean wasEmpty = rederivableMemory.isEmpty();
277 if (storeIfNotNeutral(group, newMainAccumulator, rederivableMemory) && wasEmpty) {
278 ((RecursiveCommunicationGroup) currentGroup).addRederivable(this);
279 }
280 memory.remove(group);
281 propagateAggregateResultUpdate(group, oldValue, NEUTRAL, Timestamp.ZERO);
282 }
283 }
284 }
285 }
286
287 @Override
288 public void clear() {
289 this.memory.clear();
290 this.rederivableMemory.clear();
291 this.childMailboxes.clear();
292 }
293
294 /**
295 * Returns true if the accumulator was stored, false otherwise.
296 *
297 * @since 1.6
298 */
299 protected boolean storeIfNotNeutral(final Tuple key, final Accumulator accumulator,
300 final Map<Tuple, Accumulator> memory) {
301 if (operator.isNeutral(accumulator)) {
302 memory.remove(key);
303 return false;
304 } else {
305 memory.put(key, accumulator);
306 return true;
307 }
308 }
309
310 @Override
311 public Tuple getAggregateTuple(final Tuple group) {
312 final Accumulator accumulator = getMainAccumulator(group);
313 final AggregateResult result = operator.getAggregate(accumulator);
314 return tupleFromAggregateResult(group, result);
315 }
316
317 @Override
318 public AggregateResult getAggregateResult(final Tuple group) {
319 final Accumulator accumulator = getMainAccumulator(group);
320 return operator.getAggregate(accumulator);
321 }
322
323 @Override
324 public Map<AggregateResult, Timeline<Timestamp>> getAggregateResultTimeline(Tuple key) {
325 throw new UnsupportedOperationException();
326 }
327
328 @Override
329 public Map<Tuple, Timeline<Timestamp>> getAggregateTupleTimeline(Tuple key) {
330 throw new UnsupportedOperationException();
331 }
332
333 /**
334 * @since 1.6
335 */
336 protected Accumulator getMainAccumulator(final Tuple key) {
337 return getAccumulator(key, memory);
338 }
339
340 /**
341 * @since 1.6
342 */
343 protected Accumulator getRederivableAccumulator(final Tuple key) {
344 return getAccumulator(key, rederivableMemory);
345 }
346
347 /**
348 * @since 1.6
349 */
350 protected Accumulator getAccumulator(final Tuple key, final Map<Tuple, Accumulator> memory) {
351 Accumulator accumulator = memory.get(key);
352 if (accumulator == null) {
353 return operator.createNeutral();
354 } else {
355 return accumulator;
356 }
357 }
358
359 @Override
360 public CommunicationGroup getCurrentGroup() {
361 return currentGroup;
362 }
363
364 @Override
365 public void setCurrentGroup(final CommunicationGroup currentGroup) {
366 this.currentGroup = currentGroup;
367 }
368
369}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/CountNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/CountNode.java
new file mode 100644
index 00000000..7c98de0d
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/CountNode.java
@@ -0,0 +1,38 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2009 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.rete.aggregation;
11
12import java.util.Collection;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15import tools.refinery.viatra.runtime.rete.network.ReteContainer;
16
17/**
18 * An aggregation node that simply counts the number of tuples conforming to the signature.
19 *
20 * @author Gabor Bergmann
21 * @since 1.4
22 */
23public class CountNode extends IndexerBasedAggregatorNode {
24
25 public CountNode(ReteContainer reteContainer) {
26 super(reteContainer);
27 }
28
29 int sizeOf(Collection<Tuple> group) {
30 return group == null ? 0 : group.size();
31 }
32
33 @Override
34 public Object aggregateGroup(Tuple signature, Collection<Tuple> group) {
35 return sizeOf(group);
36 }
37
38}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedMap.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedMap.java
new file mode 100644
index 00000000..3c7850de
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedMap.java
@@ -0,0 +1,120 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.rete.aggregation;
10
11import java.util.AbstractMap.SimpleEntry;
12import java.util.Collection;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
19
20/**
21 * An optimized {@link Map} implementation where each key is produced by joining together a group tuple and some other
22 * object (via left inheritance). Only a select few {@link Map} operations are supported. This collection is
23 * unmodifiable.
24 *
25 * Operations on this map assume that client queries also obey the contract that keys are constructed from a group tuple
26 * and an additional object.
27 *
28 * @author Tamas Szabo
29 * @since 2.4
30 */
31public class GroupedMap<GroupedKeyType, ValueType> implements Map<Tuple, ValueType> {
32
33 protected final Tuple group;
34 // cached group size value is to be used in get()
35 private final int groupSize;
36 protected final Map<GroupedKeyType, ValueType> mappings;
37 protected final IQueryRuntimeContext runtimeContext;
38
39 public GroupedMap(final Tuple group, final Map<GroupedKeyType, ValueType> mappings,
40 final IQueryRuntimeContext runtimeContext) {
41 this.group = group;
42 this.groupSize = group.getSize();
43 this.mappings = mappings;
44 this.runtimeContext = runtimeContext;
45 }
46
47 @Override
48 public int size() {
49 return this.mappings.size();
50 }
51
52 @Override
53 public boolean isEmpty() {
54 return this.mappings.isEmpty();
55 }
56
57 @Override
58 public boolean containsKey(final Object key) {
59 throw new UnsupportedOperationException();
60 }
61
62 @Override
63 public boolean containsValue(final Object value) {
64 return this.mappings.containsValue(value);
65 }
66
67 @Override
68 public ValueType get(final Object key) {
69 if (key instanceof Tuple) {
70 final Object value = ((Tuple) key).get(this.groupSize);
71 final Object unwrappedValue = this.runtimeContext.unwrapElement(value);
72 return this.mappings.get(unwrappedValue);
73 } else {
74 return null;
75 }
76 }
77
78 @Override
79 public ValueType put(final Tuple key, final ValueType value) {
80 throw new UnsupportedOperationException();
81 }
82
83 @Override
84 public ValueType remove(final Object key) {
85 throw new UnsupportedOperationException();
86 }
87
88 @Override
89 public void putAll(final Map<? extends Tuple, ? extends ValueType> map) {
90 throw new UnsupportedOperationException();
91 }
92
93 @Override
94 public void clear() {
95 throw new UnsupportedOperationException();
96 }
97
98 @Override
99 public Set<Tuple> keySet() {
100 return new GroupedSet<Tuple, GroupedKeyType, Tuple>(this.group, this.mappings.keySet(), (g, v) -> {
101 return Tuples.staticArityLeftInheritanceTupleOf(g, this.runtimeContext.wrapElement(v));
102 });
103 }
104
105 @Override
106 public Collection<ValueType> values() {
107 return this.mappings.values();
108 }
109
110 @Override
111 public Set<Entry<Tuple, ValueType>> entrySet() {
112 return new GroupedSet<Tuple, GroupedKeyType, Entry<Tuple, ValueType>>(this.group, this.mappings.keySet(),
113 (g, v) -> {
114 final Tuple key = Tuples.staticArityLeftInheritanceTupleOf(g, this.runtimeContext.wrapElement(v));
115 final ValueType value = this.mappings.get(v);
116 return new SimpleEntry<Tuple, ValueType>(key, value);
117 });
118 }
119
120} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedSet.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedSet.java
new file mode 100644
index 00000000..65561e53
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedSet.java
@@ -0,0 +1,114 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.rete.aggregation;
10
11import java.util.Collection;
12import java.util.Iterator;
13import java.util.Set;
14import java.util.function.BiFunction;
15
16/**
17 * An optimized {@link Set} implementation where each contained value is produced by combining together a grouping value
18 * and some other (key) object. The way of combining together these two values is specified by the closure passed to the
19 * constructor. Only a select few {@link Set} operations are supported. This collection is unmodifiable.
20 *
21 * @author Tamas Szabo
22 * @since 2.4
23 */
24public class GroupedSet<GroupingValueType, GroupedKeyType, WholeKeyType> implements Set<WholeKeyType> {
25
26 protected final GroupingValueType group;
27 protected final Collection<GroupedKeyType> values;
28 protected final BiFunction<GroupingValueType, GroupedKeyType, WholeKeyType> valueFunc;
29
30 public GroupedSet(final GroupingValueType group, final Collection<GroupedKeyType> values,
31 final BiFunction<GroupingValueType, GroupedKeyType, WholeKeyType> valueFunc) {
32 this.group = group;
33 this.values = values;
34 this.valueFunc = valueFunc;
35 }
36
37 @Override
38 public int size() {
39 return this.values.size();
40 }
41
42 @Override
43 public boolean isEmpty() {
44 return this.values.isEmpty();
45 }
46
47 @Override
48 public boolean contains(final Object obj) {
49 throw new UnsupportedOperationException();
50 }
51
52 @Override
53 public Iterator<WholeKeyType> iterator() {
54 final Iterator<GroupedKeyType> wrapped = this.values.iterator();
55 return new Iterator<WholeKeyType>() {
56 @Override
57 public boolean hasNext() {
58 return wrapped.hasNext();
59 }
60
61 @Override
62 public WholeKeyType next() {
63 final GroupedKeyType value = wrapped.next();
64 return valueFunc.apply(group, value);
65 }
66 };
67 }
68
69 @Override
70 public Object[] toArray() {
71 throw new UnsupportedOperationException();
72 }
73
74 @Override
75 public <T> T[] toArray(final T[] arr) {
76 throw new UnsupportedOperationException();
77 }
78
79 @Override
80 public boolean add(final WholeKeyType tuple) {
81 throw new UnsupportedOperationException();
82 }
83
84 @Override
85 public boolean remove(final Object obj) {
86 throw new UnsupportedOperationException();
87 }
88
89 @Override
90 public boolean containsAll(final Collection<?> c) {
91 throw new UnsupportedOperationException();
92 }
93
94 @Override
95 public boolean addAll(final Collection<? extends WholeKeyType> coll) {
96 throw new UnsupportedOperationException();
97 }
98
99 @Override
100 public boolean retainAll(final Collection<?> coll) {
101 throw new UnsupportedOperationException();
102 }
103
104 @Override
105 public boolean removeAll(final Collection<?> coll) {
106 throw new UnsupportedOperationException();
107 }
108
109 @Override
110 public void clear() {
111 throw new UnsupportedOperationException();
112 }
113
114} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IAggregatorNode.java
new file mode 100644
index 00000000..6c286364
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IAggregatorNode.java
@@ -0,0 +1,26 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.rete.aggregation;
10
11import tools.refinery.viatra.runtime.rete.index.Indexer;
12
13/**
14 * Expresses that aggregators expose specialized non-enumerable indexers for outer joining.
15 * @author Gabor Bergmann
16 *
17 * @since 1.4
18 *
19 */
20public interface IAggregatorNode {
21
22 Indexer getAggregatorOuterIndexer();
23
24 Indexer getAggregatorOuterIdentityIndexer(int resultPositionInSignature);
25
26} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IndexerBasedAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IndexerBasedAggregatorNode.java
new file mode 100644
index 00000000..d9a94a82
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IndexerBasedAggregatorNode.java
@@ -0,0 +1,278 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2009 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.rete.aggregation;
11
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Map;
15import java.util.Map.Entry;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.Direction;
22import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
23import tools.refinery.viatra.runtime.rete.index.DefaultIndexerListener;
24import tools.refinery.viatra.runtime.rete.index.Indexer;
25import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
26import tools.refinery.viatra.runtime.rete.index.StandardIndexer;
27import tools.refinery.viatra.runtime.rete.network.Node;
28import tools.refinery.viatra.runtime.rete.network.ReteContainer;
29import tools.refinery.viatra.runtime.rete.network.StandardNode;
30import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
31import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
32
33/**
34 * A special node depending on a projection indexer to aggregate tuple groups with the same projection. Only propagates
35 * the aggregates of non-empty groups. Use the outer indexers to circumvent.
36 * <p>
37 * This node cannot be used in recursive differential dataflow evaluation.
38 *
39 * @author Gabor Bergmann
40 * @since 1.4
41 */
42public abstract class IndexerBasedAggregatorNode extends StandardNode implements IAggregatorNode {
43
44 ProjectionIndexer projection;
45 IndexerBasedAggregatorNode me;
46 int sourceWidth;
47 Map<Tuple, Object> mainAggregates;
48
49 AggregatorOuterIndexer aggregatorOuterIndexer = null;
50 AggregatorOuterIdentityIndexer[] aggregatorOuterIdentityIndexers = null;
51
52 /**
53 * MUST call initializeWith() afterwards!
54 */
55 public IndexerBasedAggregatorNode(ReteContainer reteContainer) {
56 super(reteContainer);
57 this.me = this;
58 mainAggregates = CollectionsFactory.createMap();
59 }
60
61 @Override
62 public void networkStructureChanged() {
63 if (this.reteContainer.isTimelyEvaluation() && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
64 throw new IllegalStateException(this.toString() + " cannot be used in recursive differential dataflow evaluation!");
65 }
66 super.networkStructureChanged();
67 }
68
69 /**
70 * @param projection
71 * the projection indexer whose tuple groups should be aggregated
72 */
73 public void initializeWith(ProjectionIndexer projection) {
74 this.projection = projection;
75 this.sourceWidth = projection.getMask().indices.length;
76
77 for (Tuple signature : projection.getSignatures()) {
78 mainAggregates.put(signature, aggregateGroup(signature, projection.get(signature)));
79 }
80 projection.attachListener(new DefaultIndexerListener(this) {
81 @Override
82 public void notifyIndexerUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change, Timestamp timestamp) {
83 aggregateUpdate(direction, updateElement, signature, change);
84 }
85 });
86 }
87
88 /**
89 * Aggregates (reduces) a group of tuples. The group can be null.
90 */
91 public abstract Object aggregateGroup(Tuple signature, Collection<Tuple> group);
92
93
94 /**
95 * Aggregates (reduces) a group of tuples, having access to the previous aggregated value (before the update) and
96 * the update definition. Defaults to aggregateGroup(). Override to increase performance.
97 * @since 2.4
98 */
99 public Object aggregateGroupAfterUpdate(Tuple signature, Collection<Tuple> currentGroup, Object oldAggregate,
100 Direction direction, Tuple updateElement, boolean change) {
101 return aggregateGroup(signature, currentGroup);
102 }
103
104 protected Tuple aggregateAndPack(Tuple signature, Collection<Tuple> group) {
105 return packResult(signature, aggregateGroup(signature, group));
106 }
107
108 @Override
109 public Indexer getAggregatorOuterIndexer() {
110 if (aggregatorOuterIndexer == null) {
111 aggregatorOuterIndexer = new AggregatorOuterIndexer();
112 this.getCommunicationTracker().registerDependency(this, aggregatorOuterIndexer);
113 // reteContainer.connectAndSynchronize(this, aggregatorOuterIndexer);
114 }
115 return aggregatorOuterIndexer;
116 }
117
118 @Override
119 public Indexer getAggregatorOuterIdentityIndexer(int resultPositionInSignature) {
120 if (aggregatorOuterIdentityIndexers == null)
121 aggregatorOuterIdentityIndexers = new AggregatorOuterIdentityIndexer[sourceWidth + 1];
122 if (aggregatorOuterIdentityIndexers[resultPositionInSignature] == null) {
123 aggregatorOuterIdentityIndexers[resultPositionInSignature] = new AggregatorOuterIdentityIndexer(
124 resultPositionInSignature);
125 this.getCommunicationTracker().registerDependency(this, aggregatorOuterIdentityIndexers[resultPositionInSignature]);
126 // reteContainer.connectAndSynchronize(this, aggregatorOuterIdentityIndexers[resultPositionInSignature]);
127 }
128 return aggregatorOuterIdentityIndexers[resultPositionInSignature];
129 }
130
131 @Override
132 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
133 for (final Entry<Tuple, Object> aggregateEntry : mainAggregates.entrySet()) {
134 collector.add(packResult(aggregateEntry.getKey(), aggregateEntry.getValue()));
135 }
136 }
137
138 @Override
139 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
140 // use all zero timestamps because this node cannot be used in recursive groups anyway
141 for (final Entry<Tuple, Object> aggregateEntry : mainAggregates.entrySet()) {
142 collector.put(packResult(aggregateEntry.getKey(), aggregateEntry.getValue()), Timestamp.INSERT_AT_ZERO_TIMELINE);
143 }
144 }
145
146 protected Tuple packResult(Tuple signature, Object result) {
147 return Tuples.staticArityLeftInheritanceTupleOf(signature, result);
148 }
149
150 /**
151 * @since 2.4
152 */
153 protected void aggregateUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change) {
154 Collection<Tuple> currentGroup = projection.get(signature);
155 // these will be null if group is empty
156 Object oldAggregate = mainAggregates.get(signature);
157 Object safeOldAggregate = oldAggregate == null ? aggregateGroup(signature, null) : oldAggregate;
158 boolean empty = currentGroup == null || currentGroup.isEmpty();
159 Object newAggregate = empty ? null : aggregateGroupAfterUpdate(signature, currentGroup, safeOldAggregate/*
160 * non-null
161 */,
162 direction, updateElement, change);
163 if (!empty)
164 mainAggregates.put(signature, newAggregate);
165 else
166 mainAggregates.remove(signature);
167 Tuple oldTuple = packResult(signature, safeOldAggregate);
168 Tuple newTuple = packResult(signature, newAggregate == null ? aggregateGroup(signature, null) : newAggregate);
169 if (oldAggregate != null)
170 propagateUpdate(Direction.DELETE, oldTuple, Timestamp.ZERO); // direct outputs lack non-empty groups
171 if (newAggregate != null)
172 propagateUpdate(Direction.INSERT, newTuple, Timestamp.ZERO); // direct outputs lack non-empty groups
173 if (aggregatorOuterIndexer != null)
174 aggregatorOuterIndexer.propagate(signature, oldTuple, newTuple);
175 if (aggregatorOuterIdentityIndexers != null)
176 for (AggregatorOuterIdentityIndexer aggregatorOuterIdentityIndexer : aggregatorOuterIdentityIndexers)
177 if (aggregatorOuterIdentityIndexer != null)
178 aggregatorOuterIdentityIndexer.propagate(signature, oldTuple, newTuple);
179 }
180
181 private Object getAggregate(Tuple signature) {
182 Object aggregate = mainAggregates.get(signature);
183 return aggregate == null ? aggregateGroup(signature, null) : aggregate;
184 }
185
186 @Override
187 public void assignTraceInfo(TraceInfo traceInfo) {
188 super.assignTraceInfo(traceInfo);
189 if (traceInfo.propagateToIndexerParent() && projection != null)
190 projection.acceptPropagatedTraceInfo(traceInfo);
191 }
192
193 /**
194 * A special non-iterable index that retrieves the aggregated, packed result (signature+aggregate) for the original
195 * signature.
196 *
197 * @author Gabor Bergmann
198 */
199 class AggregatorOuterIndexer extends StandardIndexer {
200
201 public AggregatorOuterIndexer() {
202 super(me.reteContainer, TupleMask.omit(sourceWidth, sourceWidth + 1));
203 this.parent = me;
204 }
205
206 @Override
207 public Collection<Tuple> get(Tuple signature) {
208 return Collections.singleton(packResult(signature, getAggregate(signature)));
209 }
210
211 public void propagate(Tuple signature, Tuple oldTuple, Tuple newTuple) {
212 propagate(Direction.INSERT, newTuple, signature, false, Timestamp.ZERO);
213 propagate(Direction.DELETE, oldTuple, signature, false, Timestamp.ZERO);
214 }
215
216 @Override
217 public Node getActiveNode() {
218 return projection.getActiveNode();
219 }
220
221 }
222
223 /**
224 * A special non-iterable index that checks a suspected aggregate value for a given signature. The signature for
225 * this index is the original signature of the projection index, with the suspected result inserted at position
226 * resultPositionInSignature.
227 *
228 * @author Gabor Bergmann
229 */
230
231 class AggregatorOuterIdentityIndexer extends StandardIndexer {
232 int resultPositionInSignature;
233 TupleMask pruneResult;
234 TupleMask reorderMask;
235
236 public AggregatorOuterIdentityIndexer(int resultPositionInSignature) {
237 super(me.reteContainer, TupleMask.displace(sourceWidth, resultPositionInSignature, sourceWidth + 1));
238 this.parent = me;
239 // this.localAggregates = new HashMap<Tuple, Tuple>();
240 this.resultPositionInSignature = resultPositionInSignature;
241 this.pruneResult = TupleMask.omit(resultPositionInSignature, sourceWidth + 1);
242 if (resultPositionInSignature == sourceWidth)
243 this.reorderMask = null;
244 else
245 this.reorderMask = mask;
246 }
247
248 @Override
249 public Collection<Tuple> get(Tuple signatureWithResult) {
250 Tuple prunedSignature = pruneResult.transform(signatureWithResult);
251 Object result = getAggregate(prunedSignature);
252 if (signatureWithResult.get(resultPositionInSignature).equals(result))
253 return Collections.singleton(signatureWithResult);
254 else
255 return null;
256 }
257
258 public void propagate(Tuple signature, Tuple oldTuple, Tuple newTuple) {
259 propagate(Direction.INSERT, reorder(newTuple), signature, true, Timestamp.ZERO);
260 propagate(Direction.DELETE, reorder(oldTuple), signature, true, Timestamp.ZERO);
261 }
262
263 private Tuple reorder(Tuple signatureWithResult) {
264 Tuple transformed;
265 if (reorderMask == null)
266 transformed = signatureWithResult;
267 else
268 transformed = reorderMask.transform(signatureWithResult);
269 return transformed;
270 }
271
272 @Override
273 public Node getActiveNode() {
274 return projection.getActiveNode();
275 }
276 }
277
278}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java
new file mode 100644
index 00000000..a9863400
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java
@@ -0,0 +1,217 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.rete.aggregation.timely;
10
11import java.util.Collections;
12import java.util.Map;
13import java.util.Map.Entry;
14import java.util.Objects;
15import java.util.TreeMap;
16
17import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.Direction;
22import tools.refinery.viatra.runtime.matchers.util.IDeltaBag;
23import tools.refinery.viatra.runtime.matchers.util.Preconditions;
24import tools.refinery.viatra.runtime.matchers.util.Signed;
25import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
26import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode.CumulativeAggregate;
27import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode.FoldingState;
28import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulTimelyColumnAggregatorNode.MergeableFoldingState;
29import tools.refinery.viatra.runtime.rete.network.ReteContainer;
30import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
31import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
32
33/**
34 * Faithful column aggregator with parallel aggregation architecture.
35 *
36 * @author Tamas Szabo
37 * @since 2.4
38 *
39 */
40public class FaithfulParallelTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> extends
41 FaithfulTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult, CumulativeAggregate<Domain, Accumulator>, FoldingState<Domain>>
42 implements ResumableNode {
43
44 public FaithfulParallelTimelyColumnAggregatorNode(final ReteContainer reteContainer,
45 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
46 final TupleMask groupMask, final TupleMask columnMask) {
47 super(reteContainer, operator, groupMask, columnMask);
48 }
49
50 public FaithfulParallelTimelyColumnAggregatorNode(final ReteContainer reteContainer,
51 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
52 final TupleMask groupMask, final int aggregatedColumn) {
53 this(reteContainer, operator, groupMask, TupleMask.selectSingle(aggregatedColumn, groupMask.sourceWidth));
54 }
55
56 @Override
57 protected Map<AggregateResult, Diff<Timestamp>> doFoldingStep(final Tuple group, final FoldingState<Domain> state,
58 final Timestamp timestamp) {
59 final CumulativeAggregate<Domain, Accumulator> aggregate = getAggregate(group, timestamp);
60 if (state.delta.isEmpty()) {
61 gcAggregates(aggregate, group, timestamp);
62 return Collections.emptyMap();
63 } else {
64 final Map<AggregateResult, Diff<Timestamp>> diffMap = CollectionsFactory.createMap();
65 final Timestamp nextTimestamp = this.aggregates.get(group).higherKey(timestamp);
66
67 final AggregateResult currentOldResult = operator.getAggregate(aggregate.accumulator);
68
69 for (final Entry<Domain, Integer> entry : state.delta.entriesWithMultiplicities()) {
70 final boolean isInsertion = entry.getValue() > 0;
71 final Domain aggregand = entry.getKey();
72 for (int i = 0; i < Math.abs(entry.getValue()); i++) {
73 aggregate.accumulator = operator.update(aggregate.accumulator, aggregand, isInsertion);
74 }
75 }
76
77 final AggregateResult currentNewResult = operator.getAggregate(aggregate.accumulator);
78
79 if (!Objects.equals(currentOldResult, currentNewResult)) {
80 // current old result disappears here
81 appendDiff(currentOldResult, new Signed<>(Direction.DELETE, timestamp), diffMap);
82 if (nextTimestamp != null) {
83 appendDiff(currentOldResult, new Signed<>(Direction.INSERT, nextTimestamp), diffMap);
84 }
85
86 // current new result appears here
87 appendDiff(currentNewResult, new Signed<>(Direction.INSERT, timestamp), diffMap);
88 if (nextTimestamp != null) {
89 appendDiff(currentNewResult, new Signed<>(Direction.DELETE, nextTimestamp), diffMap);
90 }
91 }
92
93 gcAggregates(aggregate, group, timestamp);
94 updateTimeline(group, diffMap);
95
96 // prepare folding state for next timestamp
97 if (nextTimestamp != null) {
98 final FoldingState<Domain> newState = new FoldingState<>();
99 newState.delta = state.delta;
100 addFoldingState(group, newState, nextTimestamp);
101 }
102
103 return diffMap;
104 }
105 }
106
107 @Override
108 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
109 final Tuple group = groupMask.transform(update);
110 final Tuple value = columnMask.transform(update);
111 @SuppressWarnings("unchecked")
112 final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0));
113 final boolean isInsertion = direction == Direction.INSERT;
114
115 final CumulativeAggregate<Domain, Accumulator> aggregate = getAggregate(group, timestamp);
116 final FoldingState<Domain> state = new FoldingState<>();
117 if (isInsertion) {
118 aggregate.aggregands.addOne(aggregand);
119 state.delta.addOne(aggregand);
120 } else {
121 aggregate.aggregands.removeOne(aggregand);
122 state.delta.removeOne(aggregand);
123 }
124
125 addFoldingState(group, state, timestamp);
126 }
127
128 /**
129 * Garbage collects the counter of the given group and timestamp if the bag of aggregands is empty.
130 */
131 @Override
132 protected void gcAggregates(final CumulativeAggregate<Domain, Accumulator> aggregate, final Tuple group,
133 final Timestamp timestamp) {
134 if (aggregate.aggregands.isEmpty()) {
135 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator>> groupAggregates = this.aggregates
136 .get(group);
137 groupAggregates.remove(timestamp);
138 if (groupAggregates.isEmpty()) {
139 this.aggregates.remove(group);
140 }
141 }
142 }
143
144 /**
145 * On-demand initializes and returns the aggregate for the given group and timestamp.
146 */
147 @Override
148 protected CumulativeAggregate<Domain, Accumulator> getAggregate(final Tuple group, final Timestamp timestamp) {
149 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator>> groupAggregates = this.aggregates
150 .computeIfAbsent(group, k -> CollectionsFactory.createTreeMap());
151 return groupAggregates.computeIfAbsent(timestamp, k -> {
152 final CumulativeAggregate<Domain, Accumulator> aggregate = new CumulativeAggregate<>();
153 final Entry<Timestamp, CumulativeAggregate<Domain, Accumulator>> lowerEntry = groupAggregates
154 .lowerEntry(timestamp);
155 if (lowerEntry == null) {
156 aggregate.accumulator = operator.createNeutral();
157 } else {
158 aggregate.accumulator = operator.clone(lowerEntry.getValue().accumulator);
159 }
160 return aggregate;
161 });
162 }
163
164 @Override
165 public AggregateResult getAggregateResult(final Tuple group) {
166 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator>> groupAggregates = this.aggregates.get(group);
167 if (groupAggregates != null) {
168 final Entry<Timestamp, CumulativeAggregate<Domain, Accumulator>> lastEntry = groupAggregates.lastEntry();
169 return operator.getAggregate(lastEntry.getValue().accumulator);
170 } else {
171 return NEUTRAL;
172 }
173 }
174
175 protected static class CumulativeAggregate<Domain, Accumulator> {
176 protected Accumulator accumulator;
177 protected IDeltaBag<Domain> aggregands;
178
179 protected CumulativeAggregate() {
180 this.aggregands = CollectionsFactory.createDeltaBag();
181 }
182
183 @Override
184 public String toString() {
185 return "accumulator=" + accumulator + " aggregands=" + aggregands;
186 }
187 }
188
189 protected static class FoldingState<Domain> implements MergeableFoldingState<FoldingState<Domain>> {
190 protected IDeltaBag<Domain> delta;
191
192 protected FoldingState() {
193 this.delta = CollectionsFactory.createDeltaBag();
194 }
195
196 @Override
197 public String toString() {
198 return "delta=" + delta;
199 }
200
201 /**
202 * The returned result will never be null, even if the resulting delta set is empty.
203 */
204 @Override
205 public FoldingState<Domain> merge(final FoldingState<Domain> that) {
206 Preconditions.checkArgument(that != null);
207 // 'this' was the previously registered folding state
208 // 'that' is the new folding state being pushed upwards
209 final FoldingState<Domain> result = new FoldingState<>();
210 this.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m));
211 that.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m));
212 return result;
213 }
214
215 }
216
217} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulSequentialTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulSequentialTimelyColumnAggregatorNode.java
new file mode 100644
index 00000000..666b2051
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulSequentialTimelyColumnAggregatorNode.java
@@ -0,0 +1,280 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.rete.aggregation.timely;
10
11import java.util.Collections;
12import java.util.Map;
13import java.util.Map.Entry;
14import java.util.Objects;
15import java.util.TreeMap;
16
17import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.Direction;
22import tools.refinery.viatra.runtime.matchers.util.IDeltaBag;
23import tools.refinery.viatra.runtime.matchers.util.Preconditions;
24import tools.refinery.viatra.runtime.matchers.util.Signed;
25import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
26import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode.CumulativeAggregate;
27import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode.FoldingState;
28import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulTimelyColumnAggregatorNode.MergeableFoldingState;
29import tools.refinery.viatra.runtime.rete.network.ReteContainer;
30import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
31import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
32
33/**
34 * Faithful column aggregator with sequential aggregation architecture.
35 *
36 * @author Tamas Szabo
37 * @since 2.4
38 *
39 */
40public class FaithfulSequentialTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> extends
41 FaithfulTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult, CumulativeAggregate<Domain, Accumulator, AggregateResult>, FoldingState<Domain, AggregateResult>>
42 implements ResumableNode {
43
44 protected boolean isRecursiveAggregation;
45
46 public FaithfulSequentialTimelyColumnAggregatorNode(final ReteContainer reteContainer,
47 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
48 final TupleMask groupMask, final TupleMask columnMask) {
49 super(reteContainer, operator, groupMask, columnMask);
50 this.isRecursiveAggregation = false;
51 }
52
53 @Override
54 public void networkStructureChanged() {
55 super.networkStructureChanged();
56 this.isRecursiveAggregation = this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this);
57 }
58
59 @Override
60 protected Map<AggregateResult, Diff<Timestamp>> doFoldingStep(final Tuple group,
61 final FoldingState<Domain, AggregateResult> state, final Timestamp timestamp) {
62 final CumulativeAggregate<Domain, Accumulator, AggregateResult> aggregate = getAggregate(group, timestamp);
63 if (state.delta.isEmpty() && Objects.equals(state.oldResult, state.newResult)) {
64 gcAggregates(aggregate, group, timestamp);
65 return Collections.emptyMap();
66 } else {
67 final Map<AggregateResult, Diff<Timestamp>> diffMap = CollectionsFactory.createMap();
68 final Timestamp nextTimestamp = this.aggregates.get(group).higherKey(timestamp);
69
70 final AggregateResult previousOldResult = state.oldResult;
71 final AggregateResult previousNewResult = state.newResult;
72
73 final AggregateResult currentOldResult = previousOldResult == null
74 ? operator.getAggregate(aggregate.positive)
75 : operator.combine(previousOldResult, aggregate.positive);
76
77 for (final Entry<Domain, Integer> entry : state.delta.entriesWithMultiplicities()) {
78 final boolean isInsertion = entry.getValue() > 0;
79 final Domain aggregand = entry.getKey();
80 if (isInsertion) {
81 for (int i = 0; i < entry.getValue(); i++) {
82 if (isRecursiveAggregation) {
83 final boolean contains = aggregate.negative.containsNonZero(aggregand);
84 if (contains) {
85 aggregate.negative.addOne(aggregand);
86 } else {
87 aggregate.positive = operator.update(aggregate.positive, aggregand, true);
88 }
89 } else {
90 aggregate.positive = operator.update(aggregate.positive, aggregand, true);
91 }
92 }
93 } else {
94 for (int i = 0; i < -entry.getValue(); i++) {
95 if (isRecursiveAggregation) {
96 final boolean contains = operator.contains(aggregand, aggregate.positive);
97 if (contains) {
98 aggregate.positive = operator.update(aggregate.positive, aggregand, false);
99 } else {
100 aggregate.negative.removeOne(aggregand);
101 }
102 } else {
103 aggregate.positive = operator.update(aggregate.positive, aggregand, false);
104 }
105 }
106 }
107 }
108
109 final AggregateResult currentNewResult = previousNewResult == null
110 ? operator.getAggregate(aggregate.positive)
111 : operator.combine(previousNewResult, aggregate.positive);
112
113 aggregate.cachedResult = currentNewResult;
114
115 final boolean sameResult = Objects.equals(currentOldResult, currentNewResult);
116 if (!sameResult) {
117 // current old result disappears here
118 appendDiff(currentOldResult, new Signed<>(Direction.DELETE, timestamp), diffMap);
119 if (nextTimestamp != null) {
120 appendDiff(currentOldResult, new Signed<>(Direction.INSERT, nextTimestamp), diffMap);
121 }
122
123 // current new result appears here
124 appendDiff(currentNewResult, new Signed<>(Direction.INSERT, timestamp), diffMap);
125 if (nextTimestamp != null) {
126 appendDiff(currentNewResult, new Signed<>(Direction.DELETE, nextTimestamp), diffMap);
127 }
128 }
129
130 gcAggregates(aggregate, group, timestamp);
131 updateTimeline(group, diffMap);
132
133 // prepare folding state for next timestamp
134 if (nextTimestamp != null && !sameResult) {
135 final FoldingState<Domain, AggregateResult> newState = new FoldingState<>();
136 // DO NOT push forward the delta in the folding state!!! that one only affects the input timestamp
137 newState.oldResult = currentOldResult;
138 newState.newResult = currentNewResult;
139 addFoldingState(group, newState, nextTimestamp);
140 }
141
142 return diffMap;
143 }
144 }
145
146 @Override
147 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
148 final Tuple group = groupMask.transform(update);
149 final Tuple value = columnMask.transform(update);
150 @SuppressWarnings("unchecked")
151 final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0));
152 final boolean isInsertion = direction == Direction.INSERT;
153
154 final AggregateResult previousResult = getResultRaw(group, timestamp, true);
155 final FoldingState<Domain, AggregateResult> state = new FoldingState<Domain, AggregateResult>();
156 if (isInsertion) {
157 state.delta.addOne(aggregand);
158 } else {
159 state.delta.removeOne(aggregand);
160 }
161 state.oldResult = previousResult;
162 state.newResult = previousResult;
163
164 // it is acceptable if both oldResult and newResult are null at this point
165 // in that case we did not have a previous entry at a lower timestamp
166
167 addFoldingState(group, state, timestamp);
168 }
169
170 protected AggregateResult getResultRaw(final Tuple group, final Timestamp timestamp, final boolean lower) {
171 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> entryMap = this.aggregates
172 .get(group);
173 if (entryMap == null) {
174 return null;
175 } else {
176 CumulativeAggregate<Domain, Accumulator, AggregateResult> aggregate = null;
177 if (lower) {
178 final Entry<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> lowerEntry = entryMap
179 .lowerEntry(timestamp);
180 if (lowerEntry != null) {
181 aggregate = lowerEntry.getValue();
182 }
183 } else {
184 aggregate = entryMap.get(timestamp);
185 }
186 if (aggregate == null) {
187 return null;
188 } else {
189 return aggregate.cachedResult;
190 }
191 }
192 }
193
194 @Override
195 protected void gcAggregates(final CumulativeAggregate<Domain, Accumulator, AggregateResult> aggregate,
196 final Tuple group, final Timestamp timestamp) {
197 if (operator.isNeutral(aggregate.positive) && aggregate.negative.isEmpty()) {
198 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> groupAggregates = this.aggregates
199 .get(group);
200 groupAggregates.remove(timestamp);
201 if (groupAggregates.isEmpty()) {
202 this.aggregates.remove(group);
203 }
204 }
205 }
206
207 @Override
208 protected CumulativeAggregate<Domain, Accumulator, AggregateResult> getAggregate(final Tuple group,
209 final Timestamp timestamp) {
210 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> groupAggregates = this.aggregates
211 .computeIfAbsent(group, k -> CollectionsFactory.createTreeMap());
212 return groupAggregates.computeIfAbsent(timestamp, k -> {
213 final CumulativeAggregate<Domain, Accumulator, AggregateResult> aggregate = new CumulativeAggregate<>();
214 aggregate.positive = operator.createNeutral();
215 return aggregate;
216 });
217 }
218
219 @Override
220 public AggregateResult getAggregateResult(final Tuple group) {
221 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> groupAggregates = this.aggregates
222 .get(group);
223 if (groupAggregates != null) {
224 final Entry<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> lastEntry = groupAggregates
225 .lastEntry();
226 return lastEntry.getValue().cachedResult;
227 } else {
228 return NEUTRAL;
229 }
230 }
231
232 protected static class CumulativeAggregate<Domain, Accumulator, AggregateResult> {
233 protected Accumulator positive;
234 protected IDeltaBag<Domain> negative;
235 protected AggregateResult cachedResult;
236
237 protected CumulativeAggregate() {
238 this.negative = CollectionsFactory.createDeltaBag();
239 }
240
241 @Override
242 public String toString() {
243 return "positive=" + positive + " negative=" + negative + " cachedResult=" + cachedResult;
244 }
245 }
246
247 protected static class FoldingState<Domain, AggregateResult>
248 implements MergeableFoldingState<FoldingState<Domain, AggregateResult>> {
249 protected IDeltaBag<Domain> delta;
250 protected AggregateResult oldResult;
251 protected AggregateResult newResult;
252
253 protected FoldingState() {
254 this.delta = CollectionsFactory.createDeltaBag();
255 }
256
257 @Override
258 public String toString() {
259 return "delta=" + delta + " oldResult=" + oldResult + " newResult=" + newResult;
260 }
261
262 /**
263 * The returned result will never be null, even if the resulting delta set is empty.
264 */
265 @Override
266 public FoldingState<Domain, AggregateResult> merge(final FoldingState<Domain, AggregateResult> that) {
267 Preconditions.checkArgument(that != null);
268 // 'this' was the previously registered folding state
269 // 'that' is the new folding state being pushed upwards
270 final FoldingState<Domain, AggregateResult> result = new FoldingState<Domain, AggregateResult>();
271 this.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m));
272 that.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m));
273 result.oldResult = this.oldResult;
274 result.newResult = that.newResult;
275 return result;
276 }
277
278 }
279
280} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulTimelyColumnAggregatorNode.java
new file mode 100644
index 00000000..8fe9a4e9
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulTimelyColumnAggregatorNode.java
@@ -0,0 +1,247 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.rete.aggregation.timely;
10
11import java.util.Collections;
12import java.util.Map;
13import java.util.Map.Entry;
14import java.util.Objects;
15import java.util.TreeMap;
16
17import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.Signed;
22import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
23import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines;
25import tools.refinery.viatra.runtime.rete.aggregation.AbstractColumnAggregatorNode;
26import tools.refinery.viatra.runtime.rete.aggregation.GroupedMap;
27import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulTimelyColumnAggregatorNode.MergeableFoldingState;
28import tools.refinery.viatra.runtime.rete.network.ReteContainer;
29import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
30import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
31import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
32import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
33import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
34
35/**
36 * Faithful timely implementation of the column aggregator node. Complete timelines (series of appearance &
37 * disappearance) are maintained for tuples. <br>
38 * <br>
39 * Subclasses are responsible for implementing the aggregator architecture, and they must use the CumulativeAggregate
40 * type parameter for that. <br>
41 * <br>
42 * This node supports recursive aggregation.
43 *
44 * @author Tamas Szabo
45 * @since 2.4
46 */
47public abstract class FaithfulTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult, CumulativeAggregate, FoldingState extends MergeableFoldingState<FoldingState>>
48 extends AbstractColumnAggregatorNode<Domain, Accumulator, AggregateResult> implements ResumableNode {
49
50 protected final Map<Tuple, TreeMap<Timestamp, CumulativeAggregate>> aggregates;
51 protected final Map<Tuple, Map<AggregateResult, Timeline<Timestamp>>> timelines;
52 protected final TreeMap<Timestamp, Map<Tuple, FoldingState>> foldingState;
53 protected CommunicationGroup communicationGroup;
54
55 public FaithfulTimelyColumnAggregatorNode(final ReteContainer reteContainer,
56 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
57 final TupleMask groupMask, final TupleMask columnMask) {
58 super(reteContainer, operator, groupMask, columnMask);
59 this.aggregates = CollectionsFactory.createMap();
60 this.timelines = CollectionsFactory.createMap();
61 this.foldingState = CollectionsFactory.createTreeMap();
62 // mailbox MUST be instantiated after the fields are all set
63 this.mailbox = instantiateMailbox();
64 }
65
66 @Override
67 protected Mailbox instantiateMailbox() {
68 return new TimelyMailbox(this, this.reteContainer);
69 }
70
71 @Override
72 public void clear() {
73 this.mailbox.clear();
74 this.aggregates.clear();
75 this.timelines.clear();
76 this.children.clear();
77 this.childMailboxes.clear();
78 this.foldingState.clear();
79 }
80
81 /**
82 * Registers the given folding state for the specified timestamp and tuple. If there is already a state stored, the
83 * two states will be merged together.
84 *
85 *
86 */
87 protected void addFoldingState(final Tuple group, final FoldingState state, final Timestamp timestamp) {
88 // assert !state.delta.isEmpty();
89 final Map<Tuple, FoldingState> tupleMap = this.foldingState.computeIfAbsent(timestamp,
90 k -> CollectionsFactory.createMap());
91 tupleMap.compute(group, (k, v) -> {
92 return v == null ? state : v.merge(state);
93 });
94 }
95
96 @Override
97 public Timestamp getResumableTimestamp() {
98 if (this.foldingState.isEmpty()) {
99 return null;
100 } else {
101 return this.foldingState.firstKey();
102 }
103 }
104
105 @Override
106 public void resumeAt(final Timestamp timestamp) {
107 Timestamp current = this.getResumableTimestamp();
108 if (current == null) {
109 throw new IllegalStateException("There is nothing to fold!");
110 } else if (current.compareTo(timestamp) != 0) {
111 throw new IllegalStateException("Expected to continue folding at " + timestamp + "!");
112 }
113
114 final Map<Tuple, FoldingState> tupleMap = this.foldingState.remove(timestamp);
115 for (final Entry<Tuple, FoldingState> groupEntry : tupleMap.entrySet()) {
116 final Tuple group = groupEntry.getKey();
117 final FoldingState value = groupEntry.getValue();
118 final Map<AggregateResult, Diff<Timestamp>> diffMap = doFoldingStep(group, value, timestamp);
119 for (final Entry<AggregateResult, Diff<Timestamp>> resultEntry : diffMap.entrySet()) {
120 for (final Signed<Timestamp> signed : resultEntry.getValue()) {
121 propagate(signed.getDirection(), group, resultEntry.getKey(), signed.getPayload());
122 }
123 }
124 }
125
126 final Timestamp nextTimestamp = this.getResumableTimestamp();
127 if (Objects.equals(timestamp, nextTimestamp)) {
128 throw new IllegalStateException(
129 "Folding at " + timestamp + " produced more folding work at the same timestamp!");
130 } else if (nextTimestamp != null) {
131 this.communicationGroup.notifyHasMessage(this.mailbox, nextTimestamp);
132 }
133 }
134
135 protected abstract Map<AggregateResult, Diff<Timestamp>> doFoldingStep(final Tuple group, final FoldingState state,
136 final Timestamp timestamp);
137
138 /**
139 * Updates and garbage collects the timeline of the given tuple based on the given diffs.
140 */
141 protected void updateTimeline(final Tuple group, final Map<AggregateResult, Diff<Timestamp>> diffs) {
142 if (!diffs.isEmpty()) {
143 this.timelines.compute(group, (k, resultTimelines) -> {
144 if (resultTimelines == null) {
145 resultTimelines = CollectionsFactory.createMap();
146 }
147 for (final Entry<AggregateResult, Diff<Timestamp>> entry : diffs.entrySet()) {
148 final AggregateResult result = entry.getKey();
149 resultTimelines.compute(result, (k2, oldResultTimeline) -> {
150 final Diff<Timestamp> currentResultDiffs = entry.getValue();
151 if (oldResultTimeline == null) {
152 oldResultTimeline = getInitialTimeline(result);
153 }
154 final Timeline<Timestamp> timeline = oldResultTimeline.mergeAdditive(currentResultDiffs);
155 if (timeline.isEmpty()) {
156 return null;
157 } else {
158 return timeline;
159 }
160 });
161 }
162 if (resultTimelines.isEmpty()) {
163 return null;
164 } else {
165 return resultTimelines;
166 }
167 });
168 }
169 }
170
171 /**
172 * Garbage collects the counter of the given group and timestamp if the bag of aggregands is empty.
173 */
174 protected abstract void gcAggregates(final CumulativeAggregate aggregate, final Tuple group,
175 final Timestamp timestamp);
176
177 /**
178 * On-demand initializes and returns the aggregate for the given group and timestamp.
179 */
180 protected abstract CumulativeAggregate getAggregate(final Tuple group, final Timestamp timestamp);
181
182 protected static final Timeline<Timestamp> NEUTRAL_INITIAL_TIMELINE = Timestamp.INSERT_AT_ZERO_TIMELINE;
183 protected static final Timeline<Timestamp> NON_NEUTRAL_INITIAL_TIMELINE = Timelines.createEmpty();
184
185 protected Timeline<Timestamp> getInitialTimeline(final AggregateResult result) {
186 if (NEUTRAL == result) {
187 return NEUTRAL_INITIAL_TIMELINE;
188 } else {
189 return NON_NEUTRAL_INITIAL_TIMELINE;
190 }
191 }
192
193 protected static <AggregateResult> void appendDiff(final AggregateResult result, final Signed<Timestamp> diff,
194 final Map<AggregateResult, Diff<Timestamp>> diffs) {
195 if (result != null) {
196 diffs.compute(result, (k, timeLineDiff) -> {
197 if (timeLineDiff == null) {
198 timeLineDiff = new Diff<>();
199 }
200 timeLineDiff.add(diff);
201 return timeLineDiff;
202 });
203 }
204 }
205
206 @Override
207 public Tuple getAggregateTuple(final Tuple group) {
208 return tupleFromAggregateResult(group, getAggregateResult(group));
209 }
210
211 @Override
212 public Map<AggregateResult, Timeline<Timestamp>> getAggregateResultTimeline(final Tuple group) {
213 final Map<AggregateResult, Timeline<Timestamp>> resultTimelines = this.timelines.get(group);
214 if (resultTimelines == null) {
215 if (NEUTRAL == null) {
216 return Collections.emptyMap();
217 } else {
218 return Collections.singletonMap(NEUTRAL, NEUTRAL_INITIAL_TIMELINE);
219 }
220 } else {
221 return resultTimelines;
222 }
223 }
224
225 @Override
226 public Map<Tuple, Timeline<Timestamp>> getAggregateTupleTimeline(final Tuple group) {
227 final Map<AggregateResult, Timeline<Timestamp>> resultTimelines = getAggregateResultTimeline(group);
228 return new GroupedMap<AggregateResult, Timeline<Timestamp>>(group, resultTimelines, this.runtimeContext);
229 }
230
231 @Override
232 public CommunicationGroup getCurrentGroup() {
233 return communicationGroup;
234 }
235
236 @Override
237 public void setCurrentGroup(final CommunicationGroup currentGroup) {
238 this.communicationGroup = currentGroup;
239 }
240
241 protected interface MergeableFoldingState<T> {
242
243 public abstract T merge(final T that);
244
245 }
246
247} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyParallelTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyParallelTimelyColumnAggregatorNode.java
new file mode 100644
index 00000000..733d2585
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyParallelTimelyColumnAggregatorNode.java
@@ -0,0 +1,106 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.rete.aggregation.timely;
10
11import java.util.Map.Entry;
12import java.util.TreeMap;
13
14import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20
21/**
22 * First-only column aggregator with parallel aggregation architecture.
23 *
24 * @author Tamas Szabo
25 * @since 2.4
26 */
27public class FirstOnlyParallelTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult>
28 extends FirstOnlyTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> {
29
30 public FirstOnlyParallelTimelyColumnAggregatorNode(final ReteContainer reteContainer,
31 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
32 final TupleMask groupMask, final TupleMask columnMask) {
33 super(reteContainer, operator, groupMask, columnMask);
34 }
35
36 /**
37 * Accumulator gets modified at the input timestamp and at all higher timestamps. Folding cannot be interrupted if
38 * the new aggregate result is the same as the old at an intermediate timestamp because aggregands need to be copied
39 * over to all accumulators at the higher timestamps.
40 */
41 @Override
42 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
43 final Tuple group = groupMask.transform(update);
44 final Tuple value = columnMask.transform(update);
45 @SuppressWarnings("unchecked")
46 final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0));
47 final boolean isInsertion = direction == Direction.INSERT;
48
49 final AggregateResult previousResult = getResultRaw(group, timestamp, true);
50
51 Accumulator oldAccumulator = getAccumulator(group, timestamp);
52 AggregateResult oldResult = operator.getAggregate(oldAccumulator);
53
54 Accumulator newAccumulator = operator.update(oldAccumulator, aggregand, isInsertion);
55 AggregateResult newResult = operator.getAggregate(newAccumulator);
56
57 storeIfNotNeutral(group, newAccumulator, newResult, timestamp);
58
59 propagateWithChecks(group, timestamp, previousResult, previousResult, oldResult, newResult);
60
61 AggregateResult previousOldResult = oldResult;
62 AggregateResult previousNewResult = newResult;
63 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> groupEntries = this.memory
64 .get(group);
65
66 Timestamp currentTimestamp = groupEntries == null ? null : groupEntries.higherKey(timestamp);
67
68 while (currentTimestamp != null) {
69 final CumulativeAggregate<Accumulator, AggregateResult> groupEntry = groupEntries.get(currentTimestamp);
70 oldResult = groupEntry.result;
71 oldAccumulator = groupEntry.accumulator;
72 newAccumulator = operator.update(oldAccumulator, aggregand, isInsertion);
73 newResult = operator.getAggregate(newAccumulator);
74
75 storeIfNotNeutral(group, newAccumulator, newResult, currentTimestamp);
76
77 propagateWithChecks(group, currentTimestamp, previousOldResult, previousNewResult, oldResult, newResult);
78
79 previousOldResult = oldResult;
80 previousNewResult = newResult;
81 currentTimestamp = groupEntries.higherKey(currentTimestamp);
82 }
83 }
84
85 @Override
86 protected Accumulator getAccumulator(final Tuple group, final Timestamp timestamp) {
87 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group);
88 if (entryMap == null) {
89 return operator.createNeutral();
90 } else {
91 final CumulativeAggregate<Accumulator, AggregateResult> entry = entryMap.get(timestamp);
92 if (entry == null) {
93 final Entry<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> lowerEntry = entryMap
94 .lowerEntry(timestamp);
95 if (lowerEntry == null) {
96 return operator.createNeutral();
97 } else {
98 return operator.clone(lowerEntry.getValue().accumulator);
99 }
100 } else {
101 return entry.accumulator;
102 }
103 }
104 }
105
106}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlySequentialTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlySequentialTimelyColumnAggregatorNode.java
new file mode 100644
index 00000000..79197aac
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlySequentialTimelyColumnAggregatorNode.java
@@ -0,0 +1,117 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.rete.aggregation.timely;
10
11import java.util.Objects;
12import java.util.TreeMap;
13
14import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20
21/**
22 * First-only column aggregator with sequential aggregation architecture.
23 *
24 * @author Tamas Szabo
25 * @since 2.4
26 */
27public class FirstOnlySequentialTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult>
28 extends FirstOnlyTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> {
29
30 public FirstOnlySequentialTimelyColumnAggregatorNode(final ReteContainer reteContainer,
31 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
32 final TupleMask groupMask, final TupleMask columnMask) {
33 super(reteContainer, operator, groupMask, columnMask);
34 }
35
36 /**
37 * Accumulator gets modified only at the timestamp where the update happened. During the folding, accumulators are
38 * never changed at higher timestamps. Aggregate results at higher timestamps may change due to the change at the
39 * input timestamp. Uniqueness enforcement may require from aggregate results to jump up/down on demand during the
40 * folding.
41 */
42 @Override
43 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
44 final Tuple group = groupMask.transform(update);
45 final Tuple value = columnMask.transform(update);
46 @SuppressWarnings("unchecked")
47 final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0));
48 final boolean isInsertion = direction == Direction.INSERT;
49
50 final AggregateResult previousResult = getResultRaw(group, timestamp, true);
51
52 final Accumulator oldAccumulator = getAccumulator(group, timestamp);
53 final AggregateResult oldResult = previousResult == null ? operator.getAggregate(oldAccumulator)
54 : operator.combine(previousResult, oldAccumulator);
55
56 final Accumulator newAccumulator = operator.update(oldAccumulator, aggregand, isInsertion);
57 final AggregateResult newResult = previousResult == null ? operator.getAggregate(newAccumulator)
58 : operator.combine(previousResult, newAccumulator);
59
60 storeIfNotNeutral(group, newAccumulator, newResult, timestamp);
61
62 propagateWithChecks(group, timestamp, previousResult, previousResult, oldResult, newResult);
63
64 // fold up the state towards higher timestamps
65 if (!Objects.equals(oldResult, newResult)) {
66 AggregateResult previousOldResult = oldResult;
67 AggregateResult previousNewResult = newResult;
68 AggregateResult currentOldResult = null;
69 AggregateResult currentNewResult = null;
70 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> groupEntries = this.memory
71 .get(group);
72
73 Timestamp currentTimestamp = groupEntries == null ? null : groupEntries.higherKey(timestamp);
74
75 while (currentTimestamp != null) {
76 // they cannot be the same, otherwise we would not even be here
77 assert !Objects.equals(previousOldResult, previousNewResult);
78
79 final Accumulator accumulator = getAccumulator(group, currentTimestamp);
80 currentOldResult = groupEntries.get(currentTimestamp).result;
81 currentNewResult = operator.combine(previousNewResult, accumulator);
82
83 // otherwise we would not be iterating over this timestamp
84 assert !operator.isNeutral(accumulator);
85
86 propagateWithChecks(group, currentTimestamp, previousOldResult, previousNewResult, currentOldResult,
87 currentNewResult);
88
89 if (!Objects.equals(currentOldResult, currentNewResult)) {
90 storeIfNotNeutral(group, accumulator, currentNewResult, currentTimestamp);
91 previousOldResult = currentOldResult;
92 previousNewResult = currentNewResult;
93 currentTimestamp = groupEntries.higherKey(currentTimestamp);
94 } else {
95 // we can stop the folding from here
96 break;
97 }
98 }
99 }
100 }
101
102 @Override
103 protected Accumulator getAccumulator(final Tuple group, final Timestamp timestamp) {
104 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group);
105 if (entryMap == null) {
106 return operator.createNeutral();
107 } else {
108 final CumulativeAggregate<Accumulator, AggregateResult> entry = entryMap.get(timestamp);
109 if (entry == null) {
110 return operator.createNeutral();
111 } else {
112 return entry.accumulator;
113 }
114 }
115 }
116
117}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyTimelyColumnAggregatorNode.java
new file mode 100644
index 00000000..0c73000e
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyTimelyColumnAggregatorNode.java
@@ -0,0 +1,212 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.rete.aggregation.timely;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Objects;
16import java.util.TreeMap;
17
18import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
20import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
21import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
22import tools.refinery.viatra.runtime.matchers.util.Direction;
23import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines;
25import tools.refinery.viatra.runtime.rete.aggregation.AbstractColumnAggregatorNode;
26import tools.refinery.viatra.runtime.rete.aggregation.GroupedMap;
27import tools.refinery.viatra.runtime.rete.network.ReteContainer;
28import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
29import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
30import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
31
32/**
33 * First-only timely implementation of the column aggregator node. Only timestamps of appearance are maintained for
34 * tuples instead of complete timelines.
35 * <br><br>
36 * Subclasses are responsible for implementing the aggregator architecture, and they must make use of the inner class {@link CumulativeAggregate}.
37 * <br><br>
38 * This node supports recursive aggregation.
39 *
40 * @author Tamas Szabo
41 * @since 2.4
42 */
43public abstract class FirstOnlyTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult>
44 extends AbstractColumnAggregatorNode<Domain, Accumulator, AggregateResult> {
45
46 protected final Map<Tuple, TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>>> memory;
47
48 public FirstOnlyTimelyColumnAggregatorNode(final ReteContainer reteContainer,
49 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
50 final TupleMask groupMask, final TupleMask columnMask) {
51 super(reteContainer, operator, groupMask, columnMask);
52 this.memory = CollectionsFactory.createMap();
53 // mailbox MUST be instantiated after the fields are all set
54 this.mailbox = instantiateMailbox();
55 }
56
57 protected static class CumulativeAggregate<Accumulator, AggregateResult> {
58 // the accumulator storing the aggregands
59 protected Accumulator accumulator;
60 // the aggregate result at the timestamp where this cumulative aggregate is stored
61 protected AggregateResult result;
62
63 private CumulativeAggregate(final Accumulator accumulator, final AggregateResult result) {
64 this.accumulator = accumulator;
65 this.result = result;
66 }
67
68 }
69
70 public Collection<Tuple> getGroups() {
71 return this.memory.keySet();
72 }
73
74 public AggregateResult getLastResult(final Tuple group) {
75 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> groupMap = this.memory.get(group);
76 if (groupMap == null) {
77 return null;
78 } else {
79 return groupMap.lastEntry().getValue().result;
80 }
81 }
82
83 public Timestamp getLastTimestamp(final Tuple group) {
84 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> groupMap = this.memory.get(group);
85 if (groupMap == null) {
86 return null;
87 } else {
88 return groupMap.lastEntry().getKey();
89 }
90 }
91
92 @Override
93 protected Mailbox instantiateMailbox() {
94 return new TimelyMailbox(this, this.reteContainer);
95 }
96
97 @Override
98 public void clear() {
99 this.mailbox.clear();
100 this.memory.clear();
101 this.children.clear();
102 this.childMailboxes.clear();
103 }
104
105 protected void propagateWithChecks(final Tuple group, final Timestamp timestamp,
106 final AggregateResult previousOldResult, final AggregateResult previousNewResult,
107 final AggregateResult currentOldResult, final AggregateResult currentNewResult) {
108 final boolean jumpDown = Objects.equals(previousNewResult, currentOldResult);
109 final boolean jumpUp = Objects.equals(previousOldResult, currentNewResult);
110 final boolean resultsDiffer = !Objects.equals(currentOldResult, currentNewResult);
111
112 // uniqueness enforcement is happening here
113 if ((resultsDiffer || jumpDown) && !Objects.equals(previousOldResult, currentOldResult)) {
114 propagate(Direction.DELETE, group, currentOldResult, timestamp);
115 }
116 if ((resultsDiffer || jumpUp) && !Objects.equals(previousNewResult, currentNewResult)) {
117 propagate(Direction.INSERT, group, currentNewResult, timestamp);
118 }
119 }
120
121 /**
122 * Returns the aggregation architecture-specific accumulator at the specified timestamp for the given group.
123 */
124 protected abstract Accumulator getAccumulator(final Tuple group, final Timestamp timestamp);
125
126 protected AggregateResult getResultRaw(final Tuple group, final Timestamp timestamp, final boolean lower) {
127 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group);
128 if (entryMap == null) {
129 return null;
130 } else {
131 CumulativeAggregate<Accumulator, AggregateResult> entry = null;
132 if (lower) {
133 final Entry<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> lowerEntry = entryMap
134 .lowerEntry(timestamp);
135 if (lowerEntry != null) {
136 entry = lowerEntry.getValue();
137 }
138 } else {
139 entry = entryMap.get(timestamp);
140 }
141 if (entry == null) {
142 return null;
143 } else {
144 return entry.result;
145 }
146 }
147 }
148
149 protected AggregateResult getResult(final Tuple group, final Timestamp timestamp, final boolean lower) {
150 final AggregateResult result = getResultRaw(group, timestamp, lower);
151 if (result == null) {
152 return NEUTRAL;
153 } else {
154 return result;
155 }
156 }
157
158 protected AggregateResult getResult(final Tuple group, final Timestamp timestamp) {
159 return getResult(group, timestamp, false);
160 }
161
162 protected void storeIfNotNeutral(final Tuple group, final Accumulator accumulator, final AggregateResult value,
163 final Timestamp timestamp) {
164 TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group);
165 if (operator.isNeutral(accumulator)) {
166 if (entryMap != null) {
167 entryMap.remove(timestamp);
168 if (entryMap.isEmpty()) {
169 this.memory.remove(group);
170 }
171 }
172 } else {
173 if (entryMap == null) {
174 entryMap = CollectionsFactory.createTreeMap();
175 this.memory.put(group, entryMap);
176 }
177 entryMap.put(timestamp, new CumulativeAggregate<>(accumulator, value));
178 }
179 }
180
181 @Override
182 public Tuple getAggregateTuple(final Tuple group) {
183 return tupleFromAggregateResult(group, getResult(group, Timestamp.ZERO));
184 }
185
186 @Override
187 public AggregateResult getAggregateResult(final Tuple group) {
188 return getResult(group, Timestamp.ZERO);
189 }
190
191 @Override
192 public Map<AggregateResult, Timeline<Timestamp>> getAggregateResultTimeline(final Tuple group) {
193 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group);
194 if (entryMap == null) {
195 return Collections.emptyMap();
196 } else {
197 final Map<AggregateResult, Timeline<Timestamp>> result = CollectionsFactory.createMap();
198 for (final Entry<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entry : entryMap
199 .descendingMap().entrySet()) {
200 result.put(entry.getValue().result, Timelines.createFrom(entry.getKey()));
201 }
202 return result;
203 }
204 }
205
206 @Override
207 public Map<Tuple, Timeline<Timestamp>> getAggregateTupleTimeline(final Tuple group) {
208 final Map<AggregateResult, Timeline<Timestamp>> resultTimelines = getAggregateResultTimeline(group);
209 return new GroupedMap<AggregateResult, Timeline<Timestamp>>(group, resultTimelines, this.runtimeContext);
210 }
211
212}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/Disconnectable.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/Disconnectable.java
new file mode 100644
index 00000000..7bbf74ea
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/Disconnectable.java
@@ -0,0 +1,26 @@
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.rete.boundary;
11
12/**
13 * For objects that connect a RETE implementation to the underlying model.
14 *
15 * @author Gabor Bergmann
16 *
17 */
18public interface Disconnectable {
19
20 /**
21 * Disconnects this rete engine component from the underlying model. Disconnecting enables the garbage collection
22 * mechanisms to dispose of the rete network.
23 */
24 void disconnect();
25
26}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputEnumeratorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputEnumeratorNode.java
new file mode 100644
index 00000000..51f89b52
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputEnumeratorNode.java
@@ -0,0 +1,209 @@
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.rete.boundary;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.context.IInputKey;
16import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
17import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
18import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
20import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
22import tools.refinery.viatra.runtime.matchers.util.Direction;
23import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
24import tools.refinery.viatra.runtime.rete.matcher.ReteEngine;
25import tools.refinery.viatra.runtime.rete.network.Network;
26import tools.refinery.viatra.runtime.rete.network.Receiver;
27import tools.refinery.viatra.runtime.rete.network.ReteContainer;
28import tools.refinery.viatra.runtime.rete.network.StandardNode;
29import tools.refinery.viatra.runtime.rete.network.Supplier;
30import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
31import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
32import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
33import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
34import tools.refinery.viatra.runtime.rete.remote.Address;
35
36/**
37 * An input node representing an enumerable extensional input relation and receiving external updates.
38 *
39 * <p>
40 * Contains those tuples that are in the extensional relation identified by the input key, and also conform to the
41 * global seed (if any).
42 *
43 * @author Bergmann Gabor
44 *
45 */
46public class ExternalInputEnumeratorNode extends StandardNode
47 implements Disconnectable, Receiver, IQueryRuntimeContextListener {
48
49 private IQueryRuntimeContext context = null;
50 private IInputKey inputKey;
51 private Tuple globalSeed;
52 private InputConnector inputConnector;
53 private Network network;
54 private Address<? extends Receiver> myAddress;
55 private boolean parallelExecutionEnabled;
56 /**
57 * @since 1.6
58 */
59 protected final Mailbox mailbox;
60 private final IQueryBackendContext qBackendContext;
61
62 public ExternalInputEnumeratorNode(ReteContainer reteContainer) {
63 super(reteContainer);
64 myAddress = Address.of(this);
65 network = reteContainer.getNetwork();
66 inputConnector = network.getInputConnector();
67 qBackendContext = network.getEngine().getBackendContext();
68 mailbox = instantiateMailbox();
69 reteContainer.registerClearable(mailbox);
70 }
71
72 /**
73 * Instantiates the {@link Mailbox} of this receiver. Subclasses may override this method to provide their own
74 * mailbox implementation.
75 *
76 * @return the mailbox
77 * @since 2.0
78 */
79 protected Mailbox instantiateMailbox() {
80 if (this.reteContainer.isTimelyEvaluation()) {
81 return new TimelyMailbox(this, this.reteContainer);
82 } else {
83 return new BehaviorChangingMailbox(this, this.reteContainer);
84 }
85 }
86
87 @Override
88 public Mailbox getMailbox() {
89 return this.mailbox;
90 }
91
92 public void connectThroughContext(ReteEngine engine, IInputKey inputKey, Tuple globalSeed) {
93 this.inputKey = inputKey;
94 this.globalSeed = globalSeed;
95 setTag(inputKey);
96
97 final IQueryRuntimeContext context = engine.getRuntimeContext();
98 if (!context.getMetaContext().isEnumerable(inputKey))
99 throw new IllegalArgumentException(this.getClass().getSimpleName()
100 + " only applicable for enumerable input keys; received instead " + inputKey);
101
102 this.context = context;
103 this.parallelExecutionEnabled = engine.isParallelExecutionEnabled();
104
105 engine.addDisconnectable(this);
106 context.addUpdateListener(inputKey, globalSeed, this);
107 }
108
109 @Override
110 public void disconnect() {
111 if (context != null) { // if connected
112 context.removeUpdateListener(inputKey, globalSeed, this);
113 context = null;
114 }
115 }
116
117 /**
118 * @since 2.2
119 */
120 protected Iterable<Tuple> getTuplesInternal() {
121 Iterable<Tuple> tuples = null;
122
123 if (context != null) { // if connected
124 if (globalSeed == null) {
125 tuples = context.enumerateTuples(inputKey, TupleMask.empty(inputKey.getArity()),
126 Tuples.staticArityFlatTupleOf());
127 } else {
128 final TupleMask mask = TupleMask.fromNonNullIndices(globalSeed);
129 tuples = context.enumerateTuples(inputKey, mask, mask.transform(globalSeed));
130 }
131 }
132
133 return tuples;
134 }
135
136 @Override
137 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
138 final Iterable<Tuple> tuples = getTuplesInternal();
139 if (tuples != null) {
140 for (final Tuple tuple : tuples) {
141 collector.add(tuple);
142 }
143 }
144 }
145
146 @Override
147 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
148 final Iterable<Tuple> tuples = getTuplesInternal();
149 if (tuples != null) {
150 for (final Tuple tuple : tuples) {
151 collector.put(tuple, Timestamp.INSERT_AT_ZERO_TIMELINE);
152 }
153 }
154 }
155
156 /* Update from runtime context */
157 @Override
158 public void update(IInputKey key, Tuple update, boolean isInsertion) {
159 if (parallelExecutionEnabled) {
160 // send back to myself as an official external update, and then propagate it transparently
161 network.sendExternalUpdate(myAddress, direction(isInsertion), update);
162 } else {
163 if (qBackendContext.areUpdatesDelayed()) {
164 // post the update into the mailbox of the node
165 mailbox.postMessage(direction(isInsertion), update, Timestamp.ZERO);
166 } else {
167 // just propagate the input
168 update(direction(isInsertion), update, Timestamp.ZERO);
169 }
170 // if the the update method is called from within a delayed execution,
171 // the following invocation will be a no-op
172 network.waitForReteTermination();
173 }
174 }
175
176 private static Direction direction(boolean isInsertion) {
177 return isInsertion ? Direction.INSERT : Direction.DELETE;
178 }
179
180 /* Self-addressed from network */
181 @Override
182 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
183 propagateUpdate(direction, updateElement, timestamp);
184 }
185
186 @Override
187 public void appendParent(Supplier supplier) {
188 throw new UnsupportedOperationException("Input nodes can't have parents");
189 }
190
191 @Override
192 public void removeParent(Supplier supplier) {
193 throw new UnsupportedOperationException("Input nodes can't have parents");
194 }
195
196 @Override
197 public Collection<Supplier> getParents() {
198 return Collections.emptySet();
199 }
200
201 public IInputKey getInputKey() {
202 return inputKey;
203 }
204
205 public Tuple getGlobalSeed() {
206 return globalSeed;
207 }
208
209}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputStatelessFilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputStatelessFilterNode.java
new file mode 100644
index 00000000..57e06911
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputStatelessFilterNode.java
@@ -0,0 +1,68 @@
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.rete.boundary;
10
11import tools.refinery.viatra.runtime.matchers.context.IInputKey;
12import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
15import tools.refinery.viatra.runtime.rete.matcher.ReteEngine;
16import tools.refinery.viatra.runtime.rete.network.ReteContainer;
17import tools.refinery.viatra.runtime.rete.single.FilterNode;
18
19/**
20 * A filter node representing a (stateless, typically non-enumerable) extensional input relation.
21 *
22 * <p> Contains those tuples of its parents, that (when transformed by a mask, if given) are present in the extensional relation identified by the input key.
23 *
24 * @author Bergmann Gabor
25 *
26 */
27public class ExternalInputStatelessFilterNode extends FilterNode implements Disconnectable {
28
29 IQueryRuntimeContext context = null;
30 IInputKey inputKey;
31 private InputConnector inputConnector;
32 private TupleMask mask;
33
34 public ExternalInputStatelessFilterNode(ReteContainer reteContainer, TupleMask mask) {
35 super(reteContainer);
36 this.mask = mask;
37 this.inputConnector = reteContainer.getNetwork().getInputConnector();
38 }
39
40 @Override
41 public boolean check(Tuple ps) {
42 if (mask != null)
43 ps = mask.transform(ps);
44 return context.containsTuple(inputKey, ps);
45 }
46
47
48 public void connectThroughContext(ReteEngine engine, IInputKey inputKey) {
49 this.inputKey = inputKey;
50 setTag(inputKey);
51
52 final IQueryRuntimeContext context = engine.getRuntimeContext();
53 if (!context.getMetaContext().isStateless(inputKey))
54 throw new IllegalArgumentException(
55 this.getClass().getSimpleName() +
56 " only applicable for stateless input keys; received instead " +
57 inputKey);
58
59 this.context = context;
60
61 engine.addDisconnectable(this);
62 }
63
64 @Override
65 public void disconnect() {
66 this.context = null;
67 }
68}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/InputConnector.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/InputConnector.java
new file mode 100644
index 00000000..c044850f
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/InputConnector.java
@@ -0,0 +1,208 @@
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.rete.boundary;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Map;
14import java.util.stream.Stream;
15
16import tools.refinery.viatra.runtime.matchers.context.IInputKey;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
20import tools.refinery.viatra.runtime.rete.network.Network;
21import tools.refinery.viatra.runtime.rete.network.Node;
22import tools.refinery.viatra.runtime.rete.recipes.InputFilterRecipe;
23import tools.refinery.viatra.runtime.rete.recipes.InputRecipe;
24import tools.refinery.viatra.runtime.rete.remote.Address;
25
26/**
27 * A class responsible for connecting input nodes to the runtime context.
28 *
29 * @author Bergmann Gabor
30 *
31 */
32public final class InputConnector {
33 Network network;
34
35 private Map<IInputKey, Map<Tuple, Address<ExternalInputEnumeratorNode>>> externalInputRoots = CollectionsFactory.createMap();
36
37// /*
38// * arity:1 used as simple entity constraints label is the object representing the type null label means all entities
39// * regardless of type (global supertype), if allowed
40// */
41// protected Map<Object, Address<? extends Tunnel>> unaryRoots = CollectionsFactory.getMap();
42// /*
43// * arity:3 (rel, from, to) used as VPM relation constraints null label means all relations regardless of type
44// * (global supertype)
45// */
46// protected Map<Object, Address<? extends Tunnel>> ternaryEdgeRoots = CollectionsFactory.getMap();
47// /*
48// * arity:2 (from, to) not used over VPM; can be used as EMF references for instance label is the object representing
49// * the type null label means all entities regardless of type if allowed (global supertype), if allowed
50// */
51// protected Map<Object, Address<? extends Tunnel>> binaryEdgeRoots = CollectionsFactory.getMap();
52//
53// protected Address<? extends Tunnel> containmentRoot = null;
54// protected Address<? extends Supplier> containmentTransitiveRoot = null;
55// protected Address<? extends Tunnel> instantiationRoot = null;
56// protected Address<? extends Supplier> instantiationTransitiveRoot = null;
57// protected Address<? extends Tunnel> generalizationRoot = null;
58// protected Address<? extends Supplier> generalizationTransitiveRoot = null;
59
60
61 public InputConnector(Network network) {
62 super();
63 this.network = network;
64 }
65
66
67 public Network getNetwork() {
68 return network;
69 }
70
71
72 /**
73 * Connects a given input filter node to the external input source.
74 */
75 public void connectInputFilter(InputFilterRecipe recipe, Node freshNode) {
76 final ExternalInputStatelessFilterNode inputNode = (ExternalInputStatelessFilterNode)freshNode;
77
78 IInputKey inputKey = (IInputKey) recipe.getInputKey();
79 inputNode.connectThroughContext(network.getEngine(), inputKey);
80 }
81
82
83 /**
84 * Connects a given input enumerator node to the external input source.
85 */
86 public void connectInput(InputRecipe recipe, Node freshNode) {
87 final ExternalInputEnumeratorNode inputNode = (ExternalInputEnumeratorNode)freshNode;
88
89 IInputKey inputKey = (IInputKey) recipe.getInputKey();
90 Tuple seed = nopSeed(inputKey); // no preseeding as of now
91 final Address<ExternalInputEnumeratorNode> freshAddress = Address.of(inputNode);
92 externalInputRoots.computeIfAbsent(inputKey, k -> CollectionsFactory.createMap()).put(seed, freshAddress);
93 inputNode.connectThroughContext(network.getEngine(), inputKey, seed);
94
95// final Address<Tunnel> freshAddress = Address.of((Tunnel)freshNode);
96// if (recipe instanceof TypeInputRecipe) {
97// final Object typeKey = ((TypeInputRecipe) recipe).getTypeKey();
98//
99// if (recipe instanceof UnaryInputRecipe) {
100// unaryRoots.put(typeKey, freshAddress);
101// new EntityFeeder(freshAddress, this, typeKey).feed();
102//// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) {
103//// Collection<? extends Object> subTypes = context.enumerateDirectUnarySubtypes(typeObject);
104////
105//// for (Object subType : subTypes) {
106//// Address<? extends Tunnel> subRoot = accessUnaryRoot(subType);
107//// network.connectRemoteNodes(subRoot, tn, true);
108//// }
109//// }
110// } else if (recipe instanceof BinaryInputRecipe) {
111// binaryEdgeRoots.put(typeKey, freshAddress);
112// externalInputRoots.put(rowKey, columnKey, freshAddress);
113// new ReferenceFeeder(freshAddress, this, typeKey).feed();
114// // if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) {
115// // Collection<? extends Object> subTypes = context.enumerateDirectTernaryEdgeSubtypes(typeObject);
116// //
117// // for (Object subType : subTypes) {
118// // Address<? extends Tunnel> subRoot = accessTernaryEdgeRoot(subType);
119// // network.connectRemoteNodes(subRoot, tn, true);
120// // }
121// // }
122// }
123//
124//
125// }
126
127 }
128
129// /**
130// * fetches the entity Root node under specified label; returns null if it doesn't exist yet
131// */
132// public Address<? extends Tunnel> getUnaryRoot(Object label) {
133// return unaryRoots.get(label);
134// }
135//
136// public Collection<Address<? extends Tunnel>> getAllUnaryRoots() {
137// return unaryRoots.values();
138// }
139//
140// /**
141// * fetches the relation Root node under specified label; returns null if it doesn't exist yet
142// */
143// public Address<? extends Tunnel> getTernaryEdgeRoot(Object label) {
144// return ternaryEdgeRoots.get(label);
145// }
146//
147// public Collection<Address<? extends Tunnel>> getAllTernaryEdgeRoots() {
148// return ternaryEdgeRoots.values();
149// }
150//
151// /**
152// * fetches the reference Root node under specified label; returns null if it doesn't exist yet
153// */
154// public Address<? extends Tunnel> getBinaryEdgeRoot(Object label) {
155// return binaryEdgeRoots.get(label);
156// }
157//
158// public Collection<Address<? extends Tunnel>> getAllBinaryEdgeRoots() {
159// return binaryEdgeRoots.values();
160// }
161//
162//
163// public Address<? extends Tunnel> getContainmentRoot() {
164// return containmentRoot;
165// }
166//
167//
168// public Address<? extends Supplier> getContainmentTransitiveRoot() {
169// return containmentTransitiveRoot;
170// }
171//
172//
173// public Address<? extends Tunnel> getInstantiationRoot() {
174// return instantiationRoot;
175// }
176//
177//
178// public Address<? extends Supplier> getInstantiationTransitiveRoot() {
179// return instantiationTransitiveRoot;
180// }
181//
182//
183// public Address<? extends Tunnel> getGeneralizationRoot() {
184// return generalizationRoot;
185// }
186
187
188 public Stream<Address<ExternalInputEnumeratorNode>> getAllExternalInputNodes() {
189 return externalInputRoots.values().stream().flatMap(map -> map.values().stream());
190 }
191 public Collection<Address<ExternalInputEnumeratorNode>> getAllExternalInputNodesForKey(IInputKey inputKey) {
192 return externalInputRoots.getOrDefault(inputKey, Collections.emptyMap()).values();
193 }
194 public Address<ExternalInputEnumeratorNode> getExternalInputNodeForKeyUnseeded(IInputKey inputKey) {
195 return externalInputRoots.getOrDefault(inputKey, Collections.emptyMap()).get(nopSeed(inputKey));
196 }
197 public Address<ExternalInputEnumeratorNode> getExternalInputNode(IInputKey inputKey, Tuple seed) {
198 if (seed == null) seed = nopSeed(inputKey);
199 return externalInputRoots.getOrDefault(inputKey, Collections.emptyMap()).get(seed);
200 }
201
202
203 Tuple nopSeed(IInputKey inputKey) {
204 return Tuples.flatTupleOf(new Object[inputKey.getArity()]);
205 }
206
207
208}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ReteBoundary.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ReteBoundary.java
new file mode 100644
index 00000000..fe9c795e
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ReteBoundary.java
@@ -0,0 +1,551 @@
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.rete.boundary;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
17import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
18import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.Direction;
22import tools.refinery.viatra.runtime.rete.matcher.ReteEngine;
23import tools.refinery.viatra.runtime.rete.network.Network;
24import tools.refinery.viatra.runtime.rete.network.ProductionNode;
25import tools.refinery.viatra.runtime.rete.network.Receiver;
26import tools.refinery.viatra.runtime.rete.network.ReteContainer;
27import tools.refinery.viatra.runtime.rete.network.Supplier;
28import tools.refinery.viatra.runtime.rete.remote.Address;
29import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery;
30import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
31
32/**
33 * Responsible for the storage, maintenance and communication of the nodes of the network that are accessible form the
34 * outside for various reasons.
35 *
36 * @author Gabor Bergmann
37 *
38 * <p> TODO: should eventually be merged into {@link InputConnector} and deleted
39 *
40 */
41public class ReteBoundary /*implements IPatternMatcherRuntimeContextListener*/ {
42
43 protected ReteEngine engine;
44 protected Network network;
45 protected ReteContainer headContainer;
46
47 public ReteContainer getHeadContainer() {
48 return headContainer;
49 }
50
51 protected final InputConnector inputConnector;
52
53
54 protected Map<SubPlan, Address<? extends Supplier>> subplanToAddressMapping;
55
56
57 /**
58 * SubPlans of parent nodes that have the key node as their child. For RETE --> SubPlan traceability, mainly at production
59 * nodes.
60 */
61 protected Map<Address<? extends Receiver>, Set<SubPlan>> parentPlansOfReceiver;
62
63 /**
64 * Prerequisite: engine has its network and framework fields initialized
65 */
66 public ReteBoundary(ReteEngine engine) {
67 super();
68 this.engine = engine;
69 this.network = engine.getReteNet();
70 this.headContainer = network.getHeadContainer();
71 inputConnector = network.getInputConnector();
72
73 this.parentPlansOfReceiver = CollectionsFactory.createMap();
74
75 // productionsScoped = new HashMap<GTPattern, Map<Map<Integer,Scope>,Address<? extends Production>>>();
76 subplanToAddressMapping = CollectionsFactory.createMap();
77
78 }
79
80 public Collection<? extends RecipeTraceInfo> getAllProductionNodes() {
81 return engine.getCompiler().getCachedCompiledQueries().values();
82 }
83
84// /**
85// * accesses the entity Root node under specified label; creates the node if it doesn't exist yet
86// */
87// public Address<? extends Tunnel> accessUnaryRoot(Object typeObject) {
88// Address<? extends Tunnel> tn;
89// tn = unaryRoots.get(typeObject);
90// if (tn == null) {
91// tn = headContainer.getProvisioner().newUniquenessEnforcerNode(1, typeObject);
92// unaryRoots.put(typeObject, tn);
93//
94// new EntityFeeder(tn, context, network, this, typeObject).feed();
95//
96// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) {
97// Collection<? extends Object> subTypes = context.enumerateDirectUnarySubtypes(typeObject);
98//
99// for (Object subType : subTypes) {
100// Address<? extends Tunnel> subRoot = accessUnaryRoot(subType);
101// network.connectRemoteNodes(subRoot, tn, true);
102// }
103// }
104//
105// }
106// return tn;
107// }
108//
109// /**
110// * accesses the relation Root node under specified label; creates the node if it doesn't exist yet
111// */
112// public Address<? extends Tunnel> accessTernaryEdgeRoot(Object typeObject) {
113// Address<? extends Tunnel> tn;
114// tn = ternaryEdgeRoots.get(typeObject);
115// if (tn == null) {
116// tn = headContainer.getProvisioner().newUniquenessEnforcerNode(3, typeObject);
117// ternaryEdgeRoots.put(typeObject, tn);
118//
119// new RelationFeeder(tn, context, network, this, typeObject).feed();
120//
121// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) {
122// Collection<? extends Object> subTypes = context.enumerateDirectTernaryEdgeSubtypes(typeObject);
123//
124// for (Object subType : subTypes) {
125// Address<? extends Tunnel> subRoot = accessTernaryEdgeRoot(subType);
126// network.connectRemoteNodes(subRoot, tn, true);
127// }
128// }
129// }
130// return tn;
131// }
132//
133// /**
134// * accesses the reference Root node under specified label; creates the node if it doesn't exist yet
135// */
136// public Address<? extends Tunnel> accessBinaryEdgeRoot(Object typeObject) {
137// Address<? extends Tunnel> tn;
138// tn = binaryEdgeRoots.get(typeObject);
139// if (tn == null) {
140// tn = headContainer.getProvisioner().newUniquenessEnforcerNode(2, typeObject);
141// binaryEdgeRoots.put(typeObject, tn);
142//
143// new ReferenceFeeder(tn, context, network, this, typeObject).feed();
144//
145// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) {
146// Collection<? extends Object> subTypes = context.enumerateDirectBinaryEdgeSubtypes(typeObject);
147//
148// for (Object subType : subTypes) {
149// Address<? extends Tunnel> subRoot = accessBinaryEdgeRoot(subType);
150// network.connectRemoteNodes(subRoot, tn, true);
151// }
152// }
153// }
154// return tn;
155// }
156//
157// /**
158// * accesses the special direct containment relation Root node; creates the node if it doesn't exist yet
159// */
160// public Address<? extends Tunnel> accessContainmentRoot() {
161// if (containmentRoot == null) {
162// // containment: relation quasi-type
163// containmentRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(2, "$containment");
164//
165// new ContainmentFeeder(containmentRoot, context, network, this).feed();
166// }
167// return containmentRoot;
168// }
169//
170// /**
171// * accesses the special transitive containment relation Root node; creates the node if it doesn't exist yet
172// */
173// public Address<? extends Supplier> accessContainmentTransitiveRoot() {
174// if (containmentTransitiveRoot == null) {
175// // transitive containment: derived
176// Address<? extends Tunnel> containmentTransitiveRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(
177// 2, "$containmentTransitive");
178// network.connectRemoteNodes(accessContainmentRoot(), containmentTransitiveRoot, true);
179//
180// final int[] actLI = { 1 };
181// final int arcLIw = 2;
182// final int[] actRI = { 0 };
183// final int arcRIw = 2;
184// Address<? extends IterableIndexer> jPrimarySlot = headContainer.getProvisioner().accessProjectionIndexer(
185// accessContainmentRoot(), new TupleMask(actLI, arcLIw));
186// Address<? extends IterableIndexer> jSecondarySlot = headContainer.getProvisioner().accessProjectionIndexer(
187// containmentTransitiveRoot, new TupleMask(actRI, arcRIw));
188//
189// final int[] actRIcomp = { 1 };
190// final int arcRIwcomp = 2;
191// TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp);
192//
193// Address<? extends Supplier> andCT = headContainer.getProvisioner().accessJoinNode(jPrimarySlot, jSecondarySlot,
194// complementerMask);
195//
196// final int[] mask = { 0, 2 };
197// final int maskw = 3;
198// Address<? extends Supplier> tr = headContainer.getProvisioner().accessTrimmerNode(andCT, new TupleMask(mask, maskw));
199// network.connectRemoteNodes(tr, containmentTransitiveRoot, true);
200//
201// this.containmentTransitiveRoot = containmentTransitiveRoot; // cast
202// // back
203// // to
204// // Supplier
205// }
206// return containmentTransitiveRoot;
207// }
208//
209// /**
210// * accesses the special instantiation relation Root node; creates the node if it doesn't exist yet
211// */
212// public Address<? extends Tunnel> accessInstantiationRoot() {
213// if (instantiationRoot == null) {
214// // instantiation: relation quasi-type
215// instantiationRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(2, "$instantiation");
216//
217// new InstantiationFeeder(instantiationRoot, context, network, this).feed();
218// }
219// return instantiationRoot;
220// }
221//
222// /**
223// * accesses the special transitive instantiation relation Root node; creates the node if it doesn't exist yet
224// * InstantiationTransitive = Instantiation o (Generalization)^*
225// */
226// public Address<? extends Supplier> accessInstantiationTransitiveRoot() {
227// if (instantiationTransitiveRoot == null) {
228// // transitive instantiation: derived
229// Address<? extends Tunnel> instantiationTransitiveRoot = headContainer.getProvisioner()
230// .newUniquenessEnforcerNode(2, "$instantiationTransitive");
231// network.connectRemoteNodes(accessInstantiationRoot(), instantiationTransitiveRoot, true);
232//
233// final int[] actLI = { 1 };
234// final int arcLIw = 2;
235// final int[] actRI = { 0 };
236// final int arcRIw = 2;
237// Address<? extends IterableIndexer> jPrimarySlot = headContainer.getProvisioner().accessProjectionIndexer(
238// accessGeneralizationRoot(), new TupleMask(actLI, arcLIw));
239// Address<? extends Indexer> jSecondarySlot = headContainer.getProvisioner().accessProjectionIndexer(
240// instantiationTransitiveRoot, new TupleMask(actRI, arcRIw));
241//
242// final int[] actRIcomp = { 1 };
243// final int arcRIwcomp = 2;
244// TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp);
245//
246// Address<? extends Supplier> andCT = headContainer.getProvisioner().accessJoinNode(jPrimarySlot, jSecondarySlot,
247// complementerMask);
248//
249// final int[] mask = { 0, 2 };
250// final int maskw = 3;
251// Address<? extends Supplier> tr = headContainer.getProvisioner().accessTrimmerNode(andCT,
252// new TupleMask(mask, maskw));
253// network.connectRemoteNodes(tr, instantiationTransitiveRoot, true);
254//
255// this.instantiationTransitiveRoot = instantiationTransitiveRoot; // cast
256// // back
257// // to
258// // Supplier
259// }
260// return instantiationTransitiveRoot;
261// }
262//
263// /**
264// * accesses the special generalization relation Root node; creates the node if it doesn't exist yet
265// */
266// public Address<? extends Tunnel> accessGeneralizationRoot() {
267// if (generalizationRoot == null) {
268// // generalization: relation quasi-type
269// generalizationRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(2, "$generalization");
270//
271// new GeneralizationFeeder(generalizationRoot, context, network, this).feed();
272// }
273// return generalizationRoot;
274// }
275//
276// /**
277// * accesses the special transitive containment relation Root node; creates the node if it doesn't exist yet
278// */
279// public Address<? extends Supplier> accessGeneralizationTransitiveRoot() {
280// if (generalizationTransitiveRoot == null) {
281// // transitive generalization: derived
282// Address<? extends Tunnel> generalizationTransitiveRoot = headContainer.getProvisioner()
283// .newUniquenessEnforcerNode(2, "$generalizationTransitive");
284// network.connectRemoteNodes(accessGeneralizationRoot(), generalizationTransitiveRoot, true);
285//
286// final int[] actLI = { 1 };
287// final int arcLIw = 2;
288// final int[] actRI = { 0 };
289// final int arcRIw = 2;
290// Address<? extends IterableIndexer> jPrimarySlot = headContainer.getProvisioner().accessProjectionIndexer(
291// accessGeneralizationRoot(), new TupleMask(actLI, arcLIw));
292// Address<? extends Indexer> jSecondarySlot = headContainer.getProvisioner().accessProjectionIndexer(
293// generalizationTransitiveRoot, new TupleMask(actRI, arcRIw));
294//
295// final int[] actRIcomp = { 1 };
296// final int arcRIwcomp = 2;
297// TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp);
298//
299// Address<? extends Supplier> andCT = headContainer.getProvisioner().accessJoinNode(jPrimarySlot, jSecondarySlot,
300// complementerMask);
301//
302// final int[] mask = { 0, 2 };
303// final int maskw = 3;
304// Address<? extends Supplier> tr = headContainer.getProvisioner().accessTrimmerNode(andCT, new TupleMask(mask, maskw));
305// network.connectRemoteNodes(tr, generalizationTransitiveRoot, true);
306//
307// this.generalizationTransitiveRoot = generalizationTransitiveRoot; // cast
308// // back
309// // to
310// // Supplier
311// }
312// return generalizationTransitiveRoot;
313// }
314
315 // /**
316 // * Registers and publishes a supplier under specified label.
317 // */
318 // public void publishSupplier(Supplier s, Object label)
319 // {
320 // publishedSuppliers.put(label, s);
321 // }
322 //
323 // /**
324 // * fetches the production node under specified label;
325 // * returns null if it doesn't exist yet
326 // */
327 // public Production getProductionNode(Object label)
328 // {
329 // return productions.get(label);
330 // }
331 //
332 // /**
333 // * fetches the published supplier under specified label;
334 // * returns null if it doesn't exist yet
335 // */
336 // public Supplier getPublishedSupplier(Object label)
337 // {
338 // return publishedSuppliers.get(label);
339 // }
340
341 /**
342 * accesses the production node for specified pattern; builds pattern matcher if it doesn't exist yet
343 * @throws ViatraQueryRuntimeException
344 */
345 public synchronized RecipeTraceInfo accessProductionTrace(PQuery query)
346 {
347 final CompiledQuery compiled = engine.getCompiler().getCompiledForm(query);
348 return compiled;
349// RecipeTraceInfo pn;
350// pn = queryPlans.get(query);
351// if (pn == null) {
352// pn = construct(query);
353// TODO handle recursion by reinterpret-RecipeTrace
354// queryPlans.put(query, pn);
355// if (pn == null) {
356// String[] args = { query.toString() };
357// throw new RetePatternBuildException("Unsuccessful planning of RETE construction recipe for query {1}",
358// args, "Could not create RETE recipe plan.", query);
359// }
360// }
361// return pn;
362 }
363 /**
364 * accesses the production node for specified pattern; builds pattern matcher if it doesn't exist yet
365 * @throws ViatraQueryRuntimeException
366 */
367 public synchronized Address<? extends ProductionNode> accessProductionNode(PQuery query) {
368 final RecipeTraceInfo productionTrace = accessProductionTrace(query);
369 return (Address<? extends ProductionNode>) headContainer.getProvisioner().getOrCreateNodeByRecipe(productionTrace);
370 }
371
372// /**
373// * creates the production node for the specified pattern Contract: only call from the builder (through Buildable)
374// * responsible for building this pattern
375// *
376// * @throws PatternMatcherCompileTimeException
377// * if production node is already created
378// */
379// public synchronized Address<? extends Production> createProductionInternal(PQuery gtPattern)
380// throws QueryPlannerException {
381// if (queryPlans.containsKey(gtPattern)) {
382// String[] args = { gtPattern.toString() };
383// throw new RetePatternBuildException("Multiple creation attempts of production node for {1}", args,
384// "Duplicate RETE production node.", gtPattern);
385// }
386//
387// Map<String, Integer> posMapping = engine.getBuilder().getPosMapping(gtPattern);
388// Address<? extends Production> pn = headContainer.getProvisioner().newProductionNode(posMapping, gtPattern);
389// queryPlans.put(gtPattern, pn);
390// context.reportPatternDependency(gtPattern);
391//
392// return pn;
393// }
394
395 // /**
396 // * accesses the production node for specified pattern and scope map; creates the node if
397 // * it doesn't exist yet
398 // */
399 // public synchronized Address<? extends Production> accessProductionScoped(
400 // GTPattern gtPattern, Map<Integer, Scope> additionalScopeMap) throws PatternMatcherCompileTimeException {
401 // if (additionalScopeMap.isEmpty()) return accessProduction(gtPattern);
402 //
403 // Address<? extends Production> pn;
404 //
405 // Map<Map<Integer, Scope>, Address<? extends Production>> scopes = productionsScoped.get(gtPattern);
406 // if (scopes == null) {
407 // scopes = new HashMap<Map<Integer, Scope>, Address<? extends Production>>();
408 // productionsScoped.put(gtPattern, scopes);
409 // }
410 //
411 // pn = scopes.get(additionalScopeMap);
412 // if (pn == null) {
413 // Address<? extends Production> unscopedProduction = accessProduction(gtPattern);
414 //
415 // HashMap<Object, Integer> posMapping = headContainer.resolveLocal(unscopedProduction).getPosMapping();
416 // pn = headContainer.getLibrary().newProductionNode(posMapping);
417 // scopes.put(additionalScopeMap, pn);
418 //
419 // constructScoper(unscopedProduction, additionalScopeMap, pn);
420 // }
421 // return pn;
422 // }
423
424 // protected void constructScoper(
425 // Address<? extends Production> unscopedProduction,
426 // Map<Integer, Scope> additionalScopeMap,
427 // Address<? extends Production> production)
428 // throws PatternMatcherCompileTimeException {
429 // engine.reteNet.waitForReteTermination();
430 // engine.builder.constructScoper(unscopedProduction, additionalScopeMap, production);
431 // }
432
433 // /**
434 // * Invalidates the subnet constructed for the recognition of a given
435 // pattern.
436 // * The pattern matcher will have to be rebuilt.
437 // * @param gtPattern the pattern whose matcher subnet should be invalidated
438 // */
439 // public void invalidatePattern(GTPattern gtPattern) {
440 // Production production = null;
441 // try {
442 // production = accessProduction(gtPattern);
443 // } catch (PatternMatcherCompileTimeException e) {
444 // // this should not occur here, since we already have a production node
445 // e.printStackTrace();
446 // }
447 //
448 // production.tearOff();
449 // //production.setDirty(true);
450 // }
451
452 // updaters for change notification
453 // if the corresponding rete input isn't created yet, call is ignored
454
455 private static Direction direction(boolean isInsertion) {
456 return isInsertion ? Direction.INSERT : Direction.DELETE;
457 }
458
459// @Override
460// public void updateUnary(boolean isInsertion, Object entity, Object typeObject) {
461// Address<? extends Tunnel> root = inputConnector.getUnaryRoot(typeObject);
462// if (root != null) {
463// network.sendExternalUpdate(root, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(entity)));
464// if (!engine.isParallelExecutionEnabled())
465// network.waitForReteTermination();
466// }
467// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) {
468// for (Object superType : context.enumerateDirectUnarySupertypes(typeObject)) {
469// updateUnary(isInsertion, entity, superType);
470// }
471// }
472// }
473//
474// @Override
475// public void updateTernaryEdge(boolean isInsertion, Object relation, Object from, Object to, Object typeObject) {
476// Address<? extends Tunnel> root = inputConnector.getTernaryEdgeRoot(typeObject);
477// if (root != null) {
478// network.sendExternalUpdate(root, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(relation), inputConnector.wrapElement(from),
479// inputConnector.wrapElement(to)));
480// if (!engine.isParallelExecutionEnabled())
481// network.waitForReteTermination();
482// }
483// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) {
484// for (Object superType : context.enumerateDirectTernaryEdgeSupertypes(typeObject)) {
485// updateTernaryEdge(isInsertion, relation, from, to, superType);
486// }
487// }
488// }
489// @Override
490// public void updateBinaryEdge(boolean isInsertion, Object from, Object to, Object typeObject) {
491// Address<? extends Tunnel> root = inputConnector.getBinaryEdgeRoot(typeObject);
492// if (root != null) {
493// network.sendExternalUpdate(root, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(from), inputConnector.wrapElement(to)));
494// if (!engine.isParallelExecutionEnabled())
495// network.waitForReteTermination();
496// }
497// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) {
498// for (Object superType : context.enumerateDirectBinaryEdgeSupertypes(typeObject)) {
499// updateBinaryEdge(isInsertion, from, to, superType);
500// }
501// }
502// }
503//
504// @Override
505// public void updateContainment(boolean isInsertion, Object container, Object element) {
506// final Address<? extends Tunnel> containmentRoot = inputConnector.getContainmentRoot();
507// if (containmentRoot != null) {
508// network.sendExternalUpdate(containmentRoot, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(container),
509// inputConnector.wrapElement(element)));
510// if (!engine.isParallelExecutionEnabled())
511// network.waitForReteTermination();
512// }
513// }
514//
515// @Override
516// public void updateInstantiation(boolean isInsertion, Object parent, Object child) {
517// final Address<? extends Tunnel> instantiationRoot = inputConnector.getInstantiationRoot();
518// if (instantiationRoot != null) {
519// network.sendExternalUpdate(instantiationRoot, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(parent),
520// inputConnector.wrapElement(child)));
521// if (!engine.isParallelExecutionEnabled())
522// network.waitForReteTermination();
523// }
524// }
525//
526// @Override
527// public void updateGeneralization(boolean isInsertion, Object parent, Object child) {
528// final Address<? extends Tunnel> generalizationRoot = inputConnector.getGeneralizationRoot();
529// if (generalizationRoot != null) {
530// network.sendExternalUpdate(generalizationRoot, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(parent),
531// inputConnector.wrapElement(child)));
532// if (!engine.isParallelExecutionEnabled())
533// network.waitForReteTermination();
534// }
535// }
536
537 // no wrapping needed!
538 public void notifyEvaluator(Address<? extends Receiver> receiver, Tuple tuple) {
539 network.sendExternalUpdate(receiver, Direction.INSERT, tuple);
540 if (!engine.isParallelExecutionEnabled())
541 network.waitForReteTermination();
542 }
543
544 public void mapPlanToAddress(SubPlan plan, Address<? extends Supplier> handle) {
545 subplanToAddressMapping.put(plan, handle);
546 }
547
548 public Address<? extends Supplier> getAddress(SubPlan plan) {
549 return subplanToAddressMapping.get(plan);
550 }
551}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/RetePatternBuildException.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/RetePatternBuildException.java
new file mode 100644
index 00000000..bd1b219e
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/RetePatternBuildException.java
@@ -0,0 +1,50 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2009 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.rete.construction;
11
12import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
13
14/**
15 * A problem has occurred during the construction of the RETE net.
16 *
17 * @author Gabor Bergmann
18 *
19 */
20public class RetePatternBuildException extends QueryProcessingException {
21
22 private static final long serialVersionUID = 6966585498204577548L;
23
24 /**
25 * @param message
26 * The template of the exception message
27 * @param context
28 * The data elements to be used to instantiate the template. Can be null if no context parameter is
29 * defined
30 * @param patternDescription
31 * the PatternDescription where the exception occurred
32 */
33 public RetePatternBuildException(String message, String[] context, String shortMessage, Object patternDescription) {
34 super(message, context, shortMessage, patternDescription);
35 }
36
37 /**
38 * @param message
39 * The template of the exception message
40 * @param context
41 * The data elements to be used to instantiate the template. Can be null if no context parameter is
42 * defined
43 * @param patternDescription
44 * the PatternDescription where the exception occurred
45 */
46 public RetePatternBuildException(String message, String[] context, String shortMessage, Object patternDescription,
47 Throwable cause) {
48 super(message, context, shortMessage, patternDescription, cause);
49 }
50}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/BasicLinearLayout.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/BasicLinearLayout.java
new file mode 100644
index 00000000..bd22e1a0
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/BasicLinearLayout.java
@@ -0,0 +1,171 @@
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.rete.construction.basiclinear;
11
12import java.util.Arrays;
13import java.util.Collections;
14import java.util.Set;
15
16import org.apache.log4j.Logger;
17import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
18import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
19import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
20import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy;
21import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
22import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory;
23import tools.refinery.viatra.runtime.matchers.planning.helpers.BuildHelper;
24import tools.refinery.viatra.runtime.matchers.planning.operations.PApply;
25import tools.refinery.viatra.runtime.matchers.planning.operations.PProject;
26import tools.refinery.viatra.runtime.matchers.planning.operations.PStart;
27import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint;
28import tools.refinery.viatra.runtime.matchers.psystem.PBody;
29import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
30import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
31import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint;
32import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality;
33import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
34import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation;
35import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
36import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
37import tools.refinery.viatra.runtime.rete.construction.RetePatternBuildException;
38
39/**
40 * Basic layout that builds a linear RETE net based on a heuristic ordering of constraints.
41 *
42 * @author Gabor Bergmann
43 *
44 */
45public class BasicLinearLayout implements IQueryPlannerStrategy {
46
47 //SubPlanProcessor planProcessor = new SubPlanProcessor();
48
49 private IQueryBackendHintProvider hintProvider;
50 private IQueryBackendContext bContext;
51 /**
52 * @param bContext
53 * @since 1.5
54 */
55 public BasicLinearLayout(IQueryBackendContext bContext) {
56 this.bContext = bContext;
57 this.hintProvider = bContext.getHintProvider();
58 }
59
60 @Override
61 public SubPlan plan(final PBody pSystem, Logger logger, IQueryMetaContext context) {
62 SubPlanFactory planFactory = new SubPlanFactory(pSystem);
63 PQuery query = pSystem.getPattern();
64 //planProcessor.setCompiler(compiler);
65 try {
66 logger.debug(String.format(
67 "%s: patternbody build started for %s",
68 getClass().getSimpleName(),
69 query.getFullyQualifiedName()));
70
71 // STARTING THE LINE
72 SubPlan plan = planFactory.createSubPlan(new PStart());
73
74 Set<PConstraint> pQueue = CollectionsFactory.createSet(pSystem.getConstraints());
75
76 // MAIN LOOP
77 while (!pQueue.isEmpty()) {
78 PConstraint pConstraint = Collections.min(pQueue,
79 new OrderingHeuristics(plan, context)); // pQueue.iterator().next();
80 pQueue.remove(pConstraint);
81
82 // if we have no better option than an unready deferred constraint, raise error
83 if (pConstraint instanceof DeferredPConstraint) {
84 final DeferredPConstraint deferred = (DeferredPConstraint) pConstraint;
85 if (!deferred.isReadyAt(plan, context)) {
86 raiseForeverDeferredError(deferred, plan, context);
87 }
88 }
89 // TODO integrate the check above in SubPlan / POperation??
90
91 // replace incumbent plan with its child
92 plan = planFactory.createSubPlan(new PApply(pConstraint), plan);
93 }
94
95 // PROJECT TO PARAMETERS
96 SubPlan finalPlan = planFactory.createSubPlan(new PProject(pSystem.getSymbolicParameterVariables()), plan);
97
98 // FINAL CHECK, whether all exported variables are present + all constraint satisfied
99 BuildHelper.finalCheck(pSystem, finalPlan, context);
100 // TODO integrate the check above in SubPlan / POperation
101
102 logger.debug(String.format(
103 "%s: patternbody query plan concluded for %s as: %s",
104 getClass().getSimpleName(),
105 query.getFullyQualifiedName(),
106 finalPlan.toLongString()));
107
108 return finalPlan;
109
110 } catch (RetePatternBuildException ex) {
111 ex.setPatternDescription(query);
112 throw ex;
113 }
114 }
115
116 /**
117 * Called when the constraint is not ready, but cannot be deferred further.
118 *
119 * @param plan
120 * @throws RetePatternBuildException
121 * to indicate the error in detail.
122 */
123 private void raiseForeverDeferredError(DeferredPConstraint constraint, SubPlan plan, IQueryMetaContext context) {
124 if (constraint instanceof Equality) {
125 raiseForeverDeferredError((Equality)constraint, plan, context);
126 } else if (constraint instanceof ExportedParameter) {
127 raiseForeverDeferredError((ExportedParameter)constraint, plan, context);
128 } else if (constraint instanceof ExpressionEvaluation) {
129 raiseForeverDeferredError((ExpressionEvaluation)constraint, plan, context);
130 } else if (constraint instanceof VariableDeferredPConstraint) {
131 raiseForeverDeferredError(constraint, plan, context);
132 }
133 }
134
135 private void raiseForeverDeferredError(Equality constraint, SubPlan plan, IQueryMetaContext context) {
136 String[] args = { constraint.getWho().toString(), constraint.getWithWhom().toString() };
137 String msg = "Cannot express equality of variables {1} and {2} if neither of them is deducable.";
138 String shortMsg = "Equality between undeducible variables.";
139 throw new RetePatternBuildException(msg, args, shortMsg, null);
140 }
141 private void raiseForeverDeferredError(ExportedParameter constraint, SubPlan plan, IQueryMetaContext context) {
142 String[] args = { constraint.getParameterName() };
143 String msg = "Pattern Graph Search terminated incompletely: "
144 + "exported pattern variable {1} could not be determined based on the pattern constraints. "
145 + "HINT: certain constructs (e.g. negative patterns or check expressions) cannot output symbolic parameters.";
146 String shortMsg = "Could not deduce value of parameter";
147 throw new RetePatternBuildException(msg, args, shortMsg, null);
148 }
149 private void raiseForeverDeferredError(ExpressionEvaluation constraint, SubPlan plan, IQueryMetaContext context) {
150 if (constraint.checkTypeSafety(plan, context) == null) {
151 raiseForeverDeferredError(constraint, plan);
152 } else {
153 String[] args = { toString(), constraint.checkTypeSafety(plan, context).toString() };
154 String msg = "The checking of pattern constraint {1} cannot be deferred further, but variable {2} is still not type safe. "
155 + "HINT: the incremental matcher is not an equation solver, please make sure that all variable values are deducible.";
156 String shortMsg = "Could not check all constraints due to undeducible type restrictions";
157 throw new RetePatternBuildException(msg, args, shortMsg, null);
158 }
159 }
160 private void raiseForeverDeferredError(VariableDeferredPConstraint constraint, SubPlan plan) {
161 Set<PVariable> missing = CollectionsFactory.createSet(constraint.getDeferringVariables());//new HashSet<PVariable>(getDeferringVariables());
162 missing.removeAll(plan.getVisibleVariables());
163 String[] args = { toString(), Arrays.toString(missing.toArray()) };
164 String msg = "The checking of pattern constraint {1} requires the values of variables {2}, but it cannot be deferred further. "
165 + "HINT: the incremental matcher is not an equation solver, please make sure that all variable values are deducible.";
166 String shortMsg = "Could not check all constraints due to undeducible variables";
167 throw new RetePatternBuildException(msg, args, shortMsg, null);
168 }
169
170
171}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/OrderingHeuristics.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/OrderingHeuristics.java
new file mode 100644
index 00000000..2b4e8890
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/OrderingHeuristics.java
@@ -0,0 +1,90 @@
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.rete.construction.basiclinear;
11
12import java.util.Comparator;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
16import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
17import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint;
18import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint;
19import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
20import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
21import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
22import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
23import tools.refinery.viatra.runtime.rete.util.OrderingCompareAgent;
24
25/**
26 * @author Gabor Bergmann
27 *
28 */
29public class OrderingHeuristics implements Comparator<PConstraint> {
30 private SubPlan plan;
31 private IQueryMetaContext context;
32
33 public OrderingHeuristics(SubPlan plan, IQueryMetaContext context) {
34 super();
35 this.plan = plan;
36 this.context = context;
37 }
38
39 @Override
40 public int compare(PConstraint o1, PConstraint o2) {
41 return new OrderingCompareAgent<PConstraint>(o1, o2) {
42 @Override
43 protected void doCompare() {
44 boolean temp = consider(preferTrue(isConstant(a), isConstant(b)))
45 && consider(preferTrue(isReady(a), isReady(b)));
46 if (!temp)
47 return;
48
49 Set<PVariable> bound1 = boundVariables(a);
50 Set<PVariable> bound2 = boundVariables(b);
51 swallowBoolean(temp && consider(preferTrue(isBound(a, bound1), isBound(b, bound2)))
52 && consider(preferMore(degreeBound(a, bound1), degreeBound(b, bound2)))
53 && consider(preferLess(degreeFree(a, bound1), degreeFree(b, bound2)))
54
55 // tie breaking
56 && consider(preferLess(a.getMonotonousID(), b.getMonotonousID())) // this is hopefully deterministic
57 && consider(preferLess(System.identityHashCode(a), System.identityHashCode(b))));
58 }
59 }.compare();
60 }
61
62 boolean isConstant(PConstraint o) {
63 return (o instanceof ConstantValue);
64 }
65
66 boolean isReady(PConstraint o) {
67 return (o instanceof EnumerablePConstraint)
68 || (o instanceof DeferredPConstraint && ((DeferredPConstraint) o)
69 .isReadyAt(plan, context));
70 }
71
72 Set<PVariable> boundVariables(PConstraint o) {
73 Set<PVariable> boundVariables = CollectionsFactory.createSet(o.getAffectedVariables());
74 boundVariables.retainAll(plan.getVisibleVariables());
75 return boundVariables;
76 }
77
78 boolean isBound(PConstraint o, Set<PVariable> boundVariables) {
79 return boundVariables.size() == o.getAffectedVariables().size();
80 }
81
82 int degreeBound(PConstraint o, Set<PVariable> boundVariables) {
83 return boundVariables.size();
84 }
85
86 int degreeFree(PConstraint o, Set<PVariable> boundVariables) {
87 return o.getAffectedVariables().size() - boundVariables.size();
88 }
89
90}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java
new file mode 100644
index 00000000..da2fb432
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java
@@ -0,0 +1,390 @@
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.rete.construction.plancompiler;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.HashMap;
15import java.util.LinkedHashSet;
16import java.util.LinkedList;
17import java.util.List;
18import java.util.Map;
19import java.util.Map.Entry;
20import java.util.Set;
21import java.util.SortedSet;
22import java.util.TreeSet;
23
24import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
25import tools.refinery.viatra.runtime.matchers.context.IInputKey;
26import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
27import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
28import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
29import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper;
30import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint;
31import tools.refinery.viatra.runtime.matchers.psystem.PBody;
32import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
33import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
34import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
35import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
36import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
37import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration;
38import tools.refinery.viatra.runtime.rete.recipes.EqualityFilterRecipe;
39import tools.refinery.viatra.runtime.rete.recipes.IndexerBasedAggregatorRecipe;
40import tools.refinery.viatra.runtime.rete.recipes.IndexerRecipe;
41import tools.refinery.viatra.runtime.rete.recipes.JoinRecipe;
42import tools.refinery.viatra.runtime.rete.recipes.Mask;
43import tools.refinery.viatra.runtime.rete.recipes.MonotonicityInfo;
44import tools.refinery.viatra.runtime.rete.recipes.ProductionRecipe;
45import tools.refinery.viatra.runtime.rete.recipes.ProjectionIndexerRecipe;
46import tools.refinery.viatra.runtime.rete.recipes.RecipesFactory;
47import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
48import tools.refinery.viatra.runtime.rete.recipes.SingleColumnAggregatorRecipe;
49import tools.refinery.viatra.runtime.rete.recipes.TrimmerRecipe;
50import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper;
51import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery;
52import tools.refinery.viatra.runtime.rete.traceability.CompiledSubPlan;
53import tools.refinery.viatra.runtime.rete.traceability.PlanningTrace;
54import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
55import tools.refinery.viatra.runtime.rete.util.ReteHintOptions;
56
57/**
58 * @author Bergmann Gabor
59 *
60 */
61public class CompilerHelper {
62
63 private CompilerHelper() {/*Utility class constructor*/}
64
65 static final RecipesFactory FACTORY = RecipesFactory.eINSTANCE;
66
67 /**
68 * Makes sure that all variables in the tuple are different so that it can be used as {@link CompiledSubPlan}. If a
69 * variable occurs multiple times, equality checks are applied and then the results are trimmed so that duplicates
70 * are hidden. If no manipulation is necessary, the original trace is returned.
71 *
72 * <p>
73 * to be used whenever a constraint introduces new variables.
74 */
75 public static PlanningTrace checkAndTrimEqualVariables(SubPlan plan, final PlanningTrace coreTrace) {
76 // are variables in the constraint all different?
77 final List<PVariable> coreVariablesTuple = coreTrace.getVariablesTuple();
78 final int constraintArity = coreVariablesTuple.size();
79 final int distinctVariables = coreTrace.getPosMapping().size();
80 if (constraintArity == distinctVariables) {
81 // all variables occur exactly once in tuple
82 return coreTrace;
83 } else { // apply equality checks and trim
84
85 // find the positions in the tuple for each variable
86 Map<PVariable, SortedSet<Integer>> posMultimap = new HashMap<PVariable, SortedSet<Integer>>();
87 List<PVariable> trimmedVariablesTuple = new ArrayList<PVariable>(distinctVariables);
88 int[] trimIndices = new int[distinctVariables];
89 for (int i = 0; i < constraintArity; ++i) {
90 final PVariable variable = coreVariablesTuple.get(i);
91 SortedSet<Integer> indexSet = posMultimap.get(variable);
92 if (indexSet == null) { // first occurrence of variable
93 indexSet = new TreeSet<Integer>();
94 posMultimap.put(variable, indexSet);
95
96 // this is the first occurrence, set up trimming
97 trimIndices[trimmedVariablesTuple.size()] = i;
98 trimmedVariablesTuple.add(variable);
99 }
100 indexSet.add(i);
101 }
102
103 // construct equality checks for each variable occurring multiple times
104 PlanningTrace lastTrace = coreTrace;
105 for (Entry<PVariable, SortedSet<Integer>> entry : posMultimap.entrySet()) {
106 if (entry.getValue().size() > 1) {
107 EqualityFilterRecipe equalityFilterRecipe = FACTORY.createEqualityFilterRecipe();
108 equalityFilterRecipe.setParent(lastTrace.getRecipe());
109 equalityFilterRecipe.getIndices().addAll(entry.getValue());
110 lastTrace = new PlanningTrace(plan, coreVariablesTuple, equalityFilterRecipe, lastTrace);
111 }
112 }
113
114 // trim so that each variable occurs only once
115 TrimmerRecipe trimmerRecipe = FACTORY.createTrimmerRecipe();
116 trimmerRecipe.setParent(lastTrace.getRecipe());
117 trimmerRecipe.setMask(tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper
118 .mask(constraintArity, trimIndices));
119 return new PlanningTrace(plan, trimmedVariablesTuple, trimmerRecipe, lastTrace);
120 }
121 }
122
123 /**
124 * Extracts the variable list representation of the variables tuple.
125 */
126 public static List<PVariable> convertVariablesTuple(EnumerablePConstraint constraint) {
127 return convertVariablesTuple(constraint.getVariablesTuple());
128 }
129
130 /**
131 * Extracts the variable list representation of the variables tuple.
132 */
133 public static List<PVariable> convertVariablesTuple(Tuple variablesTuple) {
134 List<PVariable> result = new ArrayList<PVariable>();
135 for (Object o : variablesTuple.getElements())
136 result.add((PVariable) o);
137 return result;
138 }
139
140 /**
141 * Returns a compiled indexer trace according to a mask
142 */
143 public static RecipeTraceInfo makeIndexerTrace(SubPlan planToCompile, PlanningTrace parentTrace, TupleMask mask) {
144 final ReteNodeRecipe parentRecipe = parentTrace.getRecipe();
145 if (parentRecipe instanceof IndexerBasedAggregatorRecipe
146 || parentRecipe instanceof SingleColumnAggregatorRecipe)
147 throw new IllegalArgumentException(
148 "Cannot take projection indexer of aggregator node at plan " + planToCompile);
149 IndexerRecipe recipe = RecipesHelper.projectionIndexerRecipe(parentRecipe, toRecipeMask(mask));
150 // final List<PVariable> maskedVariables = mask.transform(parentTrace.getVariablesTuple());
151 return new PlanningTrace(planToCompile, /* maskedVariables */ parentTrace.getVariablesTuple(), recipe,
152 parentTrace);
153 // TODO add specialized indexer trace info?
154 }
155
156 /**
157 * Creates a trimmer that keeps selected variables only.
158 */
159 protected static TrimmerRecipe makeTrimmerRecipe(final PlanningTrace compiledParent,
160 List<PVariable> projectedVariables) {
161 final Mask projectionMask = makeProjectionMask(compiledParent, projectedVariables);
162 final TrimmerRecipe trimmerRecipe = ReteRecipeCompiler.FACTORY.createTrimmerRecipe();
163 trimmerRecipe.setParent(compiledParent.getRecipe());
164 trimmerRecipe.setMask(projectionMask);
165 return trimmerRecipe;
166 }
167
168 public static Mask makeProjectionMask(final PlanningTrace compiledParent, Iterable<PVariable> projectedVariables) {
169 List<Integer> projectionSourceIndices = new ArrayList<Integer>();
170 for (PVariable pVariable : projectedVariables) {
171 projectionSourceIndices.add(compiledParent.getPosMapping().get(pVariable));
172 }
173 final Mask projectionMask = RecipesHelper.mask(compiledParent.getRecipe().getArity(), projectionSourceIndices);
174 return projectionMask;
175 }
176
177 /**
178 * @since 1.6
179 */
180 public static final class PosetTriplet {
181 public Mask coreMask;
182 public Mask posetMask;
183 public IPosetComparator comparator;
184 }
185
186 /**
187 * @since 1.6
188 */
189 public static PosetTriplet computePosetInfo(List<PVariable> variables, PBody body, IQueryMetaContext context) {
190 Map<PVariable, Set<IInputKey>> typeMap = TypeHelper.inferUnaryTypesFor(variables, body.getConstraints(),
191 context);
192 List<Set<IInputKey>> keys = new LinkedList<Set<IInputKey>>();
193
194 for (int i = 0; i < variables.size(); i++) {
195 keys.add(typeMap.get(variables.get(i)));
196 }
197
198 return computePosetInfo(keys, context);
199 }
200
201 /**
202 * @since 1.6
203 */
204 public static PosetTriplet computePosetInfo(List<PParameter> parameters, IQueryMetaContext context) {
205 List<Set<IInputKey>> keys = new LinkedList<Set<IInputKey>>();
206 for (int i = 0; i < parameters.size(); i++) {
207 IInputKey key = parameters.get(i).getDeclaredUnaryType();
208 if (key == null) {
209 keys.add(Collections.emptySet());
210 } else {
211 keys.add(Collections.singleton(parameters.get(i).getDeclaredUnaryType()));
212 }
213 }
214 return computePosetInfo(keys, context);
215 }
216
217
218
219 /**
220 * @since 1.6
221 */
222 public static PosetTriplet computePosetInfo(Iterable<Set<IInputKey>> keys, IQueryMetaContext context) {
223 PosetTriplet result = new PosetTriplet();
224 List<Integer> coreIndices = new ArrayList<Integer>();
225 List<Integer> posetIndices = new ArrayList<Integer>();
226 List<IInputKey> filtered = new ArrayList<IInputKey>();
227 boolean posetKey = false;
228 int index = -1;
229
230 for (Set<IInputKey> _keys : keys) {
231 ++index;
232 posetKey = false;
233
234 for (IInputKey key : _keys) {
235 if (key != null && context.isPosetKey(key)) {
236 posetKey = true;
237 filtered.add(key);
238 break;
239 }
240 }
241
242 if (posetKey) {
243 posetIndices.add(index);
244 } else {
245 coreIndices.add(index);
246 }
247 }
248
249 result.comparator = context.getPosetComparator(filtered);
250 result.coreMask = RecipesHelper.mask(index + 1, coreIndices);
251 result.posetMask = RecipesHelper.mask(index + 1, posetIndices);
252
253 return result;
254 }
255
256 /**
257 * Creates a recipe for a production node and the corresponding trace.
258 * <p> PRE: in case this is a recursion cutoff point (see {@link RecursionCutoffPoint})
259 * and bodyFinalTraces will be filled later,
260 * the object yielded now by bodyFinalTraces.values() must return up-to-date results later
261 * @since 2.4
262 */
263 public static CompiledQuery makeQueryTrace(PQuery query, Map<PBody, RecipeTraceInfo> bodyFinalTraces,
264 Collection<ReteNodeRecipe> bodyFinalRecipes, QueryEvaluationHint hint, IQueryMetaContext context,
265 boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) {
266 ProductionRecipe recipe = ReteRecipeCompiler.FACTORY.createProductionRecipe();
267
268 // temporary solution to support the deprecated option for now
269 boolean deleteAndRederiveEvaluationDep = deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(hint);
270
271 recipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep);
272
273 if (deleteAndRederiveEvaluationDep || (timelyEvaluation != null)) {
274 PosetTriplet triplet = computePosetInfo(query.getParameters(), context);
275 if (triplet.comparator != null) {
276 MonotonicityInfo info = FACTORY.createMonotonicityInfo();
277 info.setCoreMask(triplet.coreMask);
278 info.setPosetMask(triplet.posetMask);
279 info.setPosetComparator(triplet.comparator);
280 recipe.setOptionalMonotonicityInfo(info);
281 }
282 }
283
284 recipe.setPattern(query);
285 recipe.setPatternFQN(query.getFullyQualifiedName());
286 recipe.setTraceInfo(recipe.getPatternFQN());
287 recipe.getParents().addAll(bodyFinalRecipes);
288 for (int i = 0; i < query.getParameterNames().size(); ++i) {
289 recipe.getMappedIndices().put(query.getParameterNames().get(i), i);
290 }
291
292 return new CompiledQuery(recipe, bodyFinalTraces, query);
293 }
294
295 /**
296 * Calculated index mappings for a join, based on the common variables of the two parent subplans.
297 *
298 * @author Gabor Bergmann
299 *
300 */
301 public static class JoinHelper {
302 private TupleMask primaryMask;
303 private TupleMask secondaryMask;
304 private TupleMask complementerMask;
305 private RecipeTraceInfo primaryIndexer;
306 private RecipeTraceInfo secondaryIndexer;
307 private JoinRecipe naturalJoinRecipe;
308 private List<PVariable> naturalJoinVariablesTuple;
309
310 /**
311 * @pre enforceVariableCoincidences() has been called on both sides.
312 */
313 public JoinHelper(SubPlan planToCompile, PlanningTrace primaryCompiled, PlanningTrace callTrace) {
314 super();
315
316 Set<PVariable> primaryVariables = new LinkedHashSet<PVariable>(primaryCompiled.getVariablesTuple());
317 Set<PVariable> secondaryVariables = new LinkedHashSet<PVariable>(callTrace.getVariablesTuple());
318 int oldNodes = 0;
319 Set<Integer> introducingSecondaryIndices = new TreeSet<Integer>();
320 for (PVariable var : secondaryVariables) {
321 if (primaryVariables.contains(var))
322 oldNodes++;
323 else
324 introducingSecondaryIndices.add(callTrace.getPosMapping().get(var));
325 }
326 List<Integer> primaryIndices = new ArrayList<Integer>(oldNodes);
327 List<Integer> secondaryIndices = new ArrayList<Integer>(oldNodes);
328 for (PVariable var : secondaryVariables) {
329 if (primaryVariables.contains(var)) {
330 primaryIndices.add(primaryCompiled.getPosMapping().get(var));
331 secondaryIndices.add(callTrace.getPosMapping().get(var));
332 }
333 }
334 Collection<Integer> complementerIndices = introducingSecondaryIndices;
335
336 primaryMask = TupleMask.fromSelectedIndices(primaryCompiled.getVariablesTuple().size(), primaryIndices);
337 secondaryMask = TupleMask.fromSelectedIndices(callTrace.getVariablesTuple().size(), secondaryIndices);
338 complementerMask = TupleMask.fromSelectedIndices(callTrace.getVariablesTuple().size(), complementerIndices);
339
340 primaryIndexer = makeIndexerTrace(planToCompile, primaryCompiled, primaryMask);
341 secondaryIndexer = makeIndexerTrace(planToCompile, callTrace, secondaryMask);
342
343 naturalJoinRecipe = FACTORY.createJoinRecipe();
344 naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe());
345 naturalJoinRecipe.setRightParent((IndexerRecipe) secondaryIndexer.getRecipe());
346 naturalJoinRecipe.setRightParentComplementaryMask(CompilerHelper.toRecipeMask(complementerMask));
347
348 naturalJoinVariablesTuple = new ArrayList<PVariable>(primaryCompiled.getVariablesTuple());
349 for (int complementerIndex : complementerMask.indices)
350 naturalJoinVariablesTuple.add(callTrace.getVariablesTuple().get(complementerIndex));
351 }
352
353 public TupleMask getPrimaryMask() {
354 return primaryMask;
355 }
356
357 public TupleMask getSecondaryMask() {
358 return secondaryMask;
359 }
360
361 public TupleMask getComplementerMask() {
362 return complementerMask;
363 }
364
365 public RecipeTraceInfo getPrimaryIndexer() {
366 return primaryIndexer;
367 }
368
369 public RecipeTraceInfo getSecondaryIndexer() {
370 return secondaryIndexer;
371 }
372
373 public JoinRecipe getNaturalJoinRecipe() {
374 return naturalJoinRecipe;
375 }
376
377 public List<PVariable> getNaturalJoinVariablesTuple() {
378 return naturalJoinVariablesTuple;
379 }
380
381 }
382
383 /**
384 * @since 1.4
385 */
386 public static Mask toRecipeMask(TupleMask mask) {
387 return RecipesHelper.mask(mask.sourceWidth, mask.indices);
388 }
389
390}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/RecursionCutoffPoint.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/RecursionCutoffPoint.java
new file mode 100644
index 00000000..7d1e4d3a
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/RecursionCutoffPoint.java
@@ -0,0 +1,86 @@
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.rete.construction.plancompiler;
10
11import java.util.Collections;
12import java.util.HashMap;
13import java.util.List;
14import java.util.Map;
15import java.util.stream.Collectors;
16
17import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
18import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
19import tools.refinery.viatra.runtime.matchers.psystem.PBody;
20import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
21import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration;
22import tools.refinery.viatra.runtime.rete.recipes.ProductionRecipe;
23import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
24import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery;
25import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
26
27/**
28 * In a recursive query structure, query composition references can be cut off so that the remaining structure is DAG.
29 * {@link RecursionCutoffPoint} represents one such cut off query composition.
30 * When the compilation of the recursive query finishes and the compiled form becomes available,
31 * the {@link RecursionCutoffPoint} has to be signaled to update parent traces and recipes of the recursive call.
32 *
33 * @author Bergmann Gabor
34 * @noreference This class is not intended to be referenced by clients
35 *
36 */
37public class RecursionCutoffPoint {
38 final Map<PBody, RecipeTraceInfo> futureTraceMap;
39 final CompiledQuery compiledQuery;
40 final ProductionRecipe recipe;
41 final QueryEvaluationHint hint;
42
43 public RecursionCutoffPoint(PQuery query, QueryEvaluationHint hint, IQueryMetaContext context, boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) {
44 super();
45 this.hint = hint;
46 this.futureTraceMap = new HashMap<>(); // IMPORTANT: the identity of futureTraceMap.values() will not change
47 this.compiledQuery = CompilerHelper.makeQueryTrace(query, futureTraceMap, Collections.<ReteNodeRecipe>emptySet(), hint, context, deleteAndRederiveEvaluation, timelyEvaluation);
48 this.recipe = (ProductionRecipe)compiledQuery.getRecipe();
49 if (!compiledQuery.getParentRecipeTraces().isEmpty()) {
50 throw new IllegalArgumentException(String.format("Recursion cut-off point of query %s has trace parents: %s",
51 compiledQuery.getQuery(),
52 prettyPrintParentRecipeTraces(compiledQuery.getParentRecipeTraces())));
53 }
54 if (!recipe.getParents().isEmpty()) {
55 throw new IllegalArgumentException(String.format("Recursion cut-off point of query %s has recipe parents: %s",
56 compiledQuery.getQuery(),
57 prettyPrintParentRecipeTraces(compiledQuery.getParentRecipeTraces())));
58 }
59 }
60
61
62
63 private String prettyPrintParentRecipeTraces(List<RecipeTraceInfo> trace) {
64 return trace.stream().map(Object::toString).collect(Collectors.joining(", "));
65 }
66
67 /**
68 * Signals that compilation of the recursive query has terminated, culminating into the given compiled form.
69 * The query composition that has been cut off will be connected now.
70 */
71 public void mend(CompiledQuery finalCompiledForm) {
72 futureTraceMap.putAll(finalCompiledForm.getParentRecipeTracesPerBody());
73 recipe.getParents().addAll(((ProductionRecipe)finalCompiledForm.getRecipe()).getParents());
74 }
75
76 public CompiledQuery getCompiledQuery() {
77 return compiledQuery;
78 }
79
80 public ProductionRecipe getRecipe() {
81 return recipe;
82 }
83
84
85
86}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java
new file mode 100644
index 00000000..6dd270bd
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java
@@ -0,0 +1,948 @@
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.rete.construction.plancompiler;
10
11import org.apache.log4j.Logger;
12import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
13import tools.refinery.viatra.runtime.matchers.backend.CommonQueryHintOptions;
14import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
15import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
16import tools.refinery.viatra.runtime.matchers.context.IInputKey;
17import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
18import tools.refinery.viatra.runtime.matchers.context.IQueryCacheContext;
19import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
20import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy;
21import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
22import tools.refinery.viatra.runtime.matchers.planning.helpers.BuildHelper;
23import tools.refinery.viatra.runtime.matchers.planning.operations.*;
24import tools.refinery.viatra.runtime.matchers.psystem.*;
25import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
26import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
27import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.*;
28import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.*;
29import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
30import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
31import tools.refinery.viatra.runtime.matchers.psystem.queries.PVisibility;
32import tools.refinery.viatra.runtime.matchers.psystem.rewriters.*;
33import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
34import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
35import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
36import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
37import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
38import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
39import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper.JoinHelper;
40import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper.PosetTriplet;
41import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration;
42import tools.refinery.viatra.runtime.rete.recipes.*;
43import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper;
44import tools.refinery.viatra.runtime.rete.traceability.*;
45import tools.refinery.viatra.runtime.rete.util.ReteHintOptions;
46
47import java.util.*;
48import java.util.Map.Entry;
49import java.util.function.BiFunction;
50import java.util.function.Function;
51
52/**
53 * Compiles queries and query plans into Rete recipes, traced by respectively a {@link CompiledQuery} or
54 * {@link CompiledSubPlan}.
55 *
56 * @author Bergmann Gabor
57 *
58 */
59public class ReteRecipeCompiler {
60
61 private final IQueryPlannerStrategy plannerStrategy;
62 private final IQueryMetaContext metaContext;
63 private final IQueryBackendHintProvider hintProvider;
64 private final PDisjunctionRewriter normalizer;
65 private final QueryAnalyzer queryAnalyzer;
66 private final Logger logger;
67
68 /**
69 * @since 2.2
70 */
71 protected final boolean deleteAndRederiveEvaluation;
72 /**
73 * @since 2.4
74 */
75 protected final TimelyConfiguration timelyEvaluation;
76
77 /**
78 * @since 1.5
79 */
80 public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryMetaContext metaContext,
81 IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer) {
82 this(plannerStrategy, logger, metaContext, queryCacheContext, hintProvider, queryAnalyzer, false, null);
83 }
84
85 /**
86 * @since 2.4
87 */
88 public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryMetaContext metaContext,
89 IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer,
90 boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) {
91 super();
92 this.deleteAndRederiveEvaluation = deleteAndRederiveEvaluation;
93 this.timelyEvaluation = timelyEvaluation;
94 this.plannerStrategy = plannerStrategy;
95 this.logger = logger;
96 this.metaContext = metaContext;
97 this.queryAnalyzer = queryAnalyzer;
98 this.normalizer = new PDisjunctionRewriterCacher(new SurrogateQueryRewriter(),
99 new PBodyNormalizer(metaContext) {
100
101 @Override
102 protected boolean shouldExpandWeakenedAlternatives(PQuery query) {
103 QueryEvaluationHint hint = ReteRecipeCompiler.this.hintProvider.getQueryEvaluationHint(query);
104 Boolean expandWeakenedAlternativeConstraints = ReteHintOptions.expandWeakenedAlternativeConstraints
105 .getValueOrDefault(hint);
106 return expandWeakenedAlternativeConstraints;
107 }
108
109 });
110 this.hintProvider = hintProvider;
111 }
112
113 static final RecipesFactory FACTORY = RecipesFactory.eINSTANCE;
114
115 // INTERNALLY CACHED
116 private Map<PBody, SubPlan> plannerCache = new HashMap<PBody, SubPlan>();
117 private Set<PBody> planningInProgress = new HashSet<PBody>();
118
119 private Map<PQuery, CompiledQuery> queryCompilerCache = new HashMap<PQuery, CompiledQuery>();
120 private Set<PQuery> compilationInProgress = new HashSet<PQuery>();
121 private IMultiLookup<PQuery, RecursionCutoffPoint> recursionCutoffPoints = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class);
122 private Map<SubPlan, CompiledSubPlan> subPlanCompilerCache = new HashMap<SubPlan, CompiledSubPlan>();
123 private Map<ReteNodeRecipe, SubPlan> compilerBackTrace = new HashMap<ReteNodeRecipe, SubPlan>();
124
125 /**
126 * Clears internal state
127 */
128 public void reset() {
129 plannerCache.clear();
130 planningInProgress.clear();
131 queryCompilerCache.clear();
132 subPlanCompilerCache.clear();
133 compilerBackTrace.clear();
134 }
135
136 /**
137 * Returns a {@link CompiledQuery} compiled from a query
138 * @throws ViatraQueryRuntimeException
139 */
140 public CompiledQuery getCompiledForm(PQuery query) {
141 CompiledQuery compiled = queryCompilerCache.get(query);
142 if (compiled == null) {
143
144 IRewriterTraceCollector traceCollector = CommonQueryHintOptions.normalizationTraceCollector
145 .getValueOrDefault(hintProvider.getQueryEvaluationHint(query));
146 if (traceCollector != null) {
147 traceCollector.addTrace(query, query);
148 }
149
150 boolean reentrant = !compilationInProgress.add(query);
151 if (reentrant) { // oops, recursion into body in progress
152 RecursionCutoffPoint cutoffPoint = new RecursionCutoffPoint(query, getHints(query), metaContext,
153 deleteAndRederiveEvaluation, timelyEvaluation);
154 recursionCutoffPoints.addPair(query, cutoffPoint);
155 return cutoffPoint.getCompiledQuery();
156 } else { // not reentrant, therefore no recursion, do the compilation
157 try {
158 compiled = compileProduction(query);
159 queryCompilerCache.put(query, compiled);
160 // backTrace.put(compiled.getRecipe(), plan);
161
162 // if this was a recursive query, mend all points where recursion was cut off
163 for (RecursionCutoffPoint cutoffPoint : recursionCutoffPoints.lookupOrEmpty(query)) {
164 cutoffPoint.mend(compiled);
165 }
166 } finally {
167 compilationInProgress.remove(query);
168 }
169 }
170 }
171 return compiled;
172 }
173
174 /**
175 * Returns a {@link CompiledSubPlan} compiled from a query plan
176 * @throws ViatraQueryRuntimeException
177 */
178 public CompiledSubPlan getCompiledForm(SubPlan plan) {
179 CompiledSubPlan compiled = subPlanCompilerCache.get(plan);
180 if (compiled == null) {
181 compiled = doCompileDispatch(plan);
182 subPlanCompilerCache.put(plan, compiled);
183 compilerBackTrace.put(compiled.getRecipe(), plan);
184 }
185 return compiled;
186 }
187
188 /**
189 * @throws ViatraQueryRuntimeException
190 */
191 public SubPlan getPlan(PBody pBody) {
192 // if the query is not marked as being compiled, initiate compilation
193 // (this is useful in case of recursion if getPlan() is the entry point)
194 PQuery pQuery = pBody.getPattern();
195 if (!compilationInProgress.contains(pQuery))
196 getCompiledForm(pQuery);
197
198 // Is the plan already cached?
199 SubPlan plan = plannerCache.get(pBody);
200 if (plan == null) {
201 boolean reentrant = !planningInProgress.add(pBody);
202 if (reentrant) { // oops, recursion into body in progress
203 throw new IllegalArgumentException(
204 "Planning-level recursion unsupported: " + pBody.getPattern().getFullyQualifiedName());
205 } else { // not reentrant, therefore no recursion, do the planning
206 try {
207 plan = plannerStrategy.plan(pBody, logger, metaContext);
208 plannerCache.put(pBody, plan);
209 } finally {
210 planningInProgress.remove(pBody);
211 }
212 }
213 }
214 return plan;
215 }
216
217 private CompiledQuery compileProduction(PQuery query) {
218 Collection<SubPlan> bodyPlans = new ArrayList<SubPlan>();
219 normalizer.setTraceCollector(CommonQueryHintOptions.normalizationTraceCollector
220 .getValueOrDefault(hintProvider.getQueryEvaluationHint(query)));
221 for (PBody pBody : normalizer.rewrite(query).getBodies()) {
222 SubPlan bodyPlan = getPlan(pBody);
223 bodyPlans.add(bodyPlan);
224 }
225 return doCompileProduction(query, bodyPlans);
226 }
227
228 private CompiledQuery doCompileProduction(PQuery query, Collection<SubPlan> bodies) {
229 // TODO skip production node if there is just one body and no projection needed?
230 Map<PBody, RecipeTraceInfo> bodyFinalTraces = new HashMap<PBody, RecipeTraceInfo>();
231 Collection<ReteNodeRecipe> bodyFinalRecipes = new HashSet<ReteNodeRecipe>();
232
233 for (SubPlan bodyFinalPlan : bodies) {
234 // skip over any projections at the end
235 bodyFinalPlan = BuildHelper.eliminateTrailingProjections(bodyFinalPlan);
236
237 // TODO checkAndTrimEqualVariables may introduce superfluous trim,
238 // but whatever (no uniqueness enforcer needed)
239
240 // compile body
241 final CompiledSubPlan compiledBody = getCompiledForm(bodyFinalPlan);
242
243 // project to parameter list
244 RecipeTraceInfo finalTrace = projectBodyFinalToParameters(compiledBody, false);
245
246 bodyFinalTraces.put(bodyFinalPlan.getBody(), finalTrace);
247 bodyFinalRecipes.add(finalTrace.getRecipe());
248 }
249
250 CompiledQuery compiled = CompilerHelper.makeQueryTrace(query, bodyFinalTraces, bodyFinalRecipes,
251 getHints(query), metaContext, deleteAndRederiveEvaluation, timelyEvaluation);
252
253 return compiled;
254 }
255
256 private CompiledSubPlan doCompileDispatch(SubPlan plan) {
257 final POperation operation = plan.getOperation();
258 if (operation instanceof PEnumerate) {
259 return doCompileEnumerate(((PEnumerate) operation).getEnumerablePConstraint(), plan);
260 } else if (operation instanceof PApply) {
261 final PConstraint pConstraint = ((PApply) operation).getPConstraint();
262 if (pConstraint instanceof EnumerablePConstraint) {
263 CompiledSubPlan primaryParent = getCompiledForm(plan.getParentPlans().get(0));
264 PlanningTrace secondaryParent = doEnumerateDispatch(plan, (EnumerablePConstraint) pConstraint);
265 return compileToNaturalJoin(plan, primaryParent, secondaryParent);
266 } else if (pConstraint instanceof DeferredPConstraint) {
267 return doDeferredDispatch((DeferredPConstraint) pConstraint, plan);
268 } else {
269 throw new IllegalArgumentException("Unsupported PConstraint in query plan: " + plan.toShortString());
270 }
271 } else if (operation instanceof PJoin) {
272 return doCompileJoin((PJoin) operation, plan);
273 } else if (operation instanceof PProject) {
274 return doCompileProject((PProject) operation, plan);
275 } else if (operation instanceof PStart) {
276 return doCompileStart((PStart) operation, plan);
277 } else {
278 throw new IllegalArgumentException("Unsupported POperation in query plan: " + plan.toShortString());
279 }
280 }
281
282 private CompiledSubPlan doDeferredDispatch(DeferredPConstraint constraint, SubPlan plan) {
283 final SubPlan parentPlan = plan.getParentPlans().get(0);
284 final CompiledSubPlan parentCompiled = getCompiledForm(parentPlan);
285 if (constraint instanceof Equality) {
286 return compileDeferred((Equality) constraint, plan, parentPlan, parentCompiled);
287 } else if (constraint instanceof ExportedParameter) {
288 return compileDeferred((ExportedParameter) constraint, plan, parentPlan, parentCompiled);
289 } else if (constraint instanceof Inequality) {
290 return compileDeferred((Inequality) constraint, plan, parentPlan, parentCompiled);
291 } else if (constraint instanceof NegativePatternCall) {
292 return compileDeferred((NegativePatternCall) constraint, plan, parentPlan, parentCompiled);
293 } else if (constraint instanceof PatternMatchCounter) {
294 return compileDeferred((PatternMatchCounter) constraint, plan, parentPlan, parentCompiled);
295 } else if (constraint instanceof AggregatorConstraint) {
296 return compileDeferred((AggregatorConstraint) constraint, plan, parentPlan, parentCompiled);
297 } else if (constraint instanceof ExpressionEvaluation) {
298 return compileDeferred((ExpressionEvaluation) constraint, plan, parentPlan, parentCompiled);
299 } else if (constraint instanceof TypeFilterConstraint) {
300 return compileDeferred((TypeFilterConstraint) constraint, plan, parentPlan, parentCompiled);
301 }
302 throw new UnsupportedOperationException("Unknown deferred constraint " + constraint);
303 }
304
305 private CompiledSubPlan compileDeferred(Equality constraint, SubPlan plan, SubPlan parentPlan,
306 CompiledSubPlan parentCompiled) {
307 if (constraint.isMoot())
308 return parentCompiled.cloneFor(plan);
309
310 Integer index1 = parentCompiled.getPosMapping().get(constraint.getWho());
311 Integer index2 = parentCompiled.getPosMapping().get(constraint.getWithWhom());
312
313 if (index1 != null && index2 != null && index1.intValue() != index2.intValue()) {
314 Integer indexLower = Math.min(index1, index2);
315 Integer indexHigher = Math.max(index1, index2);
316
317 EqualityFilterRecipe equalityFilterRecipe = FACTORY.createEqualityFilterRecipe();
318 equalityFilterRecipe.setParent(parentCompiled.getRecipe());
319 equalityFilterRecipe.getIndices().add(indexLower);
320 equalityFilterRecipe.getIndices().add(indexHigher);
321
322 return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), equalityFilterRecipe, parentCompiled);
323 } else {
324 throw new IllegalArgumentException(String.format("Unable to interpret %s after compiled parent %s",
325 plan.toShortString(), parentCompiled.toString()));
326 }
327 }
328
329 /**
330 * Precondition: constantTrace must map to a ConstantRecipe, and all of its variables must be contained in
331 * toFilterTrace.
332 */
333 private CompiledSubPlan compileConstantFiltering(SubPlan plan, PlanningTrace toFilterTrace,
334 ConstantRecipe constantRecipe, List<PVariable> filteredVariables) {
335 PlanningTrace resultTrace = toFilterTrace;
336
337 int constantVariablesSize = filteredVariables.size();
338 for (int i = 0; i < constantVariablesSize; ++i) {
339 Object constantValue = constantRecipe.getConstantValues().get(i);
340 PVariable filteredVariable = filteredVariables.get(i);
341 int filteredColumn = resultTrace.getVariablesTuple().indexOf(filteredVariable);
342
343 DiscriminatorDispatcherRecipe dispatcherRecipe = FACTORY.createDiscriminatorDispatcherRecipe();
344 dispatcherRecipe.setDiscriminationColumnIndex(filteredColumn);
345 dispatcherRecipe.setParent(resultTrace.getRecipe());
346
347 PlanningTrace dispatcherTrace = new PlanningTrace(plan, resultTrace.getVariablesTuple(), dispatcherRecipe,
348 resultTrace);
349
350 DiscriminatorBucketRecipe bucketRecipe = FACTORY.createDiscriminatorBucketRecipe();
351 bucketRecipe.setBucketKey(constantValue);
352 bucketRecipe.setParent(dispatcherRecipe);
353
354 PlanningTrace bucketTrace = new PlanningTrace(plan, dispatcherTrace.getVariablesTuple(), bucketRecipe,
355 dispatcherTrace);
356
357 resultTrace = bucketTrace;
358 }
359
360 return resultTrace.cloneFor(plan);
361 }
362
363 private CompiledSubPlan compileDeferred(ExportedParameter constraint, SubPlan plan, SubPlan parentPlan,
364 CompiledSubPlan parentCompiled) {
365 return parentCompiled.cloneFor(plan);
366 }
367
368 private CompiledSubPlan compileDeferred(Inequality constraint, SubPlan plan, SubPlan parentPlan,
369 CompiledSubPlan parentCompiled) {
370 if (constraint.isEliminable())
371 return parentCompiled.cloneFor(plan);
372
373 Integer index1 = parentCompiled.getPosMapping().get(constraint.getWho());
374 Integer index2 = parentCompiled.getPosMapping().get(constraint.getWithWhom());
375
376 if (index1 != null && index2 != null && index1.intValue() != index2.intValue()) {
377 Integer indexLower = Math.min(index1, index2);
378 Integer indexHigher = Math.max(index1, index2);
379
380 InequalityFilterRecipe inequalityFilterRecipe = FACTORY.createInequalityFilterRecipe();
381 inequalityFilterRecipe.setParent(parentCompiled.getRecipe());
382 inequalityFilterRecipe.setSubject(indexLower);
383 inequalityFilterRecipe.getInequals().add(indexHigher);
384
385 return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), inequalityFilterRecipe,
386 parentCompiled);
387 } else {
388 throw new IllegalArgumentException(String.format("Unable to interpret %s after compiled parent %s",
389 plan.toShortString(), parentCompiled.toString()));
390 }
391 }
392
393 private CompiledSubPlan compileDeferred(TypeFilterConstraint constraint, SubPlan plan, SubPlan parentPlan,
394 CompiledSubPlan parentCompiled) {
395 final IInputKey inputKey = constraint.getInputKey();
396 if (!metaContext.isStateless(inputKey))
397 throw new UnsupportedOperationException(
398 "Non-enumerable input keys are currently supported in Rete only if they are stateless, unlike "
399 + inputKey);
400
401 final Tuple constraintVariables = constraint.getVariablesTuple();
402 final List<PVariable> parentVariables = parentCompiled.getVariablesTuple();
403
404 Mask mask; // select elements of the tuple to check against extensional relation
405 if (Tuples.flatTupleOf(parentVariables.toArray()).equals(constraintVariables)) {
406 mask = null; // lucky case, parent signature equals that of input key
407 } else {
408 List<PVariable> variables = new ArrayList<PVariable>();
409 for (Object variable : constraintVariables.getElements()) {
410 variables.add((PVariable) variable);
411 }
412 mask = CompilerHelper.makeProjectionMask(parentCompiled, variables);
413 }
414 InputFilterRecipe inputFilterRecipe = RecipesHelper.inputFilterRecipe(parentCompiled.getRecipe(), inputKey,
415 inputKey.getStringID(), mask);
416 return new CompiledSubPlan(plan, parentVariables, inputFilterRecipe, parentCompiled);
417 }
418
419 private CompiledSubPlan compileDeferred(NegativePatternCall constraint, SubPlan plan, SubPlan parentPlan,
420 CompiledSubPlan parentCompiled) {
421 final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan,
422 constraint.getActualParametersTuple());
423
424 JoinHelper joinHelper = new JoinHelper(plan, parentCompiled, callTrace);
425 final RecipeTraceInfo primaryIndexer = joinHelper.getPrimaryIndexer();
426 final RecipeTraceInfo secondaryIndexer = joinHelper.getSecondaryIndexer();
427
428 AntiJoinRecipe antiJoinRecipe = FACTORY.createAntiJoinRecipe();
429 antiJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe());
430 antiJoinRecipe.setRightParent((IndexerRecipe) secondaryIndexer.getRecipe());
431
432 return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), antiJoinRecipe, primaryIndexer,
433 secondaryIndexer);
434 }
435
436 private CompiledSubPlan compileDeferred(PatternMatchCounter constraint, SubPlan plan, SubPlan parentPlan,
437 CompiledSubPlan parentCompiled) {
438 final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan,
439 constraint.getActualParametersTuple());
440
441 // hack: use some mask computations (+ the indexers) from a fake natural join against the called query
442 JoinHelper fakeJoinHelper = new JoinHelper(plan, parentCompiled, callTrace);
443 final RecipeTraceInfo primaryIndexer = fakeJoinHelper.getPrimaryIndexer();
444 final RecipeTraceInfo callProjectionIndexer = fakeJoinHelper.getSecondaryIndexer();
445
446 final List<PVariable> sideVariablesTuple = new ArrayList<PVariable>(
447 fakeJoinHelper.getSecondaryMask().transform(callTrace.getVariablesTuple()));
448 /* if (!booleanCheck) */ sideVariablesTuple.add(constraint.getResultVariable());
449
450 CountAggregatorRecipe aggregatorRecipe = FACTORY.createCountAggregatorRecipe();
451 aggregatorRecipe.setParent((ProjectionIndexerRecipe) callProjectionIndexer.getRecipe());
452 PlanningTrace aggregatorTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorRecipe,
453 callProjectionIndexer);
454
455 IndexerRecipe aggregatorIndexerRecipe = FACTORY.createAggregatorIndexerRecipe();
456 aggregatorIndexerRecipe.setParent(aggregatorRecipe);
457 // aggregatorIndexerRecipe.setMask(RecipesHelper.mask(
458 // sideVariablesTuple.size(),
459 // //use same indices as in the projection indexer
460 // // EVEN if result variable already visible in left parent
461 // fakeJoinHelper.getSecondaryMask().indices
462 // ));
463
464 int aggregatorWidth = sideVariablesTuple.size();
465 int aggregateResultIndex = aggregatorWidth - 1;
466
467 aggregatorIndexerRecipe.setMask(CompilerHelper.toRecipeMask(TupleMask.omit(
468 // aggregate according all but the last index
469 aggregateResultIndex, aggregatorWidth)));
470 PlanningTrace aggregatorIndexerTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorIndexerRecipe,
471 aggregatorTrace);
472
473 JoinRecipe naturalJoinRecipe = FACTORY.createJoinRecipe();
474 naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe());
475 naturalJoinRecipe.setRightParent(aggregatorIndexerRecipe);
476 naturalJoinRecipe.setRightParentComplementaryMask(RecipesHelper.mask(aggregatorWidth,
477 // extend with last element only - the computation value
478 aggregateResultIndex));
479
480 // what if the new variable already has a value?
481 // even if already known, we add the new result variable, so that it can be filtered at the end
482 // boolean alreadyKnown = parentPlan.getVisibleVariables().contains(constraint.getResultVariable());
483
484 final List<PVariable> aggregatedVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple());
485 aggregatedVariablesTuple.add(constraint.getResultVariable());
486
487 PlanningTrace joinTrace = new PlanningTrace(plan, aggregatedVariablesTuple, naturalJoinRecipe, primaryIndexer,
488 aggregatorIndexerTrace);
489
490 return CompilerHelper.checkAndTrimEqualVariables(plan, joinTrace).cloneFor(plan);
491 // if (!alreadyKnown) {
492 // return joinTrace.cloneFor(plan);
493 // } else {
494 // //final Integer equalsWithIndex = parentCompiled.getPosMapping().get(parentCompiled.getVariablesTuple());
495 // }
496 }
497
498 private CompiledSubPlan compileDeferred(AggregatorConstraint constraint, SubPlan plan, SubPlan parentPlan,
499 CompiledSubPlan parentCompiled) {
500 final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan,
501 constraint.getActualParametersTuple());
502
503 // hack: use some mask computations (+ the indexers) from a fake natural join against the called query
504 JoinHelper fakeJoinHelper = new JoinHelper(plan, parentCompiled, callTrace);
505 final RecipeTraceInfo primaryIndexer = fakeJoinHelper.getPrimaryIndexer();
506 TupleMask callGroupMask = fakeJoinHelper.getSecondaryMask();
507
508 final List<PVariable> sideVariablesTuple = new ArrayList<PVariable>(
509 callGroupMask.transform(callTrace.getVariablesTuple()));
510 /* if (!booleanCheck) */ sideVariablesTuple.add(constraint.getResultVariable());
511
512 IMultisetAggregationOperator<?, ?, ?> operator = constraint.getAggregator().getOperator();
513
514 SingleColumnAggregatorRecipe columnAggregatorRecipe = FACTORY.createSingleColumnAggregatorRecipe();
515 columnAggregatorRecipe.setParent(callTrace.getRecipe());
516 columnAggregatorRecipe.setMultisetAggregationOperator(operator);
517
518 int columnIndex = constraint.getAggregatedColumn();
519 IPosetComparator posetComparator = null;
520 Mask groupMask = CompilerHelper.toRecipeMask(callGroupMask);
521
522 // temporary solution to support the deprecated option for now
523 final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(plan));
524
525 columnAggregatorRecipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep);
526 if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) {
527 List<PParameter> parameters = constraint.getReferredQuery().getParameters();
528 IInputKey key = parameters.get(columnIndex).getDeclaredUnaryType();
529 if (key != null && metaContext.isPosetKey(key)) {
530 posetComparator = metaContext.getPosetComparator(Collections.singleton(key));
531 }
532 }
533
534 if (posetComparator == null) {
535 columnAggregatorRecipe.setGroupByMask(groupMask);
536 columnAggregatorRecipe.setAggregableIndex(columnIndex);
537 } else {
538 MonotonicityInfo monotonicityInfo = FACTORY.createMonotonicityInfo();
539 monotonicityInfo.setCoreMask(groupMask);
540 monotonicityInfo.setPosetMask(CompilerHelper.toRecipeMask(
541 TupleMask.selectSingle(columnIndex, constraint.getActualParametersTuple().getSize())));
542 monotonicityInfo.setPosetComparator(posetComparator);
543 columnAggregatorRecipe.setOptionalMonotonicityInfo(monotonicityInfo);
544 }
545
546 ReteNodeRecipe aggregatorRecipe = columnAggregatorRecipe;
547 PlanningTrace aggregatorTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorRecipe, callTrace);
548
549 IndexerRecipe aggregatorIndexerRecipe = FACTORY.createAggregatorIndexerRecipe();
550 aggregatorIndexerRecipe.setParent(aggregatorRecipe);
551
552 int aggregatorWidth = sideVariablesTuple.size();
553 int aggregateResultIndex = aggregatorWidth - 1;
554
555 aggregatorIndexerRecipe.setMask(CompilerHelper.toRecipeMask(TupleMask.omit(
556 // aggregate according all but the last index
557 aggregateResultIndex, aggregatorWidth)));
558 PlanningTrace aggregatorIndexerTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorIndexerRecipe,
559 aggregatorTrace);
560
561 JoinRecipe naturalJoinRecipe = FACTORY.createJoinRecipe();
562 naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe());
563 naturalJoinRecipe.setRightParent(aggregatorIndexerRecipe);
564 naturalJoinRecipe.setRightParentComplementaryMask(RecipesHelper.mask(aggregatorWidth,
565 // extend with last element only - the computation value
566 aggregateResultIndex));
567
568 // what if the new variable already has a value?
569 // even if already known, we add the new result variable, so that it can be filtered at the end
570 // boolean alreadyKnown = parentPlan.getVisibleVariables().contains(constraint.getResultVariable());
571
572 final List<PVariable> finalVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple());
573 finalVariablesTuple.add(constraint.getResultVariable());
574
575 PlanningTrace joinTrace = new PlanningTrace(plan, finalVariablesTuple, naturalJoinRecipe, primaryIndexer,
576 aggregatorIndexerTrace);
577
578 return CompilerHelper.checkAndTrimEqualVariables(plan, joinTrace).cloneFor(plan);
579 // if (!alreadyKnown) {
580 // return joinTrace.cloneFor(plan);
581 // } else {
582 // //final Integer equalsWithIndex = parentCompiled.getPosMapping().get(parentCompiled.getVariablesTuple());
583 // }
584 }
585
586 private CompiledSubPlan compileDeferred(ExpressionEvaluation constraint, SubPlan plan, SubPlan parentPlan,
587 CompiledSubPlan parentCompiled) {
588 Map<String, Integer> tupleNameMap = new HashMap<String, Integer>();
589 for (String name : constraint.getEvaluator().getInputParameterNames()) {
590 Map<? extends Object, Integer> index = parentCompiled.getPosMapping();
591 PVariable variable = constraint.getPSystem().getVariableByNameChecked(name);
592 Integer position = index.get(variable);
593 tupleNameMap.put(name, position);
594 }
595
596 final PVariable outputVariable = constraint.getOutputVariable();
597 final boolean booleanCheck = outputVariable == null;
598
599 // TODO determine whether expression is costly
600 boolean cacheOutput = ReteHintOptions.cacheOutputOfEvaluatorsByDefault.getValueOrDefault(getHints(plan));
601 // for (PAnnotation pAnnotation :
602 // plan.getBody().getPattern().getAnnotationsByName(EXPRESSION_EVALUATION_ANNOTATION"")) {
603 // for (Object value : pAnnotation.getAllValues("expensive")) {
604 // if (value instanceof Boolean)
605 // cacheOutput = (boolean) value;
606 // }
607 // }
608
609 ExpressionEnforcerRecipe enforcerRecipe = booleanCheck ? FACTORY.createCheckRecipe()
610 : FACTORY.createEvalRecipe();
611 enforcerRecipe.setParent(parentCompiled.getRecipe());
612 enforcerRecipe.setExpression(RecipesHelper.expressionDefinition(constraint.getEvaluator()));
613 enforcerRecipe.setCacheOutput(cacheOutput);
614 if (enforcerRecipe instanceof EvalRecipe) {
615 ((EvalRecipe) enforcerRecipe).setUnwinding(constraint.isUnwinding());
616 }
617 for (Entry<String, Integer> entry : tupleNameMap.entrySet()) {
618 enforcerRecipe.getMappedIndices().put(entry.getKey(), entry.getValue());
619 }
620
621 final List<PVariable> enforcerVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple());
622 if (!booleanCheck)
623 enforcerVariablesTuple.add(outputVariable);
624 PlanningTrace enforcerTrace = new PlanningTrace(plan, enforcerVariablesTuple, enforcerRecipe, parentCompiled);
625
626 return CompilerHelper.checkAndTrimEqualVariables(plan, enforcerTrace).cloneFor(plan);
627 }
628
629 private CompiledSubPlan doCompileJoin(PJoin operation, SubPlan plan) {
630 final List<CompiledSubPlan> compiledParents = getCompiledFormOfParents(plan);
631 final CompiledSubPlan leftCompiled = compiledParents.get(0);
632 final CompiledSubPlan rightCompiled = compiledParents.get(1);
633
634 return compileToNaturalJoin(plan, leftCompiled, rightCompiled);
635 }
636
637 private CompiledSubPlan compileToNaturalJoin(SubPlan plan, final PlanningTrace leftCompiled,
638 final PlanningTrace rightCompiled) {
639 // CHECK IF SPECIAL CASE
640
641 // Is constant filtering applicable?
642 if (ReteHintOptions.useDiscriminatorDispatchersForConstantFiltering.getValueOrDefault(getHints(plan))) {
643 if (leftCompiled.getRecipe() instanceof ConstantRecipe
644 && rightCompiled.getVariablesTuple().containsAll(leftCompiled.getVariablesTuple())) {
645 return compileConstantFiltering(plan, rightCompiled, (ConstantRecipe) leftCompiled.getRecipe(),
646 leftCompiled.getVariablesTuple());
647 }
648 if (rightCompiled.getRecipe() instanceof ConstantRecipe
649 && leftCompiled.getVariablesTuple().containsAll(rightCompiled.getVariablesTuple())) {
650 return compileConstantFiltering(plan, leftCompiled, (ConstantRecipe) rightCompiled.getRecipe(),
651 rightCompiled.getVariablesTuple());
652 }
653 }
654
655 // ELSE: ACTUAL JOIN
656 JoinHelper joinHelper = new JoinHelper(plan, leftCompiled, rightCompiled);
657 return new CompiledSubPlan(plan, joinHelper.getNaturalJoinVariablesTuple(), joinHelper.getNaturalJoinRecipe(),
658 joinHelper.getPrimaryIndexer(), joinHelper.getSecondaryIndexer());
659 }
660
661 private CompiledSubPlan doCompileProject(PProject operation, SubPlan plan) {
662 final List<CompiledSubPlan> compiledParents = getCompiledFormOfParents(plan);
663 final CompiledSubPlan compiledParent = compiledParents.get(0);
664
665 List<PVariable> projectedVariables = new ArrayList<PVariable>(operation.getToVariables());
666 // Determinizing projection: try to keep original order (hopefully facilitates node reuse)
667 Map<PVariable, Integer> parentPosMapping = compiledParent.getPosMapping();
668 Collections.sort(projectedVariables, Comparator.comparing(parentPosMapping::get));
669
670 return doProjectPlan(compiledParent, projectedVariables, true,
671 parentTrace -> parentTrace.cloneFor(plan),
672 (recipe, parentTrace) -> new PlanningTrace(plan, projectedVariables, recipe, parentTrace),
673 (recipe, parentTrace) -> new CompiledSubPlan(plan, projectedVariables, recipe, parentTrace)
674 );
675 }
676
677 /**
678 * Projects a subplan onto the specified variable tuple
679 * @param compiledParentPlan the compiled form of the subplan
680 * @param targetVariables list of variables to project to
681 * @param enforceUniqueness whether distinctness shall be enforced after the projection.
682 * Specify false only if directly connecting to a production node.
683 * @param reinterpretTraceFactory constructs a reinterpreted trace that simply relabels the compiled parent plan, in case it is sufficient
684 * @param intermediateTraceFactory constructs a recipe trace for an intermediate node, given the recipe of the node and its parent trace
685 * @param finalTraceFactory constructs a recipe trace for the final resulting node, given the recipe of the node and its parent trace
686 * @since 2.1
687 */
688 <ResultTrace extends RecipeTraceInfo> ResultTrace doProjectPlan(
689 final CompiledSubPlan compiledParentPlan,
690 final List<PVariable> targetVariables,
691 boolean enforceUniqueness,
692 Function<CompiledSubPlan, ResultTrace> reinterpretTraceFactory,
693 BiFunction<ReteNodeRecipe, RecipeTraceInfo, RecipeTraceInfo> intermediateTraceFactory,
694 BiFunction<ReteNodeRecipe, RecipeTraceInfo, ResultTrace> finalTraceFactory)
695 {
696 if (targetVariables.equals(compiledParentPlan.getVariablesTuple())) // no projection needed
697 return reinterpretTraceFactory.apply(compiledParentPlan);
698
699 // otherwise, we need at least a trimmer
700 TrimmerRecipe trimmerRecipe = CompilerHelper.makeTrimmerRecipe(compiledParentPlan, targetVariables);
701
702 // do we need to eliminate duplicates?
703 SubPlan parentPlan = compiledParentPlan.getSubPlan();
704 if (!enforceUniqueness || BuildHelper.areAllVariablesDetermined(
705 parentPlan,
706 targetVariables,
707 queryAnalyzer,
708 true))
709 {
710 // if uniqueness enforcess is unwanted or unneeeded, skip it
711 return finalTraceFactory.apply(trimmerRecipe, compiledParentPlan);
712 } else {
713 // add a uniqueness enforcer
714 UniquenessEnforcerRecipe recipe = FACTORY.createUniquenessEnforcerRecipe();
715 recipe.getParents().add(trimmerRecipe);
716
717 // temporary solution to support the deprecated option for now
718 final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(parentPlan));
719
720 recipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep);
721 if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) {
722 PosetTriplet triplet = CompilerHelper.computePosetInfo(targetVariables, parentPlan.getBody(), metaContext);
723
724 if (triplet.comparator != null) {
725 MonotonicityInfo info = FACTORY.createMonotonicityInfo();
726 info.setCoreMask(triplet.coreMask);
727 info.setPosetMask(triplet.posetMask);
728 info.setPosetComparator(triplet.comparator);
729 recipe.setOptionalMonotonicityInfo(info);
730 }
731 }
732
733 RecipeTraceInfo trimmerTrace = intermediateTraceFactory.apply(trimmerRecipe, compiledParentPlan);
734 return finalTraceFactory.apply(recipe, trimmerTrace);
735 }
736 }
737
738 /**
739 * Projects the final compiled form of a PBody onto the parameter tuple
740 * @param compiledBody the compiled form of the body, with all constraints enforced, not yet projected to query parameters
741 * @param enforceUniqueness whether distinctness shall be enforced after the projection.
742 * Specify false only if directly connecting to a production node.
743 * @since 2.1
744 */
745 RecipeTraceInfo projectBodyFinalToParameters(
746 final CompiledSubPlan compiledBody,
747 boolean enforceUniqueness)
748 {
749 final PBody body = compiledBody.getSubPlan().getBody();
750 final List<PVariable> parameterList = body.getSymbolicParameterVariables();
751
752 return doProjectPlan(compiledBody, parameterList, enforceUniqueness,
753 parentTrace -> parentTrace,
754 (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace),
755 (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace)
756 );
757 }
758
759 private CompiledSubPlan doCompileStart(PStart operation, SubPlan plan) {
760 if (!operation.getAPrioriVariables().isEmpty()) {
761 throw new IllegalArgumentException("Input variables unsupported by Rete: " + plan.toShortString());
762 }
763 final ConstantRecipe recipe = FACTORY.createConstantRecipe();
764 recipe.getConstantValues().clear();
765
766 return new CompiledSubPlan(plan, new ArrayList<PVariable>(), recipe);
767 }
768
769 private CompiledSubPlan doCompileEnumerate(EnumerablePConstraint constraint, SubPlan plan) {
770 final PlanningTrace trimmedTrace = doEnumerateAndDeduplicate(constraint, plan);
771
772 return trimmedTrace.cloneFor(plan);
773 }
774
775 private PlanningTrace doEnumerateAndDeduplicate(EnumerablePConstraint constraint, SubPlan plan) {
776 final PlanningTrace coreTrace = doEnumerateDispatch(plan, constraint);
777 final PlanningTrace trimmedTrace = CompilerHelper.checkAndTrimEqualVariables(plan, coreTrace);
778 return trimmedTrace;
779 }
780
781 private PlanningTrace doEnumerateDispatch(SubPlan plan, EnumerablePConstraint constraint) {
782 if (constraint instanceof RelationEvaluation) {
783 return compileEnumerable(plan, (RelationEvaluation) constraint);
784 } else if (constraint instanceof BinaryTransitiveClosure) {
785 return compileEnumerable(plan, (BinaryTransitiveClosure) constraint);
786 } else if (constraint instanceof BinaryReflexiveTransitiveClosure) {
787 return compileEnumerable(plan, (BinaryReflexiveTransitiveClosure) constraint);
788 } else if (constraint instanceof RepresentativeElectionConstraint) {
789 return compileEnumerable(plan, (RepresentativeElectionConstraint) constraint);
790 } else if (constraint instanceof ConstantValue) {
791 return compileEnumerable(plan, (ConstantValue) constraint);
792 } else if (constraint instanceof PositivePatternCall) {
793 return compileEnumerable(plan, (PositivePatternCall) constraint);
794 } else if (constraint instanceof TypeConstraint) {
795 return compileEnumerable(plan, (TypeConstraint) constraint);
796 }
797 throw new UnsupportedOperationException("Unknown enumerable constraint " + constraint);
798 }
799
800 private PlanningTrace compileEnumerable(SubPlan plan, BinaryReflexiveTransitiveClosure constraint) {
801 // TODO the implementation would perform better if an inequality check would be used after tcRecipe and
802 // uniqueness enforcer be replaced by a transparent node with multiple parents, but such a node is not available
803 // in recipe metamodel in VIATRA 2.0
804
805 // Find called query
806 final PQuery referredQuery = constraint.getSupplierKey();
807 final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple());
808
809 // Calculate irreflexive transitive closure
810 final TransitiveClosureRecipe tcRecipe = FACTORY.createTransitiveClosureRecipe();
811 tcRecipe.setParent(callTrace.getRecipe());
812 final PlanningTrace tcTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), tcRecipe, callTrace);
813
814 // Enumerate universe type
815 final IInputKey inputKey = constraint.getUniverseType();
816 final InputRecipe universeTypeRecipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), inputKey.getArity());
817 final PlanningTrace universeTypeTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(
818 Tuples.staticArityFlatTupleOf(constraint.getVariablesTuple().get(0))), universeTypeRecipe);
819
820 // Calculate reflexive access by duplicating universe type column
821 final TrimmerRecipe reflexiveRecipe = FACTORY.createTrimmerRecipe();
822 reflexiveRecipe.setMask(RecipesHelper.mask(1, 0, 0));
823 reflexiveRecipe.setParent(universeTypeRecipe);
824 final PlanningTrace reflexiveTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), reflexiveRecipe, universeTypeTrace);
825
826 // Finally, reduce duplicates after a join
827 final UniquenessEnforcerRecipe brtcRecipe = FACTORY.createUniquenessEnforcerRecipe();
828 brtcRecipe.getParents().add(tcRecipe);
829 brtcRecipe.getParents().add(reflexiveRecipe);
830
831 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), brtcRecipe, reflexiveTrace, tcTrace);
832 }
833
834 private PlanningTrace compileEnumerable(SubPlan plan, RepresentativeElectionConstraint constraint) {
835 var referredQuery = constraint.getSupplierKey();
836 var callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple());
837 var recipe = FACTORY.createRepresentativeElectionRecipe();
838 recipe.setParent(callTrace.getRecipe());
839 recipe.setConnectivity(constraint.getConnectivity());
840 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace);
841 }
842
843 private PlanningTrace compileEnumerable(SubPlan plan, BinaryTransitiveClosure constraint) {
844 final PQuery referredQuery = constraint.getSupplierKey();
845 final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple());
846
847 final TransitiveClosureRecipe recipe = FACTORY.createTransitiveClosureRecipe();
848 recipe.setParent(callTrace.getRecipe());
849
850 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace);
851 }
852
853 private PlanningTrace compileEnumerable(SubPlan plan, RelationEvaluation constraint) {
854 final List<ReteNodeRecipe> parentRecipes = new ArrayList<ReteNodeRecipe>();
855 final List<RecipeTraceInfo> parentTraceInfos = new ArrayList<RecipeTraceInfo>();
856 for (final PQuery inputQuery : constraint.getReferredQueries()) {
857 final CompiledQuery compiledQuery = getCompiledForm(inputQuery);
858 parentRecipes.add(compiledQuery.getRecipe());
859 parentTraceInfos.add(compiledQuery);
860 }
861 final RelationEvaluationRecipe recipe = FACTORY.createRelationEvaluationRecipe();
862 recipe.getParents().addAll(parentRecipes);
863 recipe.setEvaluator(RecipesHelper.expressionDefinition(constraint.getEvaluator()));
864 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, parentTraceInfos);
865 }
866
867 private PlanningTrace compileEnumerable(SubPlan plan, PositivePatternCall constraint) {
868 final PQuery referredQuery = constraint.getReferredQuery();
869 return referQuery(referredQuery, plan, constraint.getVariablesTuple());
870 }
871
872 private PlanningTrace compileEnumerable(SubPlan plan, TypeConstraint constraint) {
873 final IInputKey inputKey = constraint.getSupplierKey();
874 final InputRecipe recipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), inputKey.getArity());
875 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe);
876 }
877
878 private PlanningTrace compileEnumerable(SubPlan plan, ConstantValue constraint) {
879 final ConstantRecipe recipe = FACTORY.createConstantRecipe();
880 recipe.getConstantValues().add(constraint.getSupplierKey());
881 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe);
882 }
883
884 // TODO handle recursion
885 private PlanningTrace referQuery(PQuery query, SubPlan plan, Tuple actualParametersTuple) {
886 RecipeTraceInfo referredQueryTrace = originalTraceOfReferredQuery(query);
887 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(actualParametersTuple),
888 referredQueryTrace.getRecipe(), referredQueryTrace.getParentRecipeTracesForCloning());
889 }
890
891 private RecipeTraceInfo originalTraceOfReferredQuery(PQuery query) {
892 // eliminate superfluous production node?
893 if (PVisibility.EMBEDDED == query.getVisibility()) { // currently inline patterns only
894 Set<PBody> rewrittenBodies = normalizer.rewrite(query).getBodies();
895 if (1 == rewrittenBodies.size()) { // non-disjunctive
896 // TODO in the future, check if non-recursive - (not currently permitted)
897
898 PBody pBody = rewrittenBodies.iterator().next();
899 SubPlan bodyFinalPlan = getPlan(pBody);
900
901 // skip over any projections at the end
902 bodyFinalPlan = BuildHelper.eliminateTrailingProjections(bodyFinalPlan);
903
904 // TODO checkAndTrimEqualVariables may introduce superfluous trim,
905 // but whatever (no uniqueness enforcer needed)
906
907 // compile body
908 final CompiledSubPlan compiledBody = getCompiledForm(bodyFinalPlan);
909
910 // project to parameter list, add uniqueness enforcer if necessary
911 return projectBodyFinalToParameters(compiledBody, true /* ensure uniqueness, as no production node is used */);
912 }
913 }
914
915 // otherwise, regular reference to recipe realizing the query
916 return getCompiledForm(query);
917 }
918
919 protected List<CompiledSubPlan> getCompiledFormOfParents(SubPlan plan) {
920 List<CompiledSubPlan> results = new ArrayList<CompiledSubPlan>();
921 for (SubPlan parentPlan : plan.getParentPlans()) {
922 results.add(getCompiledForm(parentPlan));
923 }
924 return results;
925 }
926
927 /**
928 * Returns an unmodifiable view of currently cached compiled queries.
929 */
930 public Map<PQuery, CompiledQuery> getCachedCompiledQueries() {
931 return Collections.unmodifiableMap(queryCompilerCache);
932 }
933
934 /**
935 * Returns an unmodifiable view of currently cached query plans.
936 */
937 public Map<PBody, SubPlan> getCachedQueryPlans() {
938 return Collections.unmodifiableMap(plannerCache);
939 }
940
941 private QueryEvaluationHint getHints(SubPlan plan) {
942 return getHints(plan.getBody().getPattern());
943 }
944
945 private QueryEvaluationHint getHints(PQuery pattern) {
946 return hintProvider.getQueryEvaluationHint(pattern);
947 }
948}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinCandidate.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinCandidate.java
new file mode 100644
index 00000000..45350099
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinCandidate.java
@@ -0,0 +1,162 @@
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.rete.construction.quasitree;
11
12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.List;
15import java.util.Map;
16import java.util.Set;
17import java.util.stream.Collectors;
18import java.util.stream.Stream;
19
20import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
21import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory;
22import tools.refinery.viatra.runtime.matchers.planning.helpers.FunctionalDependencyHelper;
23import tools.refinery.viatra.runtime.matchers.planning.operations.PJoin;
24import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
25import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
26import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
27import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
28
29/**
30 * @author Gabor Bergmann
31 *
32 */
33class JoinCandidate {
34 private QueryAnalyzer analyzer;
35
36 SubPlan primary;
37 SubPlan secondary;
38
39 Set<PVariable> varPrimary;
40 Set<PVariable> varSecondary;
41 Set<PVariable> varCommon;
42
43 List<PConstraint> consPrimary;
44 List<PConstraint> consSecondary;
45
46
47 JoinCandidate(SubPlan primary, SubPlan secondary, QueryAnalyzer analyzer) {
48 super();
49 this.primary = primary;
50 this.secondary = secondary;
51 this.analyzer = analyzer;
52
53 varPrimary = getPrimary().getVisibleVariables();
54 varSecondary = getSecondary().getVisibleVariables();
55 varCommon = CollectionsFactory.createSet(varPrimary);
56 varCommon.retainAll(varSecondary);
57
58 consPrimary = new ArrayList<PConstraint>(primary.getAllEnforcedConstraints());
59 Collections.sort(consPrimary, TieBreaker.CONSTRAINT_COMPARATOR);
60 consSecondary = new ArrayList<PConstraint>(secondary.getAllEnforcedConstraints());
61 Collections.sort(consSecondary, TieBreaker.CONSTRAINT_COMPARATOR);
62 }
63
64
65
66 /**
67 * @return the a
68 */
69 public SubPlan getPrimary() {
70 return primary;
71 }
72
73 /**
74 * @return the b
75 */
76 public SubPlan getSecondary() {
77 return secondary;
78 }
79
80 public SubPlan getJoinedPlan(SubPlanFactory factory) {
81 // check special cases first
82 if (isTrivial())
83 return primary;
84 if (isSubsumption())
85 return
86 (consPrimary.size() > consSecondary.size()) ? primary : secondary;
87
88
89 // default case
90 return factory.createSubPlan(new PJoin(), primary, secondary);
91 }
92
93 @Override
94 public String toString() {
95 return primary.toString() + " |x| " + secondary.toString();
96 }
97
98 /**
99 * @return the varPrimary
100 */
101 public Set<PVariable> getVarPrimary() {
102 return varPrimary;
103 }
104
105 /**
106 * @return the varSecondary
107 */
108 public Set<PVariable> getVarSecondary() {
109 return varSecondary;
110 }
111
112 /**
113 * @return constraints of primary, sorted according to {@link TieBreaker#CONSTRAINT_COMPARATOR}.
114 */
115 public List<PConstraint> getConsPrimary() {
116 return consPrimary;
117 }
118 /**
119 * @return constraints of secondary, sorted according to {@link TieBreaker#CONSTRAINT_COMPARATOR}.
120 */
121 public List<PConstraint> getConsSecondary() {
122 return consSecondary;
123 }
124
125
126
127 public boolean isTrivial() {
128 return getPrimary().equals(getSecondary());
129 }
130
131 public boolean isSubsumption() {
132 return consPrimary.containsAll(consSecondary) || consSecondary.containsAll(consPrimary);
133 }
134
135 public boolean isCheckOnly() {
136 return varPrimary.containsAll(varSecondary) || varSecondary.containsAll(varPrimary);
137 }
138
139 public boolean isDescartes() {
140 return Collections.disjoint(varPrimary, varSecondary);
141 }
142
143 private Boolean heath;
144
145 // it is a Heath-join iff common variables functionally determine either all primary or all secondary variables
146 public boolean isHeath() {
147 if (heath == null) {
148 Set<PConstraint> union = Stream.concat(
149 primary.getAllEnforcedConstraints().stream(),
150 secondary.getAllEnforcedConstraints().stream()
151 ).collect(Collectors.toSet());
152 Map<Set<PVariable>, Set<PVariable>> dependencies =
153 analyzer.getFunctionalDependencies(union, false);
154 // does varCommon determine either varPrimary or varSecondary?
155 Set<PVariable> varCommonClosure = FunctionalDependencyHelper.closureOf(varCommon, dependencies);
156
157 heath = varCommonClosure.containsAll(varPrimary) || varCommonClosure.containsAll(varSecondary);
158 }
159 return heath;
160 }
161
162}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinOrderingHeuristics.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinOrderingHeuristics.java
new file mode 100644
index 00000000..0ea7c1d9
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinOrderingHeuristics.java
@@ -0,0 +1,49 @@
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.rete.construction.quasitree;
11
12import java.util.Comparator;
13
14import tools.refinery.viatra.runtime.rete.util.Options;
15import tools.refinery.viatra.runtime.rete.util.OrderingCompareAgent;
16
17/**
18 * @author Gabor Bergmann
19 *
20 */
21public class JoinOrderingHeuristics implements Comparator<JoinCandidate> {
22
23 @Override
24 public int compare(JoinCandidate jc1, JoinCandidate jc2) {
25 return new OrderingCompareAgent<JoinCandidate>(jc1, jc2) {
26 @Override
27 protected void doCompare() {
28 swallowBoolean(true && consider(preferTrue(a.isTrivial(), b.isTrivial()))
29 && consider(preferTrue(a.isSubsumption(), b.isSubsumption()))
30 && consider(preferTrue(a.isCheckOnly(), b.isCheckOnly()))
31 && consider(
32 Options.functionalDependencyOption == Options.FunctionalDependencyOption.OFF ?
33 dontCare() :
34 preferTrue(a.isHeath(), b.isHeath())
35 )
36 && consider(preferFalse(a.isDescartes(), b.isDescartes()))
37
38 // TODO main heuristic decisions
39
40 // tie breaking
41 && consider(preferLess(a.getConsPrimary(), b.getConsPrimary(), TieBreaker.CONSTRAINT_LIST_COMPARATOR))
42 && consider(preferLess(a.getConsSecondary(), b.getConsSecondary(), TieBreaker.CONSTRAINT_LIST_COMPARATOR))
43 && consider(preferLess(System.identityHashCode(a), System.identityHashCode(b))));
44 }
45 }.compare();
46
47 }
48
49} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/QuasiTreeLayout.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/QuasiTreeLayout.java
new file mode 100644
index 00000000..9b814376
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/QuasiTreeLayout.java
@@ -0,0 +1,205 @@
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.rete.construction.quasitree;
11
12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.LinkedHashSet;
15import java.util.List;
16import java.util.Set;
17
18import org.apache.log4j.Logger;
19import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
20import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
21import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
22import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
23import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
24import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy;
25import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
26import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory;
27import tools.refinery.viatra.runtime.matchers.planning.helpers.BuildHelper;
28import tools.refinery.viatra.runtime.matchers.planning.operations.PApply;
29import tools.refinery.viatra.runtime.matchers.planning.operations.PEnumerate;
30import tools.refinery.viatra.runtime.matchers.planning.operations.PProject;
31import tools.refinery.viatra.runtime.matchers.planning.operations.PStart;
32import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint;
33import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint;
34import tools.refinery.viatra.runtime.matchers.psystem.PBody;
35import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
36import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
37import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
38import tools.refinery.viatra.runtime.rete.construction.RetePatternBuildException;
39import tools.refinery.viatra.runtime.rete.util.ReteHintOptions;
40
41/**
42 * Layout ideas: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=398763
43 *
44 * @author Gabor Bergmann
45 *
46 */
47public class QuasiTreeLayout implements IQueryPlannerStrategy {
48
49 private IQueryBackendHintProvider hintProvider;
50 private IQueryBackendContext backendContext;
51 private QueryAnalyzer queryAnalyzer;
52
53 public QuasiTreeLayout(IQueryBackendContext backendContext) {
54 this(backendContext, backendContext.getHintProvider());
55 }
56
57 public QuasiTreeLayout(IQueryBackendContext backendContext, IQueryBackendHintProvider hintProvider) {
58 this.backendContext = backendContext;
59 this.hintProvider = hintProvider;
60 queryAnalyzer = backendContext.getQueryAnalyzer();
61 }
62
63 @Override
64 public SubPlan plan(PBody pSystem, Logger logger, IQueryMetaContext context) {
65 return new Scaffold(pSystem, logger, context).run();
66 }
67
68 public class Scaffold {
69 PBody pSystem;
70 PQuery query;
71 IQueryMetaContext context;
72 private QueryEvaluationHint hints;
73 //IOperationCompiler compiler;
74 //SubPlanProcessor planProcessor = new SubPlanProcessor();
75 SubPlanFactory planFactory;
76
77 Set<DeferredPConstraint> deferredConstraints = null;
78 Set<EnumerablePConstraint> enumerableConstraints = null;
79 Set<ConstantValue> constantConstraints = null;
80 Set<SubPlan> forefront = new LinkedHashSet<SubPlan>();
81 Logger logger;
82
83 Scaffold(PBody pSystem, Logger logger, /*IOperationCompiler compiler,*/ IQueryMetaContext context) {
84 this.pSystem = pSystem;
85 this.logger = logger;
86 this.context = context;
87 this.planFactory = new SubPlanFactory(pSystem);
88 query = pSystem.getPattern();
89 //this.compiler = compiler;
90 //planProcessor.setCompiler(compiler);
91
92 hints = hintProvider.getQueryEvaluationHint(query);
93 }
94
95 /**
96 * @throws ViatraQueryRuntimeException
97 */
98 public SubPlan run() {
99 try {
100 logger.debug(String.format(
101 "%s: patternbody build started for %s",
102 getClass().getSimpleName(),
103 query.getFullyQualifiedName()));
104
105 // PROCESS CONSTRAINTS
106 deferredConstraints = pSystem.getConstraintsOfType(DeferredPConstraint.class);
107 enumerableConstraints = pSystem.getConstraintsOfType(EnumerablePConstraint.class);
108 constantConstraints = pSystem.getConstraintsOfType(ConstantValue.class);
109
110 for (EnumerablePConstraint enumerable : enumerableConstraints) {
111 SubPlan plan = planFactory.createSubPlan(new PEnumerate(enumerable));
112 admitSubPlan(plan);
113 }
114 if (enumerableConstraints.isEmpty()) { // EXTREME CASE
115 SubPlan plan = planFactory.createSubPlan(new PStart());
116 admitSubPlan(plan);
117 }
118
119 // JOIN FOREFRONT PLANS WHILE POSSIBLE
120 while (forefront.size() > 1) {
121 // TODO QUASI-TREE TRIVIAL JOINS?
122
123 List<JoinCandidate> candidates = generateJoinCandidates();
124 JoinOrderingHeuristics ordering = new JoinOrderingHeuristics();
125 JoinCandidate selectedJoin = Collections.min(candidates, ordering);
126 doJoin(selectedJoin);
127 }
128 assert (forefront.size() == 1);
129
130 // PROJECT TO PARAMETERS
131 SubPlan preFinalPlan = forefront.iterator().next();
132 SubPlan finalPlan = planFactory.createSubPlan(new PProject(pSystem.getSymbolicParameterVariables()), preFinalPlan);
133
134 // FINAL CHECK, whether all exported variables are present + all constraint satisfied
135 BuildHelper.finalCheck(pSystem, finalPlan, context);
136 // TODO integrate the check above in SubPlan / POperation
137
138 logger.debug(String.format(
139 "%s: patternbody query plan concluded for %s as: %s",
140 getClass().getSimpleName(),
141 query.getFullyQualifiedName(),
142 finalPlan.toLongString()));
143 return finalPlan;
144 } catch (RetePatternBuildException ex) {
145 ex.setPatternDescription(query);
146 throw ex;
147 }
148 }
149
150 public List<JoinCandidate> generateJoinCandidates() {
151 List<JoinCandidate> candidates = new ArrayList<JoinCandidate>();
152 int bIndex = 0;
153 for (SubPlan b : forefront) {
154 int aIndex = 0;
155 for (SubPlan a : forefront) {
156 if (aIndex++ >= bIndex)
157 break;
158 candidates.add(new JoinCandidate(a, b, queryAnalyzer));
159 }
160 bIndex++;
161 }
162 return candidates;
163 }
164
165 private void admitSubPlan(SubPlan plan) {
166 // are there any unapplied constant filters that we can apply here?
167 if (ReteHintOptions.prioritizeConstantFiltering.getValueOrDefault(hints)) {
168 for (ConstantValue constantConstraint : constantConstraints) {
169 if (!plan.getAllEnforcedConstraints().contains(constantConstraint) &&
170 plan.getVisibleVariables().containsAll(constantConstraint.getAffectedVariables())) {
171 plan = planFactory.createSubPlan(new PApply(constantConstraint), plan);
172 }
173 }
174 }
175 // are there any variables that will not be needed anymore and are worth trimming?
176 // (check only if there are unenforced enumerables, so that there are still upcoming joins)
177// if (Options.planTrimOption != Options.PlanTrimOption.OFF &&
178// !plan.getAllEnforcedConstraints().containsAll(enumerableConstraints)) {
179 if (true) {
180 final SubPlan trimmed = BuildHelper.trimUnneccessaryVariables(
181 planFactory, plan, true, queryAnalyzer);
182 plan = trimmed;
183 }
184 // are there any checkable constraints?
185 for (DeferredPConstraint deferred : deferredConstraints) {
186 if (!plan.getAllEnforcedConstraints().contains(deferred)) {
187 if (deferred.isReadyAt(plan, context)) {
188 admitSubPlan(planFactory.createSubPlan(new PApply(deferred), plan));
189 return;
190 }
191 }
192 }
193 // if no checkable constraints and no unused variables
194 forefront.add(plan);
195 }
196
197 private void doJoin(JoinCandidate selectedJoin) {
198 forefront.remove(selectedJoin.getPrimary());
199 forefront.remove(selectedJoin.getSecondary());
200 admitSubPlan(selectedJoin.getJoinedPlan(planFactory));
201 }
202
203 }
204
205}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/TieBreaker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/TieBreaker.java
new file mode 100644
index 00000000..0b955922
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/TieBreaker.java
@@ -0,0 +1,30 @@
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.rete.construction.quasitree;
10
11import java.util.Comparator;
12
13import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
14import tools.refinery.viatra.runtime.rete.util.LexicographicComparator;
15
16/**
17 * Class providing comparators for breaking ties somewhat more deterministically.
18 * @author Bergmann Gabor
19 *
20 */
21public class TieBreaker {
22
23 private TieBreaker() {/*Utility class constructor*/}
24
25 public static final Comparator<PConstraint> CONSTRAINT_COMPARATOR = (arg0, arg1) -> arg0.getMonotonousID() - arg1.getMonotonousID();
26
27 public static final Comparator<Iterable<? extends PConstraint>> CONSTRAINT_LIST_COMPARATOR =
28 new LexicographicComparator<PConstraint>(CONSTRAINT_COMPARATOR);
29
30}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/AbstractEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/AbstractEvaluatorNode.java
new file mode 100644
index 00000000..d32a0449
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/AbstractEvaluatorNode.java
@@ -0,0 +1,65 @@
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.rete.eval;
10
11import java.util.Iterator;
12
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import tools.refinery.viatra.runtime.matchers.util.Direction;
15import tools.refinery.viatra.runtime.rete.network.ReteContainer;
16import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
17import tools.refinery.viatra.runtime.rete.single.SingleInputNode;
18
19/**
20 * @author Bergmann Gabor
21 */
22public abstract class AbstractEvaluatorNode extends SingleInputNode implements IEvaluatorNode {
23
24 /**
25 * @since 1.5
26 */
27 protected EvaluatorCore core;
28
29
30 /**
31 * @since 1.5
32 */
33 public AbstractEvaluatorNode(ReteContainer reteContainer, EvaluatorCore core) {
34 super(reteContainer);
35 this.core = core;
36 core.init(this);
37 }
38
39 /**
40 * @since 1.5
41 */
42 @Override
43 public ReteContainer getReteContainer() {
44 return getContainer();
45 }
46
47 /**
48 * @since 1.5
49 */
50 @Override
51 public String prettyPrintTraceInfoPatternList() {
52 return getTraceInfoPatternsEnumerated();
53 }
54
55 /**
56 * @since 2.4
57 */
58 protected void propagateIterableUpdate(final Direction direction, final Iterable<Tuple> update, final Timestamp timestamp) {
59 final Iterator<Tuple> itr = update.iterator();
60 while (itr.hasNext()) {
61 propagateUpdate(direction, itr.next(), timestamp);
62 }
63 }
64
65}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/EvaluatorCore.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/EvaluatorCore.java
new file mode 100644
index 00000000..c45c6048
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/EvaluatorCore.java
@@ -0,0 +1,180 @@
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.rete.eval;
10
11import java.util.Collections;
12import java.util.Iterator;
13import java.util.Map;
14import java.util.Set;
15
16import org.apache.log4j.Logger;
17import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
18import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
20import tools.refinery.viatra.runtime.matchers.tuple.TupleValueProvider;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
22import tools.refinery.viatra.runtime.matchers.util.Sets;
23
24/**
25 * An instance of this class performs the evaluation of Java expressions.
26 *
27 * @author Bergmann Gabor
28 * @author Tamas Szabo
29 * @since 1.5
30 */
31public abstract class EvaluatorCore {
32
33 protected Logger logger;
34 protected IExpressionEvaluator evaluator;
35 /**
36 * @since 2.4
37 */
38 protected int sourceTupleWidth;
39 private Map<String, Integer> parameterPositions;
40 protected IQueryRuntimeContext runtimeContext;
41 protected IEvaluatorNode evaluatorNode;
42
43 public EvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator,
44 final Map<String, Integer> parameterPositions, final int sourceTupleWidth) {
45 this.logger = logger;
46 this.evaluator = evaluator;
47 this.parameterPositions = parameterPositions;
48 this.sourceTupleWidth = sourceTupleWidth;
49 }
50
51 public void init(final IEvaluatorNode evaluatorNode) {
52 this.evaluatorNode = evaluatorNode;
53 this.runtimeContext = evaluatorNode.getReteContainer().getNetwork().getEngine().getRuntimeContext();
54 }
55
56 /**
57 * @since 2.4
58 */
59 public abstract Iterable<Tuple> performEvaluation(final Tuple input);
60
61 protected abstract String evaluationKind();
62
63 public Object evaluateTerm(final Tuple input) {
64 // actual evaluation
65 Object result = null;
66 try {
67 final TupleValueProvider tupleParameters = new TupleValueProvider(runtimeContext.unwrapTuple(input),
68 parameterPositions);
69 result = evaluator.evaluateExpression(tupleParameters);
70 } catch (final Exception e) {
71 logger.warn(String.format(
72 "The incremental pattern matcher encountered an error during %s evaluation for pattern(s) %s over values %s. Error message: %s. (Developer note: %s in %s)",
73 evaluationKind(), evaluatorNode.prettyPrintTraceInfoPatternList(), prettyPrintTuple(input),
74 e.getMessage(), e.getClass().getSimpleName(), this.evaluatorNode), e);
75 result = errorResult();
76 }
77
78 return result;
79 }
80
81 protected String prettyPrintTuple(final Tuple tuple) {
82 return tuple.toString();
83 }
84
85 protected Object errorResult() {
86 return null;
87 }
88
89 public static class PredicateEvaluatorCore extends EvaluatorCore {
90
91 public PredicateEvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator,
92 final Map<String, Integer> parameterPositions, final int sourceTupleWidth) {
93 super(logger, evaluator, parameterPositions, sourceTupleWidth);
94 }
95
96 @Override
97 public Iterable<Tuple> performEvaluation(final Tuple input) {
98 final Object result = evaluateTerm(input);
99 if (Boolean.TRUE.equals(result)) {
100 return Collections.singleton(input);
101 } else {
102 return null;
103 }
104 }
105
106 @Override
107 protected String evaluationKind() {
108 return "check()";
109 }
110
111 }
112
113 public static class FunctionEvaluatorCore extends EvaluatorCore {
114
115 /**
116 * @since 2.4
117 */
118 protected final boolean isUnwinding;
119
120 public FunctionEvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator,
121 final Map<String, Integer> parameterPositions, final int sourceTupleWidth) {
122 this(logger, evaluator, parameterPositions, sourceTupleWidth, false);
123 }
124
125 /**
126 * @since 2.4
127 */
128 public FunctionEvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator,
129 final Map<String, Integer> parameterPositions, final int sourceTupleWidth, final boolean isUnwinding) {
130 super(logger, evaluator, parameterPositions, sourceTupleWidth);
131 this.isUnwinding = isUnwinding;
132 }
133
134 @Override
135 public Iterable<Tuple> performEvaluation(final Tuple input) {
136 final Object result = evaluateTerm(input);
137 if (result != null) {
138 if (this.isUnwinding) {
139 final Set<?> resultAsSet = (result instanceof Set<?>) ? (Set<?>) result
140 : (result instanceof Iterable<?>) ? Sets.newSet((Iterable<?>) result) : null;
141
142 if (resultAsSet != null) {
143 return () -> {
144 final Iterator<?> wrapped = resultAsSet.iterator();
145 return new Iterator<Tuple>() {
146 @Override
147 public boolean hasNext() {
148 return wrapped.hasNext();
149 }
150
151 @Override
152 public Tuple next() {
153 final Object next = wrapped.next();
154 return Tuples.staticArityLeftInheritanceTupleOf(input,
155 runtimeContext.wrapElement(next));
156 }
157 };
158 };
159 } else {
160 throw new IllegalStateException(
161 "This is an unwinding evaluator, which expects the evaluation result to either be a set or an iterable, but it was "
162 + result);
163 }
164 } else {
165 return Collections.singleton(
166 Tuples.staticArityLeftInheritanceTupleOf(input, runtimeContext.wrapElement(result)));
167 }
168 } else {
169 return null;
170 }
171 }
172
173 @Override
174 protected String evaluationKind() {
175 return "eval" + (this.isUnwinding ? "Unwind" : "") + "()";
176 }
177
178 }
179
180}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/IEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/IEvaluatorNode.java
new file mode 100644
index 00000000..177433ab
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/IEvaluatorNode.java
@@ -0,0 +1,25 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.rete.eval;
10
11import tools.refinery.viatra.runtime.rete.network.ReteContainer;
12
13/**
14 * This interface is required for the communication between the evaluation core end the evaluator node.
15 * @author Gabor Bergmann
16 * @since 1.5
17 */
18public interface IEvaluatorNode {
19
20 ReteContainer getReteContainer();
21
22 String prettyPrintTraceInfoPatternList();
23
24
25}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/MemorylessEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/MemorylessEvaluatorNode.java
new file mode 100644
index 00000000..8928645c
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/MemorylessEvaluatorNode.java
@@ -0,0 +1,75 @@
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.rete.eval;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Iterator;
14import java.util.Map;
15import java.util.Map.Entry;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19import tools.refinery.viatra.runtime.matchers.util.Direction;
20import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
23
24/**
25 * @author Bergmann Gabor
26 *
27 */
28public class MemorylessEvaluatorNode extends AbstractEvaluatorNode {
29
30 /**
31 * @since 1.5
32 */
33 public MemorylessEvaluatorNode(final ReteContainer reteContainer, final EvaluatorCore core) {
34 super(reteContainer, core);
35 }
36
37 @Override
38 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
39 final Collection<Tuple> parentTuples = new ArrayList<Tuple>();
40 propagatePullInto(parentTuples, flush);
41 for (final Tuple parentTuple : parentTuples) {
42 final Iterable<Tuple> output = core.performEvaluation(parentTuple);
43 if (output != null) {
44 final Iterator<Tuple> itr = output.iterator();
45 while (itr.hasNext()) {
46 collector.add(itr.next());
47 }
48 }
49 }
50 }
51
52 @Override
53 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
54 final Map<Tuple, Timeline<Timestamp>> parentTuples = CollectionsFactory.createMap();
55 propagatePullIntoWithTimestamp(parentTuples, flush);
56 for (final Entry<Tuple, Timeline<Timestamp>> entry : parentTuples.entrySet()) {
57 final Iterable<Tuple> output = core.performEvaluation(entry.getKey());
58 if (output != null) {
59 final Iterator<Tuple> itr = output.iterator();
60 while (itr.hasNext()) {
61 collector.put(itr.next(), entry.getValue());
62 }
63 }
64 }
65 }
66
67 @Override
68 public void update(final Direction direction, final Tuple input, final Timestamp timestamp) {
69 final Iterable<Tuple> output = core.performEvaluation(input);
70 if (output != null) {
71 propagateIterableUpdate(direction, output, timestamp);
72 }
73 }
74
75}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/OutputCachingEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/OutputCachingEvaluatorNode.java
new file mode 100644
index 00000000..40a20c4e
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/OutputCachingEvaluatorNode.java
@@ -0,0 +1,311 @@
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.rete.eval;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Iterator;
14import java.util.Map;
15import java.util.Map.Entry;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
19import tools.refinery.viatra.runtime.matchers.util.Clearable;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.Direction;
22import tools.refinery.viatra.runtime.matchers.util.Signed;
23import tools.refinery.viatra.runtime.matchers.util.TimelyMemory;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
25import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
26import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
27import tools.refinery.viatra.runtime.rete.network.ReteContainer;
28import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
29import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
30import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
31
32/**
33 * An evaluator node that caches the evaluation result. This node is also capable of caching the timestamps associated
34 * with the result tuples if it is used in recursive differential dataflow evaluation.
35 *
36 * @author Bergmann Gabor
37 * @author Tamas Szabo
38 */
39public class OutputCachingEvaluatorNode extends AbstractEvaluatorNode implements Clearable, ResumableNode {
40
41 /**
42 * @since 2.3
43 */
44 protected NetworkStructureChangeSensitiveLogic logic;
45
46 /**
47 * @since 2.4
48 */
49 protected Map<Tuple, Iterable<Tuple>> outputCache;
50
51 /**
52 * Maps input tuples to timestamps. It is wrong to map evaluation result to timestamps because the different input
53 * tuples may yield the same evaluation result. This field is null as long as this node is in a non-recursive group.
54 *
55 * @since 2.4
56 */
57 protected TimelyMemory<Timestamp> memory;
58
59 /**
60 * @since 2.4
61 */
62 protected CommunicationGroup group;
63
64 /**
65 * @since 1.5
66 */
67 public OutputCachingEvaluatorNode(final ReteContainer reteContainer, final EvaluatorCore core) {
68 super(reteContainer, core);
69 reteContainer.registerClearable(this);
70 this.outputCache = CollectionsFactory.createMap();
71 this.logic = createLogic();
72 }
73
74 @Override
75 public CommunicationGroup getCurrentGroup() {
76 return this.group;
77 }
78
79 @Override
80 public void setCurrentGroup(final CommunicationGroup group) {
81 this.group = group;
82 }
83
84 @Override
85 public void networkStructureChanged() {
86 super.networkStructureChanged();
87 this.logic = createLogic();
88 }
89
90 @Override
91 public void clear() {
92 this.outputCache.clear();
93 if (this.memory != null) {
94 this.memory.clear();
95 }
96 }
97
98 /**
99 * @since 2.3
100 */
101 protected NetworkStructureChangeSensitiveLogic createLogic() {
102 if (this.reteContainer.isTimelyEvaluation()
103 && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
104 if (this.memory == null) {
105 this.memory = new TimelyMemory<Timestamp>(reteContainer.isTimelyEvaluation() && reteContainer
106 .getTimelyConfiguration().getTimelineRepresentation() == TimelineRepresentation.FAITHFUL);
107 }
108 return TIMELY;
109 } else {
110 return TIMELESS;
111 }
112 }
113
114 @Override
115 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
116 this.logic.pullInto(collector, flush);
117 }
118
119 @Override
120 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
121 this.logic.pullIntoWithTimeline(collector, flush);
122 }
123
124 @Override
125 public void update(final Direction direction, final Tuple input, final Timestamp timestamp) {
126 this.logic.update(direction, input, timestamp);
127 }
128
129 /**
130 * @since 2.4
131 */
132 @Override
133 public Timestamp getResumableTimestamp() {
134 if (this.memory == null) {
135 return null;
136 } else {
137 return this.memory.getResumableTimestamp();
138 }
139 }
140
141 /**
142 * @since 2.4
143 */
144 @Override
145 public void resumeAt(final Timestamp timestamp) {
146 this.logic.resumeAt(timestamp);
147 }
148
149 /**
150 * @since 2.3
151 */
152 protected static abstract class NetworkStructureChangeSensitiveLogic {
153
154 /**
155 * @since 2.4
156 */
157 public abstract void update(final Direction direction, final Tuple input, final Timestamp timestamp);
158
159 public abstract void pullInto(final Collection<Tuple> collector, final boolean flush);
160
161 /**
162 * @since 2.4
163 */
164 public abstract void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush);
165
166 /**
167 * @since 2.4
168 */
169 public abstract void resumeAt(final Timestamp timestamp);
170
171 }
172
173 private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() {
174
175 @Override
176 public void resumeAt(final Timestamp timestamp) {
177 // there is nothing to resume in the timeless case because we do not even care about timestamps
178 }
179
180 @Override
181 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
182 throw new UnsupportedOperationException();
183 }
184
185 @Override
186 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
187 for (final Iterable<Tuple> output : outputCache.values()) {
188 if (output != NORESULT) {
189 final Iterator<Tuple> itr = output.iterator();
190 while (itr.hasNext()) {
191 collector.add(itr.next());
192 }
193 }
194 }
195 }
196
197 @Override
198 public void update(final Direction direction, final Tuple input, final Timestamp timestamp) {
199 if (direction == Direction.INSERT) {
200 final Iterable<Tuple> output = core.performEvaluation(input);
201 if (output != null) {
202 final Iterable<Tuple> previous = outputCache.put(input, output);
203 if (previous != null) {
204 throw new IllegalStateException(
205 String.format("Duplicate insertion of tuple %s into node %s", input, this));
206 }
207 propagateIterableUpdate(direction, output, timestamp);
208 }
209 } else {
210 final Iterable<Tuple> output = outputCache.remove(input);
211 if (output != null) {
212 // may be null if no result was yielded
213 propagateIterableUpdate(direction, output, timestamp);
214 }
215 }
216 }
217 };
218
219 private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() {
220
221 @Override
222 public void resumeAt(final Timestamp timestamp) {
223 final Map<Tuple, Diff<Timestamp>> diffMap = memory.resumeAt(timestamp);
224
225 for (final Entry<Tuple, Diff<Timestamp>> entry : diffMap.entrySet()) {
226 final Tuple input = entry.getKey();
227 final Iterable<Tuple> output = outputCache.get(input);
228 if (output != NORESULT) {
229 for (final Signed<Timestamp> signed : entry.getValue()) {
230 propagateIterableUpdate(signed.getDirection(), output, signed.getPayload());
231 }
232 }
233
234 if (memory.get(input) == null) {
235 outputCache.remove(input);
236 }
237 }
238
239 final Timestamp nextTimestamp = memory.getResumableTimestamp();
240 if (nextTimestamp != null) {
241 group.notifyHasMessage(mailbox, nextTimestamp);
242 }
243 }
244
245 @Override
246 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
247 for (final Entry<Tuple, Timeline<Timestamp>> entry : memory.asMap().entrySet()) {
248 final Tuple input = entry.getKey();
249 final Iterable<Tuple> output = outputCache.get(input);
250 if (output != NORESULT) {
251 final Timeline<Timestamp> timestamp = entry.getValue();
252 final Iterator<Tuple> itr = output.iterator();
253 while (itr.hasNext()) {
254 collector.put(itr.next(), timestamp);
255 }
256 }
257 }
258 }
259
260 @Override
261 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
262 TIMELESS.pullInto(collector, flush);
263 }
264
265 @Override
266 public void update(final Direction direction, final Tuple input, final Timestamp timestamp) {
267 if (direction == Direction.INSERT) {
268 Iterable<Tuple> output = outputCache.get(input);
269 if (output == null) {
270 output = core.performEvaluation(input);
271 if (output == null) {
272 // the evaluation result is really null
273 output = NORESULT;
274 }
275 outputCache.put(input, output);
276 }
277 final Diff<Timestamp> diff = memory.put(input, timestamp);
278 if (output != NORESULT) {
279 for (final Signed<Timestamp> signed : diff) {
280 propagateIterableUpdate(signed.getDirection(), output, signed.getPayload());
281 }
282 }
283 } else {
284 final Iterable<Tuple> output = outputCache.get(input);
285 final Diff<Timestamp> diff = memory.remove(input, timestamp);
286 if (memory.get(input) == null) {
287 outputCache.remove(input);
288 }
289 if (output != NORESULT) {
290 for (final Signed<Timestamp> signed : diff) {
291 propagateIterableUpdate(signed.getDirection(), output, signed.getPayload());
292 }
293 }
294 }
295 }
296 };
297
298 /**
299 * This field is used to represent the "null" evaluation result. This is an optimization used in the timely case
300 * where the same tuple may be inserted multiple times with different timestamps. This way, we can also cache if
301 * something evaluated to null (instead of just forgetting about the previously computed result), thus avoiding the
302 * need to re-run a potentially expensive evaluation.
303 */
304 private static final Iterable<Tuple> NORESULT = Collections
305 .singleton(Tuples.staticArityFlatTupleOf(NoResult.INSTANCE));
306
307 private enum NoResult {
308 INSTANCE
309 }
310
311}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/RelationEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/RelationEvaluatorNode.java
new file mode 100644
index 00000000..68d277e8
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/RelationEvaluatorNode.java
@@ -0,0 +1,183 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2022, Tamas Szabo, GitHub
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 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.rete.eval;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.List;
14import java.util.Map;
15import java.util.Map.Entry;
16import java.util.Set;
17
18import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator;
19import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.RelationEvaluation;
20import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
21import tools.refinery.viatra.runtime.matchers.util.Clearable;
22import tools.refinery.viatra.runtime.matchers.util.Direction;
23import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines;
25import tools.refinery.viatra.runtime.rete.misc.SimpleReceiver;
26import tools.refinery.viatra.runtime.rete.network.ProductionNode;
27import tools.refinery.viatra.runtime.rete.network.Receiver;
28import tools.refinery.viatra.runtime.rete.network.ReteContainer;
29import tools.refinery.viatra.runtime.rete.network.StandardNode;
30import tools.refinery.viatra.runtime.rete.network.Supplier;
31import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
32import tools.refinery.viatra.runtime.rete.single.AbstractUniquenessEnforcerNode;
33
34/**
35 * A node that operates in batch-style (see {@link Receiver#doesProcessUpdatesInBatch()} and evaluates arbitrary Java
36 * logic represented by an {@link IRelationEvaluator} on the input relations. This is the backing computation node of a
37 * {@link RelationEvaluation} constraint.
38 *
39 * @author Tamas Szabo
40 * @since 2.8
41 */
42public class RelationEvaluatorNode extends StandardNode implements Supplier, Clearable {
43
44 private final IRelationEvaluator evaluator;
45 private Set<Tuple> cachedOutputs;
46 private Supplier[] inputSuppliers;
47 private BatchingReceiver[] inputReceivers;
48
49 public RelationEvaluatorNode(final ReteContainer container, final IRelationEvaluator evaluator) {
50 super(container);
51 this.evaluator = evaluator;
52 this.reteContainer.registerClearable(this);
53 }
54
55 @Override
56 public void clear() {
57 this.cachedOutputs.clear();
58 }
59
60 public void connectToParents(final List<Supplier> inputSuppliers) {
61 this.inputSuppliers = new Supplier[inputSuppliers.size()];
62 this.inputReceivers = new BatchingReceiver[inputSuppliers.size()];
63
64 final List<Integer> inputArities = evaluator.getInputArities();
65
66 if (inputArities.size() != inputSuppliers.size()) {
67 throw new IllegalStateException(evaluator.toString() + " expects " + inputArities.size()
68 + " inputs, but got " + inputSuppliers.size() + " input(s)!");
69 }
70
71 for (int i = 0; i < inputSuppliers.size(); i++) {
72 final int currentExpectedInputArity = inputArities.get(i);
73 final Supplier inputSupplier = inputSuppliers.get(i);
74 // it is expected that the supplier is a production node because
75 // the corresponding constraint itself accepts a list of PQuery
76 if (!(inputSupplier instanceof ProductionNode)) {
77 throw new IllegalStateException(
78 evaluator.toString() + " expects each one of its suppliers to be instances of "
79 + ProductionNode.class.getSimpleName() + " but got an instance of "
80 + inputSupplier.getClass().getSimpleName() + "!");
81 }
82 final int currentActualInputArity = ((ProductionNode) inputSupplier).getPosMapping().size();
83 if (currentActualInputArity != currentExpectedInputArity) {
84 throw new IllegalStateException(
85 evaluator.toString() + " expects input arity " + currentExpectedInputArity + " at position " + i
86 + " but got " + currentActualInputArity + "!");
87 }
88 final BatchingReceiver inputReceiver = new BatchingReceiver((ProductionNode) inputSupplier,
89 this.reteContainer);
90 this.inputSuppliers[i] = inputSupplier;
91 this.inputReceivers[i] = inputReceiver;
92 this.reteContainer.connectAndSynchronize(inputSupplier, inputReceiver);
93 reteContainer.getCommunicationTracker().registerDependency(inputReceiver, this);
94 }
95
96 // initialize the output relation
97 final List<Set<Tuple>> inputSets = new ArrayList<Set<Tuple>>();
98 for (final BatchingReceiver inputReceiver : this.inputReceivers) {
99 inputSets.add(inputReceiver.getTuples());
100 }
101 this.cachedOutputs = evaluateRelation(inputSets);
102 }
103
104 @Override
105 public void networkStructureChanged() {
106 if (this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
107 throw new IllegalStateException(this.toString() + " cannot be used in recursive evaluation!");
108 }
109 super.networkStructureChanged();
110 }
111
112 @Override
113 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
114 collector.addAll(this.cachedOutputs);
115 }
116
117 @Override
118 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
119 final Timeline<Timestamp> timeline = Timelines.createFrom(Timestamp.ZERO);
120 for (final Tuple output : this.cachedOutputs) {
121 collector.put(output, timeline);
122 }
123 }
124
125 private Set<Tuple> evaluateRelation(final List<Set<Tuple>> inputs) {
126 try {
127 return this.evaluator.evaluateRelation(inputs);
128 } catch (final Exception e) {
129 throw new IllegalStateException("Exception during the evaluation of " + this.evaluator.toString() + "!", e);
130 }
131 }
132
133 private void batchUpdateCompleted() {
134 final List<Set<Tuple>> inputSets = new ArrayList<Set<Tuple>>();
135 for (final BatchingReceiver inputReceiver : this.inputReceivers) {
136 inputSets.add(inputReceiver.getTuples());
137 }
138 final Set<Tuple> newOutputs = evaluateRelation(inputSets);
139 for (final Tuple tuple : newOutputs) {
140 if (this.cachedOutputs != null && this.cachedOutputs.remove(tuple)) {
141 // already known tuple - do nothing
142 } else {
143 // newly inserted tuple
144 propagateUpdate(Direction.INSERT, tuple, Timestamp.ZERO);
145 }
146 }
147 if (this.cachedOutputs != null) {
148 for (final Tuple tuple : this.cachedOutputs) {
149 // lost tuple
150 propagateUpdate(Direction.DELETE, tuple, Timestamp.ZERO);
151 }
152 }
153 this.cachedOutputs = newOutputs;
154 }
155
156 public class BatchingReceiver extends SimpleReceiver {
157 private final ProductionNode source;
158
159 private BatchingReceiver(final ProductionNode source, final ReteContainer container) {
160 super(container);
161 this.source = source;
162 }
163
164 private Set<Tuple> getTuples() {
165 return ((AbstractUniquenessEnforcerNode) this.source).getTuples();
166 }
167
168 @Override
169 public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) {
170 throw new UnsupportedOperationException("This receiver only supports batch-style operation!");
171 }
172
173 @Override
174 public void batchUpdate(final Collection<Entry<Tuple, Integer>> updates, final Timestamp timestamp) {
175 assert Timestamp.ZERO.equals(timestamp);
176 // there is nothing to do here because the source production node has already updated itself
177 // the only thing we need to do is to issue the callback
178 RelationEvaluatorNode.this.batchUpdateCompleted();
179 }
180
181 }
182
183}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DefaultIndexerListener.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DefaultIndexerListener.java
new file mode 100644
index 00000000..6306a482
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DefaultIndexerListener.java
@@ -0,0 +1,28 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, 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.rete.index;
10
11import java.lang.ref.WeakReference;
12
13import tools.refinery.viatra.runtime.rete.network.Node;
14
15public abstract class DefaultIndexerListener implements IndexerListener {
16
17 WeakReference<Node> owner;
18
19 public DefaultIndexerListener(Node owner) {
20 this.owner = new WeakReference<Node>(owner);
21 }
22
23 @Override
24 public Node getOwner() {
25 return owner.get();
26 }
27
28}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DualInputNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DualInputNode.java
new file mode 100644
index 00000000..170ac460
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DualInputNode.java
@@ -0,0 +1,348 @@
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.rete.index;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19import tools.refinery.viatra.runtime.matchers.util.Direction;
20import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
21import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
22import tools.refinery.viatra.runtime.rete.network.Receiver;
23import tools.refinery.viatra.runtime.rete.network.ReteContainer;
24import tools.refinery.viatra.runtime.rete.network.StandardNode;
25import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
26import tools.refinery.viatra.runtime.rete.network.communication.Timestamp.AllZeroMap;
27import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand;
28import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
29import tools.refinery.viatra.runtime.rete.util.Options;
30
31/**
32 * Abstract superclass for nodes with two inputs that are matched against each other.
33 *
34 * @author Gabor Bergmann
35 */
36public abstract class DualInputNode extends StandardNode implements NetworkStructureChangeSensitiveNode {
37
38 /**
39 * @since 2.3
40 */
41 protected NetworkStructureChangeSensitiveLogic logic;
42
43 public IterableIndexer getPrimarySlot() {
44 return primarySlot;
45 }
46
47 public Indexer getSecondarySlot() {
48 return secondarySlot;
49 }
50
51 /**
52 * @author Gabor Bergmann
53 *
54 */
55 public enum Side {
56 PRIMARY, SECONDARY, BOTH;
57
58 public Side opposite() {
59 switch (this) {
60 case PRIMARY:
61 return SECONDARY;
62 case SECONDARY:
63 return PRIMARY;
64 case BOTH:
65 return BOTH;
66 default:
67 return BOTH;
68 }
69 }
70 }
71
72 /**
73 * Holds the primary input slot of this node.
74 */
75 protected IterableIndexer primarySlot;
76
77 /**
78 * Holds the secondary input slot of this node.
79 */
80 protected Indexer secondarySlot;
81
82 /**
83 * Optional complementer mask
84 */
85 protected TupleMask complementerSecondaryMask;
86
87 /**
88 * true if the primary and secondary slots coincide
89 */
90 protected boolean coincidence;
91
92 /**
93 * @param reteContainer
94 */
95 public DualInputNode(final ReteContainer reteContainer, final TupleMask complementerSecondaryMask) {
96 super(reteContainer);
97 this.complementerSecondaryMask = complementerSecondaryMask;
98 this.indexerGroupCache = CollectionsFactory.createMap();
99 this.refreshIndexerGroupCache();
100 }
101
102 /**
103 * Should be called only once, when node is initialized.
104 */
105 public void connectToIndexers(final IterableIndexer primarySlot, final Indexer secondarySlot) {
106 this.primarySlot = primarySlot;
107 this.secondarySlot = secondarySlot;
108
109 reteContainer.getCommunicationTracker().registerDependency(primarySlot, this);
110 reteContainer.getCommunicationTracker().registerDependency(secondarySlot, this);
111
112 // attach listeners
113 // if there is syncing, do this after the flush done for pulling, but before syncing updates
114 coincidence = primarySlot.equals(secondarySlot);
115
116 if (!coincidence) { // regular case
117 primarySlot.attachListener(new DefaultIndexerListener(this) {
118 @Override
119 public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement,
120 final Tuple signature, final boolean change, final Timestamp timestamp) {
121 DualInputNode.this.logic.notifyUpdate(Side.PRIMARY, direction, updateElement, signature, change,
122 timestamp);
123 }
124
125 @Override
126 public String toString() {
127 return "primary@" + DualInputNode.this;
128 }
129 });
130 secondarySlot.attachListener(new DefaultIndexerListener(this) {
131 public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement,
132 final Tuple signature, final boolean change, final Timestamp timestamp) {
133 DualInputNode.this.logic.notifyUpdate(Side.SECONDARY, direction, updateElement, signature, change,
134 timestamp);
135 }
136
137 @Override
138 public String toString() {
139 return "secondary@" + DualInputNode.this;
140 }
141 });
142 } else { // if the two slots are the same, updates have to be handled carefully
143 primarySlot.attachListener(new DefaultIndexerListener(this) {
144 public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement,
145 final Tuple signature, final boolean change, final Timestamp timestamp) {
146 DualInputNode.this.logic.notifyUpdate(Side.BOTH, direction, updateElement, signature, change,
147 timestamp);
148 }
149
150 @Override
151 public String toString() {
152 return "both@" + DualInputNode.this;
153 }
154 });
155 }
156
157 for (final Receiver receiver : getReceivers()) {
158 this.reteContainer.getDelayedCommandQueue()
159 .add(new DelayedConnectCommand(this, receiver, this.reteContainer));
160 }
161
162 // Given that connectToIndexers registers new dependencies, the networkStructureChanged() method will be called
163 // by the CommunicationTracker, and the implementation of that method in turn will call refreshIndexerGroupCache() anyway.
164 this.refreshIndexerGroupCache();
165 }
166
167 /**
168 * Helper: retrieves all stored substitutions from the opposite side memory.
169 *
170 * @return the collection of opposite substitutions if any, or null if none
171 */
172 protected Collection<Tuple> retrieveOpposites(final Side side, final Tuple signature) {
173 return getSlot(side.opposite()).get(signature);
174 }
175
176 /**
177 * @since 2.3
178 */
179 protected NetworkStructureChangeSensitiveLogic createLogic() {
180 if (this.reteContainer.isTimelyEvaluation()
181 && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
182 return createTimelyLogic();
183 } else {
184 return createTimelessLogic();
185 }
186 }
187
188 /**
189 * Helper: unifies a left and right partial matching.
190 */
191 protected Tuple unify(final Tuple left, final Tuple right) {
192 return complementerSecondaryMask.combine(left, right, Options.enableInheritance, true);
193 }
194
195 @Override
196 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
197 this.logic.pullInto(collector, flush);
198 }
199
200 @Override
201 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
202 this.logic.pullIntoWithTimeline(collector, flush);
203 }
204
205 /**
206 * Helper: unifies a substitution from the specified side with another substitution from the other side.
207 */
208 protected Tuple unify(final Side side, final Tuple ps, final Tuple opposite) {
209 switch (side) {
210 case PRIMARY:
211 return unify(ps, opposite);
212 case SECONDARY:
213 return unify(opposite, ps);
214 case BOTH:
215 return unify(ps, opposite);
216 default:
217 return null;
218 }
219 }
220
221 /**
222 * Simulates the behavior of the node for calibration purposes only.
223 */
224 public abstract Tuple calibrate(final Tuple primary, final Tuple secondary);
225
226 /**
227 * @param complementerSecondaryMask
228 * the complementerSecondaryMask to set
229 */
230 public void setComplementerSecondaryMask(final TupleMask complementerSecondaryMask) {
231 this.complementerSecondaryMask = complementerSecondaryMask;
232 }
233
234 /**
235 * Retrieves the slot corresponding to the specified side.
236 */
237 protected Indexer getSlot(final Side side) {
238 if (side == Side.SECONDARY) {
239 return secondarySlot;
240 } else {
241 return primarySlot;
242 }
243 }
244
245 @Override
246 public void assignTraceInfo(final TraceInfo traceInfo) {
247 super.assignTraceInfo(traceInfo);
248 if (traceInfo.propagateToIndexerParent()) {
249 if (primarySlot != null) {
250 primarySlot.acceptPropagatedTraceInfo(traceInfo);
251 }
252 if (secondarySlot != null) {
253 secondarySlot.acceptPropagatedTraceInfo(traceInfo);
254 }
255 }
256 }
257
258 @Override
259 public void networkStructureChanged() {
260 super.networkStructureChanged();
261 this.logic = createLogic();
262 this.refreshIndexerGroupCache();
263 }
264
265 /**
266 * @since 2.3
267 */
268 protected abstract NetworkStructureChangeSensitiveLogic createTimelyLogic();
269
270 /**
271 * @since 2.3
272 */
273 protected abstract NetworkStructureChangeSensitiveLogic createTimelessLogic();
274
275 /**
276 * This map caches the result of a CommunicationTracker.areInSameGroup(indexer, this) call. It does that for both
277 * the primary and secondary slots. This way we can avoid the lookup in the getWithTimestamp call for each tuple.
278 * The cache needs to be maintained when the network structure changes.
279 * @since 2.3
280 */
281 protected Map<Indexer, Boolean> indexerGroupCache;
282
283 /**
284 * @since 2.3
285 */
286 protected void refreshIndexerGroupCache() {
287 this.indexerGroupCache.clear();
288 if (this.primarySlot != null) {
289 this.indexerGroupCache.put(this.primarySlot,
290 this.reteContainer.getCommunicationTracker().areInSameGroup(this.primarySlot, this));
291 }
292 if (this.secondarySlot != null) {
293 this.indexerGroupCache.put(this.secondarySlot,
294 this.reteContainer.getCommunicationTracker().areInSameGroup(this.secondarySlot, this));
295 }
296 }
297
298 /**
299 * @since 2.4
300 */
301 protected Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature, final Indexer indexer) {
302 if (this.indexerGroupCache.get(indexer)) {
303 // recursive timely case
304 return indexer.getTimeline(signature);
305 } else {
306 // the indexer is in a different group, treat all of its tuples as they would have timestamp 0
307 final Collection<Tuple> tuples = indexer.get(signature);
308 if (tuples == null) {
309 return null;
310 } else {
311 return new AllZeroMap<Tuple>((Set<Tuple>) tuples);
312 }
313 }
314 }
315
316 /**
317 * @since 2.3
318 */
319 protected static abstract class NetworkStructureChangeSensitiveLogic {
320
321 /**
322 * Abstract handler for update event.
323 *
324 * @param side
325 * The side on which the event occurred.
326 * @param direction
327 * The direction of the update.
328 * @param updateElement
329 * The partial matching that is inserted.
330 * @param signature
331 * Masked signature of updateElement.
332 * @param change
333 * Indicates whether this is/was the first/last instance of this signature in this slot.
334 * @since 2.4
335 */
336 public abstract void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement,
337 final Tuple signature, final boolean change, final Timestamp timestamp);
338
339 public abstract void pullInto(final Collection<Tuple> collector, final boolean flush);
340
341 /**
342 * @since 2.4
343 */
344 public abstract void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush);
345
346 }
347
348}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ExistenceNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ExistenceNode.java
new file mode 100644
index 00000000..275ff638
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ExistenceNode.java
@@ -0,0 +1,199 @@
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.rete.index;
11
12import java.util.Collection;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.matchers.util.Signed;
18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import tools.refinery.viatra.runtime.rete.network.ReteContainer;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21
22/**
23 * Propagates all substitutions arriving at the PRIMARY slot if and only if (a matching substitution on the SECONDARY is
24 * present) xor (NEGATIVE).
25 *
26 * The negative parameter specifies whether this node checks for existence or non-existence.
27 * <p>
28 * It is mandatory in differential dataflow evaluation that the secondary parent is in an upstream dependency component
29 * (so that every secondary tuple comes with zero timestamp).
30 *
31 * @author Gabor Bergmann
32 */
33public class ExistenceNode extends DualInputNode {
34
35 protected boolean negative;
36
37 /**
38 * @param reteContainer
39 * @param negative
40 * if false, act as existence checker, otherwise a nonexistence-checker
41 */
42 public ExistenceNode(final ReteContainer reteContainer, final boolean negative) {
43 super(reteContainer, null);
44 this.negative = negative;
45 this.logic = createLogic();
46 }
47
48 @Override
49 public Tuple calibrate(final Tuple primary, final Tuple secondary) {
50 return primary;
51 }
52
53 @Override
54 public void networkStructureChanged() {
55 if (this.reteContainer.isTimelyEvaluation() && this.secondarySlot != null
56 && this.reteContainer.getCommunicationTracker().areInSameGroup(this, this.secondarySlot)) {
57 throw new IllegalStateException("Secondary parent must be in an upstream dependency component!");
58 }
59 super.networkStructureChanged();
60 }
61
62 private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() {
63
64 @Override
65 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
66 throw new UnsupportedOperationException();
67 }
68
69 @Override
70 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
71 if (primarySlot == null || secondarySlot == null) {
72 return;
73 }
74 if (flush) {
75 reteContainer.flushUpdates();
76 }
77
78 for (final Tuple signature : primarySlot.getSignatures()) {
79 // primaries can not be null due to the contract of IterableIndex.getSignatures()
80 final Collection<Tuple> primaries = primarySlot.get(signature);
81 final Collection<Tuple> opposites = secondarySlot.get(signature);
82 if ((opposites != null) ^ negative) {
83 collector.addAll(primaries);
84 }
85 }
86 }
87
88 @Override
89 public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement,
90 final Tuple signature, final boolean change, final Timestamp timestamp) {
91 // in the default case, all timestamps must be zero
92 assert Timestamp.ZERO.equals(timestamp);
93
94 switch (side) {
95 case PRIMARY:
96 if ((retrieveOpposites(side, signature) != null) ^ negative) {
97 propagateUpdate(direction, updateElement, timestamp);
98 }
99 break;
100 case SECONDARY:
101 if (change) {
102 final Collection<Tuple> opposites = retrieveOpposites(side, signature);
103 if (opposites != null) {
104 for (final Tuple opposite : opposites) {
105 propagateUpdate((negative ? direction.opposite() : direction), opposite, timestamp);
106 }
107 }
108 }
109 break;
110 case BOTH:
111 // in case the slots coincide,
112 // negative --> always empty
113 // !positive --> identity
114 if (!negative) {
115 propagateUpdate(direction, updateElement, timestamp);
116 }
117 break;
118 }
119 }
120 };
121
122 private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() {
123
124 @Override
125 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
126 if (primarySlot == null || secondarySlot == null) {
127 return;
128 }
129 if (flush) {
130 reteContainer.flushUpdates();
131 }
132
133 for (final Tuple signature : primarySlot.getSignatures()) {
134 // primaries can not be null due to the contract of IterableIndex.getSignatures()
135 final Map<Tuple, Timeline<Timestamp>> primaries = getTimeline(signature, primarySlot);
136 // see contract: secondary must be in an upstream SCC
137 final Collection<Tuple> opposites = secondarySlot.get(signature);
138 if ((opposites != null) ^ negative) {
139 for (final Tuple primary : primaries.keySet()) {
140 collector.put(primary, primaries.get(primary));
141 }
142 }
143 }
144 }
145
146 @Override
147 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
148 ExistenceNode.this.TIMELESS.pullInto(collector, flush);
149 }
150
151 @Override
152 public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement,
153 final Tuple signature, final boolean change, final Timestamp timestamp) {
154 switch (side) {
155 case PRIMARY: {
156 final Collection<Tuple> opposites = secondarySlot.get(signature);
157 if ((opposites != null) ^ negative) {
158 propagateUpdate(direction, updateElement, timestamp);
159 }
160 break;
161 }
162 case SECONDARY: {
163 final Map<Tuple, Timeline<Timestamp>> opposites = primarySlot.getTimeline(signature);
164 if (change) {
165 if (opposites != null) {
166 for (final Tuple opposite : opposites.keySet()) {
167 for (final Signed<Timestamp> oppositeSigned : opposites.get(opposite).asChangeSequence()) {
168 final Direction product = direction.multiply(oppositeSigned.getDirection());
169 propagateUpdate((negative ? product.opposite() : product), opposite,
170 oppositeSigned.getPayload());
171 }
172 }
173 }
174 }
175 break;
176 }
177 case BOTH:
178 // in case the slots coincide,
179 // negative --> always empty
180 // positive --> identity
181 if (!negative) {
182 propagateUpdate(direction, updateElement, timestamp);
183 }
184 break;
185 }
186 }
187 };
188
189 @Override
190 protected NetworkStructureChangeSensitiveLogic createTimelessLogic() {
191 return this.TIMELESS;
192 }
193
194 @Override
195 protected NetworkStructureChangeSensitiveLogic createTimelyLogic() {
196 return this.TIMELY;
197 }
198
199}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/GenericProjectionIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/GenericProjectionIndexer.java
new file mode 100644
index 00000000..3de10def
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/GenericProjectionIndexer.java
@@ -0,0 +1,76 @@
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.rete.index;
11
12import java.util.Collection;
13import java.util.Iterator;
14import java.util.Map;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
20import tools.refinery.viatra.runtime.rete.network.Receiver;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
23
24/**
25 * A generic Indexer capable of indexing along any valid TupleMask. Does not keep track of parents, because will not
26 * ever pull parents.
27 *
28 * @author Gabor Bergmann
29 *
30 */
31public class GenericProjectionIndexer extends IndexerWithMemory implements ProjectionIndexer {
32
33 public GenericProjectionIndexer(ReteContainer reteContainer, TupleMask mask) {
34 super(reteContainer, mask);
35 }
36
37 @Override
38 protected void update(Direction direction, Tuple updateElement, Tuple signature, boolean change,
39 Timestamp timestamp) {
40 propagate(direction, updateElement, signature, change, timestamp);
41 }
42
43 @Override
44 public Collection<Tuple> get(Tuple signature) {
45 return memory.get(signature);
46 }
47
48 @Override
49 public Map<Tuple, Timeline<Timestamp>> getTimeline(Tuple signature) {
50 return memory.getWithTimeline(signature);
51 }
52
53 @Override
54 public Iterator<Tuple> iterator() {
55 return memory.iterator();
56 }
57
58 @Override
59 public Iterable<Tuple> getSignatures() {
60 return memory.getSignatures();
61 }
62
63 /**
64 * @since 2.0
65 */
66 @Override
67 public int getBucketCount() {
68 return memory.getKeysetSize();
69 }
70
71 @Override
72 public Receiver getActiveNode() {
73 return this;
74 }
75
76}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IdentityIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IdentityIndexer.java
new file mode 100644
index 00000000..6c158f2c
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IdentityIndexer.java
@@ -0,0 +1,76 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2012 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.rete.index;
11
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Iterator;
15import java.util.List;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.util.Direction;
20import tools.refinery.viatra.runtime.rete.network.Node;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.Supplier;
23import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
24
25/**
26 * Defines an abstract trivial indexer that identically projects the contents of some stateful node, and can therefore
27 * save space. Can only exist in connection with a stateful store, and must be operated by another node (the active
28 * node). Do not attach parents directly!
29 *
30 * @author Gabor Bergmann
31 * @noimplement Rely on the provided implementations
32 * @noreference Use only via standard Node and Indexer interfaces
33 * @noinstantiate This class is not intended to be instantiated by clients.
34 */
35public abstract class IdentityIndexer extends SpecializedProjectionIndexer {
36
37 protected abstract Collection<Tuple> getTuples();
38
39 public IdentityIndexer(ReteContainer reteContainer, int tupleWidth, Supplier parent,
40 Node activeNode, List<ListenerSubscription> sharedSubscriptionList) {
41 super(reteContainer, TupleMask.identity(tupleWidth), parent, activeNode, sharedSubscriptionList);
42 }
43
44 @Override
45 public Collection<Tuple> get(Tuple signature) {
46 if (contains(signature)) {
47 return Collections.singleton(signature);
48 } else
49 return null;
50 }
51
52 protected boolean contains(Tuple signature) {
53 return getTuples().contains(signature);
54 }
55
56 @Override
57 public Collection<Tuple> getSignatures() {
58 return getTuples();
59 }
60
61 @Override
62 public int getBucketCount() {
63 return getTuples().size();
64 }
65
66 @Override
67 public Iterator<Tuple> iterator() {
68 return getTuples().iterator();
69 }
70
71 @Override
72 public void propagateToListener(IndexerListener listener, Direction direction, Tuple updateElement, Timestamp timestamp) {
73 listener.notifyIndexerUpdate(direction, updateElement, updateElement, true, timestamp);
74 }
75
76} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/Indexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/Indexer.java
new file mode 100644
index 00000000..fc9d7781
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/Indexer.java
@@ -0,0 +1,71 @@
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.rete.index;
11
12import java.util.Collection;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.network.Node;
19import tools.refinery.viatra.runtime.rete.network.Supplier;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21
22/**
23 * A node that indexes incoming Tuples by their signatures as specified by a TupleMask. Notifies listeners about such
24 * update events through the IndexerListener.
25 *
26 * Signature tuples are created by transforming the update tuples using the mask. Tuples stored with the same signature
27 * are grouped together. The group or a reduction thereof is retrievable.
28 *
29 * @author Gabor Bergmann
30 */
31public interface Indexer extends Node {
32 /**
33 * @return the mask by which the contents are indexed.
34 */
35 public TupleMask getMask();
36
37 /**
38 * @return the node whose contents are indexed.
39 */
40 public Supplier getParent();
41
42 /**
43 * @return all stored tuples that conform to the specified signature, null if there are none such. CONTRACT: do not
44 * modify!
45 */
46 public Collection<Tuple> get(Tuple signature);
47
48 /**
49 * @since 2.4
50 */
51 default public Map<Tuple, Timeline<Timestamp>> getTimeline(Tuple signature) {
52 throw new UnsupportedOperationException();
53 }
54
55 /**
56 * This indexer will be updated whenever a Rete update is sent to the active node (or an equivalent time slot
57 * allotted to it). The active node is typically the indexer itself, but it can be a different node such as its
58 * parent.
59 *
60 * @return the active node that operates this indexer
61 */
62 public Node getActiveNode();
63
64
65 public Collection<IndexerListener> getListeners();
66
67 public void attachListener(IndexerListener listener);
68
69 public void detachListener(IndexerListener listener);
70
71}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerListener.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerListener.java
new file mode 100644
index 00000000..f52b6a06
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerListener.java
@@ -0,0 +1,41 @@
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.rete.index;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.matchers.util.Direction;
14import tools.refinery.viatra.runtime.rete.network.Node;
15import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
16
17/**
18 * A listener for update events concerning an Indexer.
19 *
20 * @author Gabor Bergmann
21 *
22 */
23public interface IndexerListener {
24 /**
25 * Notifies recipient that the indexer has just received an update. Contract: indexer already reflects the updated
26 * state.
27 *
28 * @param direction
29 * the direction of the update.
30 * @param updateElement
31 * the tuple that was updated.
32 * @param signature
33 * the signature of the tuple according to the indexer's mask.
34 * @param change
35 * whether this was the first inserted / last revoked update element with this particular signature.
36 * @since 2.4
37 */
38 void notifyIndexerUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change, Timestamp timestamp);
39
40 Node getOwner();
41}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerWithMemory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerWithMemory.java
new file mode 100644
index 00000000..a31562e9
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerWithMemory.java
@@ -0,0 +1,284 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2009 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.rete.index;
11
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Map;
15import java.util.Map.Entry;
16
17import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
22import tools.refinery.viatra.runtime.matchers.util.Direction;
23import tools.refinery.viatra.runtime.matchers.util.Signed;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
25import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
26import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
27import tools.refinery.viatra.runtime.rete.network.Receiver;
28import tools.refinery.viatra.runtime.rete.network.ReteContainer;
29import tools.refinery.viatra.runtime.rete.network.Supplier;
30import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
31import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
32import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
33import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
34import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
35import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
36
37/**
38 * @author Gabor Bergmann
39 * @author Tamas Szabo
40 */
41public abstract class IndexerWithMemory extends StandardIndexer
42 implements Receiver, NetworkStructureChangeSensitiveNode, ResumableNode {
43
44 protected MaskedTupleMemory<Timestamp> memory;
45
46 /**
47 * @since 2.3
48 */
49 protected NetworkStructureChangeSensitiveLogic logic;
50
51 /**
52 * @since 1.6
53 */
54 protected final Mailbox mailbox;
55
56 /**
57 * @since 2.4
58 */
59 protected CommunicationGroup group;
60
61 public IndexerWithMemory(final ReteContainer reteContainer, final TupleMask mask) {
62 super(reteContainer, mask);
63 final boolean isTimely = reteContainer.isTimelyEvaluation()
64 && reteContainer.getCommunicationTracker().isInRecursiveGroup(this);
65 memory = MaskedTupleMemory.create(mask, MemoryType.SETS, this, isTimely, isTimely && reteContainer
66 .getTimelyConfiguration().getTimelineRepresentation() == TimelineRepresentation.FAITHFUL);
67 reteContainer.registerClearable(memory);
68 mailbox = instantiateMailbox();
69 reteContainer.registerClearable(mailbox);
70 this.logic = createLogic();
71 }
72
73 @Override
74 public CommunicationGroup getCurrentGroup() {
75 return this.group;
76 }
77
78 @Override
79 public void setCurrentGroup(final CommunicationGroup group) {
80 this.group = group;
81 }
82
83 @Override
84 public void networkStructureChanged() {
85 super.networkStructureChanged();
86 final boolean wasTimely = this.memory.isTimely();
87 final boolean isTimely = this.reteContainer.isTimelyEvaluation()
88 && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this);
89 if (wasTimely != isTimely) {
90 final MaskedTupleMemory<Timestamp> newMemory = MaskedTupleMemory.create(mask, MemoryType.SETS, this,
91 isTimely, isTimely && reteContainer.getTimelyConfiguration()
92 .getTimelineRepresentation() == TimelineRepresentation.FAITHFUL);
93 newMemory.initializeWith(this.memory, Timestamp.ZERO);
94 memory.clear();
95 memory = newMemory;
96 }
97 this.logic = createLogic();
98 }
99
100 /**
101 * Instantiates the {@link Mailbox} of this receiver. Subclasses may override this method to provide their own
102 * mailbox implementation.
103 *
104 * @return the mailbox
105 * @since 2.0
106 */
107 protected Mailbox instantiateMailbox() {
108 if (this.reteContainer.isTimelyEvaluation()) {
109 return new TimelyMailbox(this, this.reteContainer);
110 } else {
111 return new BehaviorChangingMailbox(this, this.reteContainer);
112 }
113 }
114
115 @Override
116 public Mailbox getMailbox() {
117 return this.mailbox;
118 }
119
120 /**
121 * @since 2.0
122 */
123 public MaskedTupleMemory<Timestamp> getMemory() {
124 return memory;
125 }
126
127 @Override
128 public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) {
129 this.logic.update(direction, updateElement, timestamp);
130 }
131
132 /**
133 * Refined version of update
134 *
135 * @since 2.4
136 */
137 protected abstract void update(final Direction direction, final Tuple updateElement, final Tuple signature,
138 final boolean change, final Timestamp timestamp);
139
140 @Override
141 public void appendParent(final Supplier supplier) {
142 if (parent == null) {
143 parent = supplier;
144 } else {
145 throw new UnsupportedOperationException("Illegal RETE edge: " + this + " already has a parent (" + parent
146 + ") and cannot connect to additional parent (" + supplier + "). ");
147 }
148 }
149
150 @Override
151 public void removeParent(final Supplier supplier) {
152 if (parent == supplier) {
153 parent = null;
154 } else {
155 throw new IllegalArgumentException(
156 "Illegal RETE edge removal: the parent of " + this + " is not " + supplier);
157 }
158 }
159
160 /**
161 * @since 2.4
162 */
163 @Override
164 public Collection<Supplier> getParents() {
165 return Collections.singleton(parent);
166 }
167
168 /**
169 * @since 2.4
170 */
171 @Override
172 public void resumeAt(final Timestamp timestamp) {
173 this.logic.resumeAt(timestamp);
174 }
175
176 /**
177 * @since 2.4
178 */
179 @Override
180 public Timestamp getResumableTimestamp() {
181 return this.memory.getResumableTimestamp();
182 }
183
184 /**
185 * @since 2.3
186 */
187 protected static abstract class NetworkStructureChangeSensitiveLogic {
188
189 /**
190 * @since 2.4
191 */
192 public abstract void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp);
193
194 /**
195 * @since 2.4
196 */
197 public abstract void resumeAt(final Timestamp timestamp);
198
199 }
200
201 /**
202 * @since 2.3
203 */
204 protected NetworkStructureChangeSensitiveLogic createLogic() {
205 if (this.reteContainer.isTimelyEvaluation()
206 && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
207 return TIMELY;
208 } else {
209 return TIMELESS;
210 }
211 }
212
213 private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() {
214
215 @Override
216 public void resumeAt(final Timestamp timestamp) {
217 final Iterable<Tuple> signatures = memory.getResumableSignatures();
218
219 final Map<Tuple, Boolean> wasPresent = CollectionsFactory.createMap();
220 for (final Tuple signature : signatures) {
221 wasPresent.put(signature, memory.isPresentAtInfinity(signature));
222 }
223
224 final Map<Tuple, Map<Tuple, Diff<Timestamp>>> signatureMap = memory.resumeAt(timestamp);
225
226 for (final Entry<Tuple, Map<Tuple, Diff<Timestamp>>> outerEntry : signatureMap.entrySet()) {
227 final Tuple signature = outerEntry.getKey();
228 final Map<Tuple, Diff<Timestamp>> diffMap = outerEntry.getValue();
229 final boolean isPresent = memory.isPresentAtInfinity(signature);
230 // only send out a potential true value the first time for a given signature, then set it to false
231 boolean change = wasPresent.get(signature) ^ isPresent;
232
233 for (final Entry<Tuple, Diff<Timestamp>> innerEntry : diffMap.entrySet()) {
234 final Tuple tuple = innerEntry.getKey();
235 final Diff<Timestamp> diffs = innerEntry.getValue();
236 for (final Signed<Timestamp> signed : diffs) {
237 IndexerWithMemory.this.update(signed.getDirection(), tuple, signature, change,
238 signed.getPayload());
239 }
240 // change is a signature-wise flag, so it is ok to "try" to signal it for the first tuple only
241 change = false;
242 }
243 }
244
245 final Timestamp nextTimestamp = memory.getResumableTimestamp();
246 if (nextTimestamp != null) {
247 group.notifyHasMessage(mailbox, nextTimestamp);
248 }
249 }
250
251 @Override
252 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
253 final Tuple signature = mask.transform(update);
254 final boolean wasPresent = memory.isPresentAtInfinity(signature);
255 final Diff<Timestamp> resultDiff = direction == Direction.INSERT
256 ? memory.addWithTimestamp(update, signature, timestamp)
257 : memory.removeWithTimestamp(update, signature, timestamp);
258 final boolean isPresent = memory.isPresentAtInfinity(signature);
259 final boolean change = wasPresent ^ isPresent;
260 for (final Signed<Timestamp> signed : resultDiff) {
261 IndexerWithMemory.this.update(signed.getDirection(), update, signature, change, signed.getPayload());
262 }
263 }
264
265 };
266
267 private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() {
268
269 @Override
270 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
271 final Tuple signature = mask.transform(update);
272 final boolean change = direction == Direction.INSERT ? memory.add(update, signature)
273 : memory.remove(update, signature);
274 IndexerWithMemory.this.update(direction, update, signature, change, timestamp);
275 }
276
277 @Override
278 public void resumeAt(final Timestamp timestamp) {
279 // there is nothing to resume in the timeless case because we do not even care about timestamps
280 }
281
282 };
283
284} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IterableIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IterableIndexer.java
new file mode 100644
index 00000000..d6f8ef05
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IterableIndexer.java
@@ -0,0 +1,34 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2009 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.rete.index;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13
14/**
15 * An indexer that allows the iteration of all retrievable tuple groups (or reduced groups).
16 *
17 * @author Gabor Bergmann
18 *
19 */
20public interface IterableIndexer extends Indexer, Iterable<Tuple> {
21
22 /**
23 * A view consisting of exactly those signatures whose tuple group is not empty
24 * @since 2.0
25 */
26 public Iterable<Tuple> getSignatures();
27
28 /**
29 * @return the number of signatures whose tuple group is not empty
30 * @since 2.0
31 */
32 public int getBucketCount();
33
34}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/JoinNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/JoinNode.java
new file mode 100644
index 00000000..9a6a0de9
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/JoinNode.java
@@ -0,0 +1,193 @@
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.rete.index;
11
12import java.util.Collection;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.matchers.util.Signed;
19import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
20import tools.refinery.viatra.runtime.rete.network.ReteContainer;
21import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
22
23/**
24 * @author Gabor Bergmann
25 *
26 */
27public class JoinNode extends DualInputNode {
28
29 public JoinNode(final ReteContainer reteContainer, final TupleMask complementerSecondaryMask) {
30 super(reteContainer, complementerSecondaryMask);
31 this.logic = createLogic();
32 }
33
34 @Override
35 public Tuple calibrate(final Tuple primary, final Tuple secondary) {
36 return unify(primary, secondary);
37 }
38
39 private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() {
40
41 @Override
42 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
43 throw new UnsupportedOperationException();
44 }
45
46 @Override
47 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
48 if (primarySlot == null || secondarySlot == null) {
49 return;
50 }
51
52 if (flush) {
53 reteContainer.flushUpdates();
54 }
55
56 for (final Tuple signature : primarySlot.getSignatures()) {
57 // primaries can not be null due to the contract of IterableIndex.getSignatures()
58 final Collection<Tuple> primaries = primarySlot.get(signature);
59 final Collection<Tuple> opposites = secondarySlot.get(signature);
60 if (opposites != null) {
61 for (final Tuple primary : primaries) {
62 for (final Tuple opposite : opposites) {
63 collector.add(unify(primary, opposite));
64 }
65 }
66 }
67 }
68 }
69
70 @Override
71 public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement,
72 final Tuple signature, final boolean change, final Timestamp timestamp) {
73 // in the default case, all timestamps must be zero
74 assert Timestamp.ZERO.equals(timestamp);
75
76 final Collection<Tuple> opposites = retrieveOpposites(side, signature);
77
78 if (!coincidence) {
79 if (opposites != null) {
80 for (final Tuple opposite : opposites) {
81 propagateUpdate(direction, unify(side, updateElement, opposite), timestamp);
82 }
83 }
84 } else {
85 // compensate for coincidence of slots - this is the case when an Indexer is joined with itself
86 if (opposites != null) {
87 for (final Tuple opposite : opposites) {
88 if (opposite.equals(updateElement)) {
89 // handle self-joins of a single tuple separately
90 continue;
91 }
92 propagateUpdate(direction, unify(opposite, updateElement), timestamp);
93 propagateUpdate(direction, unify(updateElement, opposite), timestamp);
94 }
95 }
96
97 // handle self-joins here
98 propagateUpdate(direction, unify(updateElement, updateElement), timestamp);
99 }
100 }
101 };
102
103 private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() {
104
105 @Override
106 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
107 if (primarySlot == null || secondarySlot == null) {
108 return;
109 }
110
111 if (flush) {
112 reteContainer.flushUpdates();
113 }
114
115 for (final Tuple signature : primarySlot.getSignatures()) {
116 // primaries can not be null due to the contract of IterableIndex.getSignatures()
117 final Map<Tuple, Timeline<Timestamp>> primaries = getTimeline(signature, primarySlot);
118 final Map<Tuple, Timeline<Timestamp>> opposites = getTimeline(signature, secondarySlot);
119 if (opposites != null) {
120 for (final Tuple primary : primaries.keySet()) {
121 for (final Tuple opposite : opposites.keySet()) {
122 final Timeline<Timestamp> primaryTimeline = primaries.get(primary);
123 final Timeline<Timestamp> oppositeTimeline = opposites.get(opposite);
124 final Timeline<Timestamp> mergedTimeline = primaryTimeline
125 .mergeMultiplicative(oppositeTimeline);
126 if (!mergedTimeline.isEmpty()) {
127 collector.put(unify(primary, opposite), mergedTimeline);
128 }
129 }
130 }
131 }
132 }
133 }
134
135 @Override
136 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
137 JoinNode.this.TIMELESS.pullInto(collector, flush);
138 }
139
140 @Override
141 public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement,
142 final Tuple signature, final boolean change, final Timestamp timestamp) {
143 final Indexer oppositeIndexer = getSlot(side.opposite());
144 final Map<Tuple, Timeline<Timestamp>> opposites = getTimeline(signature, oppositeIndexer);
145
146 if (!coincidence) {
147 if (opposites != null) {
148 for (final Tuple opposite : opposites.keySet()) {
149 final Tuple unifiedTuple = unify(side, updateElement, opposite);
150 for (final Signed<Timestamp> signed : opposites.get(opposite).asChangeSequence()) {
151 // TODO only consider signed timestamps that are greater or equal to timestamp
152 // plus compact the previous timestamps into at most one update
153 propagateUpdate(signed.getDirection().multiply(direction), unifiedTuple,
154 timestamp.max(signed.getPayload()));
155 }
156 }
157 }
158 } else {
159 // compensate for coincidence of slots - this is the case when an Indexer is joined with itself
160 if (opposites != null) {
161 for (final Tuple opposite : opposites.keySet()) {
162 if (opposite.equals(updateElement)) {
163 // handle self-joins of a single tuple separately
164 continue;
165 }
166 final Tuple u1 = unify(opposite, updateElement);
167 final Tuple u2 = unify(updateElement, opposite);
168 for (final Signed<Timestamp> oppositeSigned : opposites.get(opposite).asChangeSequence()) {
169 final Direction updateDirection = direction.multiply(oppositeSigned.getDirection());
170 final Timestamp updateTimestamp = timestamp.max(oppositeSigned.getPayload());
171 propagateUpdate(updateDirection, u1, updateTimestamp);
172 propagateUpdate(updateDirection, u2, updateTimestamp);
173 }
174 }
175 }
176
177 // handle self-join here
178 propagateUpdate(direction, unify(updateElement, updateElement), timestamp);
179 }
180 }
181 };
182
183 @Override
184 protected NetworkStructureChangeSensitiveLogic createTimelessLogic() {
185 return this.TIMELESS;
186 }
187
188 @Override
189 protected NetworkStructureChangeSensitiveLogic createTimelyLogic() {
190 return this.TIMELY;
191 }
192
193}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryIdentityIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryIdentityIndexer.java
new file mode 100644
index 00000000..59b75c33
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryIdentityIndexer.java
@@ -0,0 +1,55 @@
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.rete.index;
11
12import java.util.Collection;
13import java.util.List;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.rete.network.Receiver;
17import tools.refinery.viatra.runtime.rete.network.ReteContainer;
18import tools.refinery.viatra.runtime.rete.network.Supplier;
19
20/**
21 * Defines a trivial indexer that identically projects the contents of a memory-equipped node, and can therefore save
22 * space. Can only exist in connection with a memory, and must be operated by another node. Do not attach parents
23 * directly!
24 *
25 * @noimplement Rely on the provided implementations
26 * @noreference Use only via standard Node and Indexer interfaces
27 * @noinstantiate This class is not intended to be instantiated by clients.
28 * @author Gabor Bergmann
29 */
30
31public class MemoryIdentityIndexer extends IdentityIndexer {
32
33 protected final Collection<Tuple> memory;
34
35 /**
36 * @param reteContainer
37 * @param tupleWidth
38 * the width of the tuples of memoryNode
39 * @param memory
40 * the memory whose contents are to be identity-indexed
41 * @param parent
42 * the parent node that owns the memory
43 */
44 public MemoryIdentityIndexer(ReteContainer reteContainer, int tupleWidth, Collection<Tuple> memory,
45 Supplier parent, Receiver activeNode, List<ListenerSubscription> sharedSubscriptionList) {
46 super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList);
47 this.memory = memory;
48 }
49
50 @Override
51 protected Collection<Tuple> getTuples() {
52 return this.memory;
53 }
54
55}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryNullIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryNullIndexer.java
new file mode 100644
index 00000000..204fe433
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryNullIndexer.java
@@ -0,0 +1,54 @@
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.rete.index;
11
12import java.util.Collection;
13import java.util.List;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.rete.network.Receiver;
17import tools.refinery.viatra.runtime.rete.network.ReteContainer;
18import tools.refinery.viatra.runtime.rete.network.Supplier;
19
20/**
21 * Defines a trivial indexer that projects the contents of a memory-equipped node to the empty tuple, and can therefore
22 * save space. Can only exist in connection with a memory, and must be operated by another node. Do not attach parents
23 * directly!
24 *
25 * @author Gabor Bergmann
26 * @noimplement Rely on the provided implementations
27 * @noreference Use only via standard Node and Indexer interfaces
28 * @noinstantiate This class is not intended to be instantiated by clients.
29 */
30public class MemoryNullIndexer extends NullIndexer {
31
32 Collection<Tuple> memory;
33
34 /**
35 * @param reteContainer
36 * @param tupleWidth
37 * the width of the tuples of memoryNode
38 * @param memory
39 * the memory whose contents are to be null-indexed
40 * @param parent
41 * the parent node that owns the memory
42 */
43 public MemoryNullIndexer(ReteContainer reteContainer, int tupleWidth, Collection<Tuple> memory,
44 Supplier parent, Receiver activeNode, List<ListenerSubscription> sharedSubscriptionList) {
45 super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList);
46 this.memory = memory;
47 }
48
49 @Override
50 protected Collection<Tuple> getTuples() {
51 return this.memory;
52 }
53
54}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/NullIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/NullIndexer.java
new file mode 100644
index 00000000..a875b29f
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/NullIndexer.java
@@ -0,0 +1,88 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2012 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.rete.index;
11
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Iterator;
15import java.util.List;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
20import tools.refinery.viatra.runtime.matchers.util.Direction;
21import tools.refinery.viatra.runtime.rete.network.Node;
22import tools.refinery.viatra.runtime.rete.network.ReteContainer;
23import tools.refinery.viatra.runtime.rete.network.Supplier;
24import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
25
26/**
27 * Defines an abstract trivial indexer that projects the contents of some stateful node to the empty tuple, and can
28 * therefore save space. Can only exist in connection with a stateful store, and must be operated by another node (the
29 * active node). Do not attach parents directly!
30 *
31 * @author Gabor Bergmann
32 * @noimplement Rely on the provided implementations
33 * @noreference Use only via standard Node and Indexer interfaces
34 * @noinstantiate This class is not intended to be instantiated by clients.
35 */
36public abstract class NullIndexer extends SpecializedProjectionIndexer {
37
38 protected abstract Collection<Tuple> getTuples();
39
40 protected static final Tuple nullSignature = Tuples.staticArityFlatTupleOf();
41 protected static final Collection<Tuple> nullSingleton = Collections.singleton(nullSignature);
42 protected static final Collection<Tuple> emptySet = Collections.emptySet();
43
44 public NullIndexer(ReteContainer reteContainer, int tupleWidth, Supplier parent, Node activeNode,
45 List<ListenerSubscription> sharedSubscriptionList) {
46 super(reteContainer, TupleMask.linear(0, tupleWidth), parent, activeNode, sharedSubscriptionList);
47 }
48
49 @Override
50 public Collection<Tuple> get(Tuple signature) {
51 if (nullSignature.equals(signature))
52 return isEmpty() ? null : getTuples();
53 else
54 return null;
55 }
56
57 @Override
58 public Collection<Tuple> getSignatures() {
59 return isEmpty() ? emptySet : nullSingleton;
60 }
61
62 protected boolean isEmpty() {
63 return getTuples().isEmpty();
64 }
65
66 protected boolean isSingleElement() {
67 return getTuples().size() == 1;
68 }
69
70 @Override
71 public Iterator<Tuple> iterator() {
72 return getTuples().iterator();
73 }
74
75 @Override
76 public int getBucketCount() {
77 return getTuples().isEmpty() ? 0 : 1;
78 }
79
80 @Override
81 public void propagateToListener(IndexerListener listener, Direction direction, Tuple updateElement,
82 Timestamp timestamp) {
83 boolean radical = (direction == Direction.DELETE && isEmpty())
84 || (direction == Direction.INSERT && isSingleElement());
85 listener.notifyIndexerUpdate(direction, updateElement, nullSignature, radical, timestamp);
86 }
87
88} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/OnetimeIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/OnetimeIndexer.java
new file mode 100644
index 00000000..fef84bb1
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/OnetimeIndexer.java
@@ -0,0 +1,47 @@
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.rete.index;
11
12import java.util.Collection;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
16import tools.refinery.viatra.runtime.rete.network.ReteContainer;
17import tools.refinery.viatra.runtime.rete.network.Supplier;
18
19/**
20 * @author Gabor Bergmann Indexer whose lifetime last until the first get() DO NOT connect to nodes!
21 */
22public class OnetimeIndexer extends GenericProjectionIndexer {
23
24 public OnetimeIndexer(ReteContainer reteContainer, TupleMask mask) {
25 super(reteContainer, mask);
26 }
27
28 @Override
29 public Collection<Tuple> get(Tuple signature) {
30 if (tools.refinery.viatra.runtime.rete.util.Options.releaseOnetimeIndexers) {
31 reteContainer.unregisterClearable(memory);
32 reteContainer.unregisterNode(this);
33 }
34 return super.get(signature);
35 }
36
37 @Override
38 public void appendParent(Supplier supplier) {
39 throw new UnsupportedOperationException("onetime indexer cannot have parents");
40 }
41
42 @Override
43 public void attachListener(IndexerListener listener) {
44 throw new UnsupportedOperationException("onetime indexer cannot have listeners");
45 }
46
47}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ProjectionIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ProjectionIndexer.java
new file mode 100644
index 00000000..58e593d9
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ProjectionIndexer.java
@@ -0,0 +1,21 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2009 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.rete.index;
11
12/**
13 * An iterable indexer that receives updates from a node, and groups received tuples intact, i.e. it does not reduce
14 * tuple groups.
15 *
16 * @author Gabor Bergmann
17 *
18 */
19public interface ProjectionIndexer extends IterableIndexer {
20
21}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/SpecializedProjectionIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/SpecializedProjectionIndexer.java
new file mode 100644
index 00000000..9c647aa9
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/SpecializedProjectionIndexer.java
@@ -0,0 +1,176 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2012 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.rete.index;
11
12import java.util.ArrayList;
13import java.util.List;
14import java.util.Objects;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19import tools.refinery.viatra.runtime.rete.network.Node;
20import tools.refinery.viatra.runtime.rete.network.ReteContainer;
21import tools.refinery.viatra.runtime.rete.network.Supplier;
22import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
23import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
24
25/**
26 * A specialized projection indexer that can be memory-less (relying on an external source of information).
27 *
28 * <p>
29 * All specialized projection indexers of a single node will share the same listener list, so that notification order is
30 * maintained (see Bug 518434).
31 *
32 * @author Gabor Bergmann
33 * @noimplement Rely on the provided implementations
34 * @noreference Use only via standard Node and Indexer interfaces
35 * @noinstantiate This class is not intended to be instantiated by clients.
36 */
37public abstract class SpecializedProjectionIndexer extends StandardIndexer implements ProjectionIndexer {
38
39 protected Node activeNode;
40 protected List<ListenerSubscription> subscriptions;
41
42 /**
43 * @since 1.7
44 */
45 public SpecializedProjectionIndexer(final ReteContainer reteContainer, final TupleMask mask, final Supplier parent,
46 final Node activeNode, final List<ListenerSubscription> subscriptions) {
47 super(reteContainer, mask);
48 this.parent = parent;
49 this.activeNode = activeNode;
50 this.subscriptions = subscriptions;
51 }
52
53 public List<ListenerSubscription> getSubscriptions() {
54 return subscriptions;
55 }
56
57 @Override
58 public Node getActiveNode() {
59 return activeNode;
60 }
61
62 @Override
63 protected void propagate(final Direction direction, final Tuple updateElement, final Tuple signature,
64 final boolean change, final Timestamp timestamp) {
65 throw new UnsupportedOperationException();
66 }
67
68 @Override
69 public void attachListener(final IndexerListener listener) {
70 super.attachListener(listener);
71 final CommunicationTracker tracker = this.getCommunicationTracker();
72 final IndexerListener proxy = tracker.proxifyIndexerListener(this, listener);
73 final ListenerSubscription subscription = new ListenerSubscription(this, proxy);
74 tracker.registerDependency(this, proxy.getOwner());
75 // See Bug 518434
76 // Must add to the first position, so that the later listeners are notified earlier.
77 // Thus if the beta node added as listener is also an indirect descendant of the same indexer on its opposite
78 // slot,
79 // then the beta node is connected later than its ancestor's listener, therefore it will be notified earlier,
80 // eliminating duplicate insertions and lost deletions that would result from fall-through update propagation
81 subscriptions.add(0, subscription);
82 }
83
84 @Override
85 public void detachListener(final IndexerListener listener) {
86 final CommunicationTracker tracker = this.getCommunicationTracker();
87 // obtain the proxy before the super call would unregister the dependency
88 final IndexerListener proxy = tracker.proxifyIndexerListener(this, listener);
89 super.detachListener(listener);
90 final ListenerSubscription subscription = new ListenerSubscription(this, proxy);
91 final boolean wasContained = subscriptions.remove(subscription);
92 assert wasContained;
93 tracker.unregisterDependency(this, proxy.getOwner());
94 }
95
96 @Override
97 public void networkStructureChanged() {
98 super.networkStructureChanged();
99 final List<ListenerSubscription> oldSubscriptions = new ArrayList<ListenerSubscription>();
100 oldSubscriptions.addAll(subscriptions);
101 subscriptions.clear();
102 for (final ListenerSubscription oldSubscription : oldSubscriptions) {
103 // there is no need to unregister and re-register the dependency between indexer and listener
104 // because the owner of the listener is the same (even if it is proxified)
105 final CommunicationTracker tracker = this.getCommunicationTracker();
106 // the subscriptions are shared, so we MUST reuse the indexer of the subscription instead of simply 'this'
107 final IndexerListener proxy = tracker.proxifyIndexerListener(oldSubscription.indexer, oldSubscription.listener);
108 final ListenerSubscription newSubscription = new ListenerSubscription(oldSubscription.indexer, proxy);
109 subscriptions.add(newSubscription);
110 }
111 }
112
113 /**
114 * @since 2.4
115 */
116 public abstract void propagateToListener(IndexerListener listener, Direction direction, Tuple updateElement,
117 Timestamp timestamp);
118
119 /**
120 * Infrastructure to share subscriptions between specialized indexers of the same parent node.
121 *
122 * @author Gabor Bergmann
123 * @since 1.7
124 */
125 public static class ListenerSubscription {
126 protected SpecializedProjectionIndexer indexer;
127 protected IndexerListener listener;
128
129 public ListenerSubscription(SpecializedProjectionIndexer indexer, IndexerListener listener) {
130 super();
131 this.indexer = indexer;
132 this.listener = listener;
133 }
134
135 /**
136 * @since 2.4
137 */
138 public SpecializedProjectionIndexer getIndexer() {
139 return indexer;
140 }
141
142 /**
143 * @since 2.4
144 */
145 public IndexerListener getListener() {
146 return listener;
147 }
148
149 /**
150 * Call this from parent node.
151 * @since 2.4
152 */
153 public void propagate(Direction direction, Tuple updateElement, Timestamp timestamp) {
154 indexer.propagateToListener(listener, direction, updateElement, timestamp);
155 }
156
157 @Override
158 public int hashCode() {
159 return Objects.hash(indexer, listener);
160 }
161
162 @Override
163 public boolean equals(Object obj) {
164 if (this == obj)
165 return true;
166 if (obj == null)
167 return false;
168 if (getClass() != obj.getClass())
169 return false;
170 ListenerSubscription other = (ListenerSubscription) obj;
171 return Objects.equals(listener, other.listener) && Objects.equals(indexer, other.indexer);
172 }
173
174 }
175
176}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/StandardIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/StandardIndexer.java
new file mode 100644
index 00000000..9847a8dd
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/StandardIndexer.java
@@ -0,0 +1,127 @@
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.rete.index;
11
12import java.util.Collection;
13import java.util.List;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19import tools.refinery.viatra.runtime.rete.network.BaseNode;
20import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.Supplier;
23import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
24import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
25
26/**
27 * An abstract standard implementation of the Indexer interface, providing common bookkeeping functionality.
28 *
29 * @author Gabor Bergmann
30 *
31 */
32public abstract class StandardIndexer extends BaseNode implements Indexer, NetworkStructureChangeSensitiveNode {
33
34 protected Supplier parent;
35 private final List<IndexerListener> originalListeners;
36 private final List<IndexerListener> proxyListeners;
37 protected TupleMask mask;
38
39 public StandardIndexer(ReteContainer reteContainer, TupleMask mask) {
40 super(reteContainer);
41 this.parent = null;
42 this.mask = mask;
43 this.originalListeners = CollectionsFactory.createObserverList();
44 this.proxyListeners = CollectionsFactory.createObserverList();
45 }
46
47 /**
48 * @since 2.4
49 */
50 protected void propagate(Direction direction, Tuple updateElement, Tuple signature, boolean change, Timestamp timestamp) {
51 for (IndexerListener listener : proxyListeners) {
52 listener.notifyIndexerUpdate(direction, updateElement, signature, change, timestamp);
53 }
54 }
55
56 @Override
57 public TupleMask getMask() {
58 return mask;
59 }
60
61 @Override
62 public Supplier getParent() {
63 return parent;
64 }
65
66 @Override
67 public void attachListener(IndexerListener listener) {
68 this.getCommunicationTracker().registerDependency(this, listener.getOwner());
69 // obtain the proxy after registering the dependency because then the proxy reflects the new SCC structure
70 final IndexerListener proxy = this.getCommunicationTracker().proxifyIndexerListener(this, listener);
71 // See Bug 518434
72 // Must add to the first position, so that the later listeners are notified earlier.
73 // Thus if the beta node added as listener is also an indirect descendant of the same indexer on its opposite slot,
74 // then the beta node is connected later than its ancestor's listener, therefore it will be notified earlier,
75 // eliminating duplicate insertions and lost deletions that would result from fall-through update propagation
76 this.originalListeners.add(0, listener);
77 this.proxyListeners.add(0, proxy);
78 }
79
80 @Override
81 public void detachListener(IndexerListener listener) {
82 this.originalListeners.remove(listener);
83 IndexerListener listenerToRemove = null;
84 for (final IndexerListener proxyListener : this.proxyListeners) {
85 if (proxyListener.getOwner() == listener.getOwner()) {
86 listenerToRemove = proxyListener;
87 break;
88 }
89 }
90 assert listenerToRemove != null;
91 this.proxyListeners.remove(listenerToRemove);
92 this.getCommunicationTracker().unregisterDependency(this, listener.getOwner());
93 }
94
95 @Override
96 public void networkStructureChanged() {
97 this.proxyListeners.clear();
98 for (final IndexerListener original : this.originalListeners) {
99 this.proxyListeners.add(this.getCommunicationTracker().proxifyIndexerListener(this, original));
100 }
101 }
102
103 @Override
104 public Collection<IndexerListener> getListeners() {
105 return proxyListeners;
106 }
107
108 @Override
109 public ReteContainer getContainer() {
110 return reteContainer;
111 }
112
113 @Override
114 protected String toStringCore() {
115 return super.toStringCore() + "(" + parent + "/" + mask + ")";
116 }
117
118 @Override
119 public void assignTraceInfo(TraceInfo traceInfo) {
120 super.assignTraceInfo(traceInfo);
121 if (traceInfo.propagateFromIndexerToSupplierParent())
122 if (parent != null)
123 parent.acceptPropagatedTraceInfo(traceInfo);
124 }
125
126
127}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java
new file mode 100644
index 00000000..b0bea8cb
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java
@@ -0,0 +1,121 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.index;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Iterator;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.base.itc.alg.incscc.IncSCCAlg;
17import tools.refinery.viatra.runtime.matchers.tuple.MaskedTuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
21import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
22import tools.refinery.viatra.runtime.matchers.util.Direction;
23import tools.refinery.viatra.runtime.rete.network.Receiver;
24import tools.refinery.viatra.runtime.rete.single.TransitiveClosureNode;
25
26// UNFINISHED, not used yet
27public class TransitiveClosureNodeIndexer extends StandardIndexer implements IterableIndexer {
28 private TransitiveClosureNode tcNode;
29 private IncSCCAlg<Object> tcAlg;
30 private Collection<Tuple> emptySet;
31
32 public TransitiveClosureNodeIndexer(TupleMask mask, IncSCCAlg<Object> tcAlg, TransitiveClosureNode tcNode) {
33 super(tcNode.getContainer(), mask);
34 this.tcAlg = tcAlg;
35 this.tcNode = tcNode;
36 this.emptySet = Collections.emptySet();
37 this.parent = tcNode;
38 }
39
40 @Override
41 public Collection<Tuple> get(Tuple signature) {
42 if (signature.getSize() == mask.sourceWidth) {
43 if (mask.indices.length == 0) {
44 // mask ()/2
45 return getSignatures();
46 } else if (mask.indices.length == 1) {
47 Set<Tuple> retSet = CollectionsFactory.createSet();
48
49 // mask (0)/2
50 if (mask.indices[0] == 0) {
51 Object source = signature.get(0);
52 for (Object target : tcAlg.getAllReachableTargets(source)) {
53 retSet.add(Tuples.staticArityFlatTupleOf(source, target));
54 }
55 return retSet;
56 }
57 // mask (1)/2
58 if (mask.indices[0] == 1) {
59 Object target = signature.get(1);
60 for (Object source : tcAlg.getAllReachableSources(target)) {
61 retSet.add(Tuples.staticArityFlatTupleOf(source, target));
62 }
63 return retSet;
64 }
65 } else {
66 // mask (0,1)/2
67 if (mask.indices[0] == 0 && mask.indices[1] == 1) {
68 Object source = signature.get(0);
69 Object target = signature.get(1);
70 Tuple singleton = Tuples.staticArityFlatTupleOf(source, target);
71 return (tcAlg.isReachable(source, target) ? Collections.singleton(singleton) : emptySet);
72 }
73 // mask (1,0)/2
74 if (mask.indices[0] == 1 && mask.indices[1] == 0) {
75 Object source = signature.get(1);
76 Object target = signature.get(0);
77 Tuple singleton = Tuples.staticArityFlatTupleOf(source, target);
78 return (tcAlg.isReachable(source, target) ? Collections.singleton(singleton) : emptySet);
79 }
80 }
81 }
82 return null;
83 }
84
85 @Override
86 public int getBucketCount() {
87 throw new UnsupportedOperationException();
88 }
89
90 @Override
91 public Collection<Tuple> getSignatures() {
92 return asTupleCollection(tcAlg.getTcRelation());
93 }
94
95 @Override
96 public Iterator<Tuple> iterator() {
97 return asTupleCollection(tcAlg.getTcRelation()).iterator();
98 }
99
100 private Collection<Tuple> asTupleCollection(
101 Collection<tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple<Object>> tuples) {
102 Set<Tuple> retSet = CollectionsFactory.createSet();
103 for (tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple<Object> tuple : tuples) {
104 retSet.add(Tuples.staticArityFlatTupleOf(tuple.getSource(), tuple.getTarget()));
105 }
106 return retSet;
107 }
108
109 /**
110 * @since 2.4
111 */
112 public void propagate(Direction direction, Tuple updateElement, boolean change) {
113 propagate(direction, updateElement, new MaskedTuple(updateElement, mask), change, null);
114 }
115
116 @Override
117 public Receiver getActiveNode() {
118 return tcNode;
119 }
120
121}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryIdentityIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryIdentityIndexer.java
new file mode 100644
index 00000000..4319ee29
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryIdentityIndexer.java
@@ -0,0 +1,51 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.index.timely;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.List;
14import java.util.Map;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.TimelyMemory;
18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import tools.refinery.viatra.runtime.rete.index.IdentityIndexer;
20import tools.refinery.viatra.runtime.rete.network.Receiver;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.Supplier;
23import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
24
25public class TimelyMemoryIdentityIndexer extends IdentityIndexer {
26
27 protected final TimelyMemory<Timestamp> memory;
28
29 public TimelyMemoryIdentityIndexer(final ReteContainer reteContainer, final int tupleWidth,
30 final TimelyMemory<Timestamp> memory, final Supplier parent, final Receiver activeNode,
31 final List<ListenerSubscription> sharedSubscriptionList) {
32 super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList);
33 this.memory = memory;
34 }
35
36 @Override
37 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
38 final Timeline<Timestamp> timestamp = this.memory.get(signature);
39 if (timestamp != null) {
40 return Collections.singletonMap(signature, timestamp);
41 } else {
42 return null;
43 }
44 }
45
46 @Override
47 protected Collection<Tuple> getTuples() {
48 return this.memory.getTuplesAtInfinity();
49 }
50
51}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryNullIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryNullIndexer.java
new file mode 100644
index 00000000..0386b006
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryNullIndexer.java
@@ -0,0 +1,49 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.index.timely;
10
11import java.util.Collection;
12import java.util.List;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.TimelyMemory;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.index.NullIndexer;
19import tools.refinery.viatra.runtime.rete.network.Receiver;
20import tools.refinery.viatra.runtime.rete.network.ReteContainer;
21import tools.refinery.viatra.runtime.rete.network.Supplier;
22import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
23
24public class TimelyMemoryNullIndexer extends NullIndexer {
25
26 protected final TimelyMemory<Timestamp> memory;
27
28 public TimelyMemoryNullIndexer(final ReteContainer reteContainer, final int tupleWidth,
29 final TimelyMemory<Timestamp> memory, final Supplier parent,
30 final Receiver activeNode, final List<ListenerSubscription> sharedSubscriptionList) {
31 super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList);
32 this.memory = memory;
33 }
34
35 @Override
36 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
37 if (nullSignature.equals(signature)) {
38 return isEmpty() ? null : this.memory.asMap();
39 } else {
40 return null;
41 }
42 }
43
44 @Override
45 protected Collection<Tuple> getTuples() {
46 return this.memory.getTuplesAtInfinity();
47 }
48
49}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/DRedReteBackendFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/DRedReteBackendFactory.java
new file mode 100644
index 00000000..83e86fe6
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/DRedReteBackendFactory.java
@@ -0,0 +1,49 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.matcher;
10
11import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
12import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
13
14/**
15 * A {@link ReteBackendFactory} implementation that creates {@link ReteEngine}s that use delete and re-derive
16 * evaluation.
17 *
18 * @author Tamas Szabo
19 * @since 2.2
20 */
21public class DRedReteBackendFactory extends ReteBackendFactory {
22
23 public static final DRedReteBackendFactory INSTANCE = new DRedReteBackendFactory();
24
25 @Override
26 public IQueryBackend create(IQueryBackendContext context) {
27 return create(context, true, null);
28 }
29
30 @Override
31 public int hashCode() {
32 return DRedReteBackendFactory.class.hashCode();
33 }
34
35 @Override
36 public boolean equals(final Object obj) {
37 if (this == obj) {
38 return true;
39 }
40 if (obj == null) {
41 return false;
42 }
43 if (!(obj instanceof DRedReteBackendFactory)) {
44 return false;
45 }
46 return true;
47 }
48
49}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/HintConfigurator.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/HintConfigurator.java
new file mode 100644
index 00000000..a4fa4914
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/HintConfigurator.java
@@ -0,0 +1,46 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.rete.matcher;
10
11import java.util.HashMap;
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
15import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
16import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
17
18/**
19 * A configurable hint provider that gathers hints for queries during runtime, and delegates defaults to an external hint provider.
20 *
21 * @author Gabor Bergmann
22 * @since 1.5
23 */
24class HintConfigurator implements IQueryBackendHintProvider {
25
26 private IQueryBackendHintProvider defaultHintProvider;
27 private Map<PQuery, QueryEvaluationHint> storedHints = new HashMap<PQuery, QueryEvaluationHint>();
28
29 public HintConfigurator(IQueryBackendHintProvider defaultHintProvider) {
30 this.defaultHintProvider = defaultHintProvider;
31 }
32
33 @Override
34 public QueryEvaluationHint getQueryEvaluationHint(PQuery query) {
35 return defaultHintProvider.getQueryEvaluationHint(query).overrideBy(storedHints.get(query));
36 }
37
38 public void storeHint(PQuery query, QueryEvaluationHint hint) {
39 QueryEvaluationHint oldHint = storedHints.get(query);
40 if (oldHint == null)
41 storedHints.put(query, hint);
42 else
43 storedHints.put(query, oldHint.overrideBy(hint));
44 }
45
46}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/IncrementalMatcherCapability.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/IncrementalMatcherCapability.java
new file mode 100644
index 00000000..4c64a1a1
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/IncrementalMatcherCapability.java
@@ -0,0 +1,30 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, 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.rete.matcher;
10
11import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
12
13/**
14 * @author Grill Balázs
15 * @since 1.4
16 *
17 */
18public class IncrementalMatcherCapability implements IMatcherCapability {
19
20 @Override
21 public boolean canBeSubstitute(IMatcherCapability capability) {
22 /*
23 * TODO: for now, as we are only prepared for Rete and LS, we can assume that
24 * a matcher created with Rete can always be a substitute for a matcher created
25 * by any backend.
26 */
27 return true;
28 }
29
30}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactory.java
new file mode 100644
index 00000000..347cabc4
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactory.java
@@ -0,0 +1,100 @@
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.rete.matcher;
10
11import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
12import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
13import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
14import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
15import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
16import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
17import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
18import tools.refinery.viatra.runtime.rete.construction.plancompiler.ReteRecipeCompiler;
19import tools.refinery.viatra.runtime.rete.util.Options;
20
21public class ReteBackendFactory implements IQueryBackendFactory {
22 /**
23 * EXPERIMENTAL
24 */
25 protected static final int reteThreads = 0;
26
27 /**
28 * @since 2.0
29 */
30 public static final ReteBackendFactory INSTANCE = new ReteBackendFactory();
31
32 /**
33 * @deprecated Use the static {@link #INSTANCE} field instead
34 */
35 @Deprecated
36 public ReteBackendFactory() {
37 }
38
39 /**
40 * @since 1.5
41 */
42 @Override
43 public IQueryBackend create(IQueryBackendContext context) {
44 return create(context, false, null);
45 }
46
47 /**
48 * @since 2.4
49 */
50 public IQueryBackend create(IQueryBackendContext context, boolean deleteAndRederiveEvaluation,
51 TimelyConfiguration timelyConfiguration) {
52 ReteEngine engine;
53 engine = new ReteEngine(context, reteThreads, deleteAndRederiveEvaluation, timelyConfiguration);
54 IQueryBackendHintProvider hintConfiguration = engine.getHintConfiguration();
55 ReteRecipeCompiler compiler = new ReteRecipeCompiler(
56 Options.builderMethod.layoutStrategy(context, hintConfiguration), context.getLogger(),
57 context.getRuntimeContext().getMetaContext(), context.getQueryCacheContext(), hintConfiguration,
58 context.getQueryAnalyzer(), deleteAndRederiveEvaluation, timelyConfiguration);
59 engine.setCompiler(compiler);
60 return engine;
61 }
62
63 @Override
64 public Class<? extends IQueryBackend> getBackendClass() {
65 return ReteEngine.class;
66 }
67
68 @Override
69 public int hashCode() {
70 return ReteBackendFactory.class.hashCode();
71 }
72
73 @Override
74 public boolean equals(Object obj) {
75 if (this == obj) {
76 return true;
77 }
78 if (obj == null) {
79 return false;
80 }
81 if (!(obj instanceof ReteBackendFactory)) {
82 return false;
83 }
84 return true;
85 }
86
87 /**
88 * @since 1.4
89 */
90 @Override
91 public IMatcherCapability calculateRequiredCapability(PQuery query, QueryEvaluationHint hint) {
92 return new IncrementalMatcherCapability();
93 }
94
95 @Override
96 public boolean isCaching() {
97 return true;
98 }
99
100} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactoryProvider.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactoryProvider.java
new file mode 100644
index 00000000..98775ab3
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactoryProvider.java
@@ -0,0 +1,35 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, 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.rete.matcher;
10
11import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
12import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactoryProvider;
13
14/**
15 * @since 2.0
16 *
17 */
18public class ReteBackendFactoryProvider implements IQueryBackendFactoryProvider {
19
20 @Override
21 public IQueryBackendFactory getFactory() {
22 return ReteBackendFactory.INSTANCE;
23 }
24
25 @Override
26 public boolean isSystemDefaultEngine() {
27 return true;
28 }
29
30 @Override
31 public boolean isSystemDefaultCachingBackend() {
32 return true;
33 }
34
35}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java
new file mode 100644
index 00000000..9bd499f4
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java
@@ -0,0 +1,579 @@
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.rete.matcher;
11
12import java.lang.reflect.InvocationTargetException;
13import java.util.Collection;
14import java.util.LinkedList;
15import java.util.Map;
16import java.util.concurrent.Callable;
17
18import org.apache.log4j.Logger;
19import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
20import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
21import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
22import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
23import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
24import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
25import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
26import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
27import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
28import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
29import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
30import tools.refinery.viatra.runtime.rete.boundary.Disconnectable;
31import tools.refinery.viatra.runtime.rete.boundary.ReteBoundary;
32import tools.refinery.viatra.runtime.rete.construction.RetePatternBuildException;
33import tools.refinery.viatra.runtime.rete.construction.plancompiler.ReteRecipeCompiler;
34import tools.refinery.viatra.runtime.rete.index.Indexer;
35import tools.refinery.viatra.runtime.rete.network.Network;
36import tools.refinery.viatra.runtime.rete.network.NodeProvisioner;
37import tools.refinery.viatra.runtime.rete.network.ReteContainer;
38import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
39
40/**
41 * @author Gabor Bergmann
42 *
43 */
44public class ReteEngine implements IQueryBackend {
45
46 protected Network reteNet;
47 protected final int reteThreads;
48 protected ReteBoundary boundary;
49
50 /**
51 * @since 2.2
52 */
53 protected final boolean deleteAndRederiveEvaluation;
54 /**
55 * @since 2.4
56 */
57 protected final TimelyConfiguration timelyConfiguration;
58
59 private IQueryBackendContext context;
60 private Logger logger;
61 protected IQueryRuntimeContext runtimeContext;
62
63 protected Collection<Disconnectable> disconnectables;
64
65 protected Map<PQuery, RetePatternMatcher> matchers;
66
67 protected ReteRecipeCompiler compiler;
68
69 protected final boolean parallelExecutionEnabled; // TRUE if model manipulation can go on
70
71 private boolean disposedOrUninitialized = true;
72
73 private HintConfigurator hintConfigurator;
74
75 /**
76 * @param context
77 * the context of the pattern matcher, conveying all information from the outside world.
78 * @param reteThreads
79 * the number of threads to operate the RETE network with; 0 means single-threaded operation, 1 starts an
80 * asynchronous thread to operate the RETE net, >1 uses multiple RETE containers.
81 */
82 public ReteEngine(IQueryBackendContext context, int reteThreads) {
83 this(context, reteThreads, false, null);
84 }
85
86 /**
87 * @since 2.4
88 */
89 public ReteEngine(IQueryBackendContext context, int reteThreads, boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyConfiguration) {
90 super();
91 this.context = context;
92 this.logger = context.getLogger();
93 this.runtimeContext = context.getRuntimeContext();
94 this.reteThreads = reteThreads;
95 this.parallelExecutionEnabled = reteThreads > 0;
96 this.deleteAndRederiveEvaluation = deleteAndRederiveEvaluation;
97 this.timelyConfiguration = timelyConfiguration;
98 initEngine();
99 this.compiler = null;
100 }
101
102 /**
103 * @since 1.6
104 */
105 public IQueryBackendContext getBackendContext() {
106 return context;
107 }
108
109 /**
110 * @since 2.2
111 */
112 public boolean isDeleteAndRederiveEvaluation() {
113 return this.deleteAndRederiveEvaluation;
114 }
115
116 /**
117 * @since 2.4
118 */
119 public TimelyConfiguration getTimelyConfiguration() {
120 return this.timelyConfiguration;
121 }
122
123 /**
124 * initializes engine components
125 */
126 private synchronized void initEngine() {
127 this.disposedOrUninitialized = false;
128 this.disconnectables = new LinkedList<Disconnectable>();
129 // this.caughtExceptions = new LinkedBlockingQueue<Throwable>();
130
131
132 this.hintConfigurator = new HintConfigurator(context.getHintProvider());
133
134 this.reteNet = new Network(reteThreads, this);
135 this.boundary = new ReteBoundary(this); // prerequisite: network
136
137 this.matchers = CollectionsFactory.createMap();
138 /* this.matchersScoped = new HashMap<PatternDescription, Map<Map<Integer,Scope>,RetePatternMatcher>>(); */
139
140 // prerequisite: network, framework, boundary, disconnectables
141 //context.subscribeBackendForUpdates(this.boundary);
142 // prerequisite: boundary, disconnectables
143// this.traceListener = context.subscribePatternMatcherForTraceInfluences(this);
144
145 }
146
147 @Override
148 public void flushUpdates() {
149 for (ReteContainer container : this.reteNet.getContainers()) {
150 container.deliverMessagesSingleThreaded();
151 }
152 }
153
154 /**
155 * deconstructs engine components
156 */
157 private synchronized void deconstructEngine() {
158 ensureInitialized();
159 reteNet.kill();
160
161 //context.unSubscribeBackendFromUpdates(this.boundary);
162 for (Disconnectable disc : disconnectables) {
163 disc.disconnect();
164 }
165
166 this.matchers = null;
167 this.disconnectables = null;
168
169 this.reteNet = null;
170 this.boundary = null;
171
172 this.hintConfigurator = null;
173
174 // this.machineListener = new MachineListener(this); // prerequisite:
175 // framework, disconnectables
176// this.traceListener = null;
177
178 this.disposedOrUninitialized = true;
179 }
180
181 /**
182 * Deconstructs the engine to get rid of it finally
183 */
184 public void killEngine() {
185 deconstructEngine();
186 // this.framework = null;
187 this.compiler = null;
188 this.logger = null;
189 }
190
191 /**
192 * Resets the engine to an after-initialization phase
193 *
194 */
195 public void reset() {
196 deconstructEngine();
197
198 initEngine();
199
200 compiler.reset();
201 }
202
203 /**
204 * Accesses the patternmatcher for a given pattern, constructs one if a matcher is not available yet.
205 *
206 * @pre: builder is set.
207 * @param query
208 * the pattern to be matched.
209 * @return a patternmatcher object that can match occurences of the given pattern.
210 * @throws ViatraQueryRuntimeException
211 * if construction fails.
212 */
213 public synchronized RetePatternMatcher accessMatcher(final PQuery query) {
214 ensureInitialized();
215 RetePatternMatcher matcher;
216 // String namespace = gtPattern.getNamespace().getName();
217 // String name = gtPattern.getName();
218 // String fqn = namespace + "." + name;
219 matcher = matchers.get(query);
220 if (matcher == null) {
221 constructionWrapper(() -> {
222 RecipeTraceInfo prodNode;
223 prodNode = boundary.accessProductionTrace(query);
224
225 RetePatternMatcher retePatternMatcher = new RetePatternMatcher(ReteEngine.this,
226 prodNode);
227 retePatternMatcher.setTag(query);
228 matchers.put(query, retePatternMatcher);
229 return null;
230 });
231 matcher = matchers.get(query);
232 }
233
234 executeDelayedCommands();
235
236 return matcher;
237 }
238
239
240 /**
241 * Constructs RETE pattern matchers for a collection of patterns, if they are not available yet. Model traversal
242 * during the whole construction period is coalesced (which may have an effect on performance, depending on the
243 * matcher context).
244 *
245 * @pre: builder is set.
246 * @param specifications
247 * the patterns to be matched.
248 * @throws ViatraQueryRuntimeException
249 * if construction fails.
250 */
251 public synchronized void buildMatchersCoalesced(final Collection<PQuery> specifications) {
252 ensureInitialized();
253 constructionWrapper(() -> {
254 for (PQuery specification : specifications) {
255 boundary.accessProductionNode(specification);
256 }
257 return null;
258 });
259 }
260
261 /**
262 * @since 2.4
263 */
264 public <T> T constructionWrapper(final Callable<T> payload) {
265 T result = null;
266// context.modelReadLock();
267// try {
268 if (parallelExecutionEnabled)
269 reteNet.getStructuralChangeLock().lock();
270 try {
271 try {
272 result = runtimeContext.coalesceTraversals(() -> {
273 T innerResult = payload.call();
274 this.executeDelayedCommands();
275 return innerResult;
276 });
277 } catch (InvocationTargetException ex) {
278 final Throwable cause = ex.getCause();
279 if (cause instanceof RetePatternBuildException)
280 throw (RetePatternBuildException) cause;
281 if (cause instanceof RuntimeException)
282 throw (RuntimeException) cause;
283 assert (false);
284 }
285 } finally {
286 if (parallelExecutionEnabled)
287 reteNet.getStructuralChangeLock().unlock();
288 reteNet.waitForReteTermination();
289 }
290// } finally {
291// context.modelReadUnLock();
292// }
293 return result;
294 }
295
296 // /**
297 // * Accesses the patternmatcher for a given pattern with additional scoping, constructs one if
298 // * a matcher is not available yet.
299 // *
300 // * @param gtPattern
301 // * the pattern to be matched.
302 // * @param additionalScopeMap
303 // * additional, optional scopes for the symbolic parameters
304 // * maps the position of the symbolic parameter to its additional scope (if any)
305 // * @pre: scope.parent is non-root, i.e. this is a nontrivial constraint
306 // * use the static method RetePatternMatcher.buildAdditionalScopeMap() to create from PatternCallSignature
307 // * @return a patternmatcher object that can match occurences of the given
308 // * pattern.
309 // * @throws PatternMatcherCompileTimeException
310 // * if construction fails.
311 // */
312 // public synchronized RetePatternMatcher accessMatcherScoped(PatternDescription gtPattern, Map<Integer, Scope>
313 // additionalScopeMap)
314 // throws PatternMatcherCompileTimeException {
315 // if (additionalScopeMap.isEmpty()) return accessMatcher(gtPattern);
316 //
317 // RetePatternMatcher matcher;
318 //
319 // Map<Map<Integer, Scope>, RetePatternMatcher> scopes = matchersScoped.get(gtPattern);
320 // if (scopes == null) {
321 // scopes = new HashMap<Map<Integer, Scope>, RetePatternMatcher>();
322 // matchersScoped.put(gtPattern, scopes);
323 // }
324 //
325 // matcher = scopes.get(additionalScopeMap);
326 // if (matcher == null) {
327 // context.modelReadLock();
328 // try {
329 // reteNet.getStructuralChangeLock().lock();
330 // try {
331 // Address<? extends Production> prodNode;
332 // prodNode = boundary.accessProductionScoped(gtPattern, additionalScopeMap);
333 //
334 // matcher = new RetePatternMatcher(this, prodNode);
335 // scopes.put(additionalScopeMap, matcher);
336 // } finally {
337 // reteNet.getStructuralChangeLock().unlock();
338 // }
339 // } finally {
340 // context.modelReadUnLock();
341 // }
342 // // reteNet.flushUpdates();
343 // }
344 //
345 // return matcher;
346 // }
347
348 /**
349 * Returns an indexer that groups the contents of this Production node by their projections to a given mask.
350 * Designed to be called by a RetePatternMatcher.
351 *
352 * @param production
353 * the production node to be indexed.
354 * @param mask
355 * the mask that defines the projection.
356 * @return the Indexer.
357 */
358 synchronized Indexer accessProjection(RecipeTraceInfo production, TupleMask mask) {
359 ensureInitialized();
360 NodeProvisioner nodeProvisioner = reteNet.getHeadContainer().getProvisioner();
361 Indexer result = nodeProvisioner.peekProjectionIndexer(production, mask);
362 if (result == null) {
363 result = constructionWrapper(() ->
364 nodeProvisioner.accessProjectionIndexerOnetime(production, mask)
365 );
366 }
367
368 return result;
369 }
370
371 // /**
372 // * Retrieves the patternmatcher for a given pattern fqn, returns null if
373 // the matching network hasn't been constructed yet.
374 // *
375 // * @param fqn the fully qualified name of the pattern to be matched.
376 // * @return the previously constructed patternmatcher object that can match
377 // occurences of the given pattern, or null if it doesn't exist.
378 // */
379 // public RetePatternMatcher getMatcher(String fqn)
380 // {
381 // RetePatternMatcher matcher = matchersByFqn.get(fqn);
382 // if (matcher == null)
383 // {
384 // Production prodNode = boundary.getProduction(fqn);
385 //
386 // matcher = new RetePatternMatcher(this, prodNode);
387 // matchersByFqn.put(fqn, matcher);
388 // }
389 //
390 // return matcher;
391 // }
392
393 /**
394 * @since 2.3
395 */
396 public void executeDelayedCommands() {
397 for (final ReteContainer container : this.reteNet.getContainers()) {
398 container.executeDelayedCommands();
399 }
400 }
401
402 /**
403 * Waits until the pattern matcher is in a steady state and output can be retrieved.
404 */
405 public void settle() {
406 ensureInitialized();
407 reteNet.waitForReteTermination();
408 }
409
410 /**
411 * Waits until the pattern matcher is in a steady state and output can be retrieved. When steady state is reached, a
412 * retrieval action is executed before the steady state ceases.
413 *
414 * @param action
415 * the action to be run when reaching the steady-state.
416 */
417 public void settle(Runnable action) {
418 ensureInitialized();
419 reteNet.waitForReteTermination(action);
420 }
421
422 // /**
423 // * @return the framework
424 // */
425 // public IFramework getFramework() {
426 // return framework.get();
427 // }
428
429 /**
430 * @return the reteNet
431 */
432 public Network getReteNet() {
433 ensureInitialized();
434 return reteNet;
435 }
436
437 /**
438 * @return the boundary
439 */
440 public ReteBoundary getBoundary() {
441 ensureInitialized();
442 return boundary;
443 }
444
445 // /**
446 // * @return the pattern matcher builder
447 // */
448 // public IRetePatternBuilder getBuilder() {
449 // return builder;
450 // }
451
452 /**
453 * @param builder
454 * the pattern matcher builder to set
455 */
456 public void setCompiler(ReteRecipeCompiler builder) {
457 ensureInitialized();
458 this.compiler = builder;
459 }
460
461// /**
462// * @return the manipulationListener
463// */
464// public IManipulationListener getManipulationListener() {
465// ensureInitialized();
466// return manipulationListener;
467// }
468
469// /**
470// * @return the traceListener
471// */
472// public IPredicateTraceListener geTraceListener() {
473// ensureInitialized();
474// return traceListener;
475// }
476
477 /**
478 * @param disc
479 * the new Disconnectable adapter.
480 */
481 public void addDisconnectable(Disconnectable disc) {
482 ensureInitialized();
483 disconnectables.add(disc);
484 }
485
486 /**
487 * @return the parallelExecutionEnabled
488 */
489 public boolean isParallelExecutionEnabled() {
490 return parallelExecutionEnabled;
491 }
492
493
494 public Logger getLogger() {
495 ensureInitialized();
496 return logger;
497 }
498
499 public IQueryRuntimeContext getRuntimeContext() {
500 ensureInitialized();
501 return runtimeContext;
502 }
503
504 public ReteRecipeCompiler getCompiler() {
505 ensureInitialized();
506 return compiler;
507 }
508
509 // /**
510 // * For internal use only: logs exceptions occurring during term evaluation inside the RETE net.
511 // * @param e
512 // */
513 // public void logEvaluatorException(Throwable e) {
514 // try {
515 // caughtExceptions.put(e);
516 // } catch (InterruptedException e1) {
517 // logEvaluatorException(e);
518 // }
519 // }
520 // /**
521 // * Polls the exceptions caught and logged during term evaluation by this RETE engine.
522 // * Recommended usage: iterate polling until null is returned.
523 // *
524 // * @return the next caught exception, or null if there are no more.
525 // */
526 // public Throwable getNextLoggedEvaluatorException() {
527 // return caughtExceptions.poll();
528 // }
529
530 void ensureInitialized() {
531 if (disposedOrUninitialized)
532 throw new IllegalStateException("Trying to use a Rete engine that has been disposed or has not yet been initialized.");
533
534 }
535
536 @Override
537 public IQueryResultProvider getResultProvider(PQuery query) {
538 return accessMatcher(query);
539 }
540
541 /**
542 * @since 1.4
543 */
544 @Override
545 public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint hints) {
546 hintConfigurator.storeHint(query, hints);
547 return accessMatcher(query);
548 }
549
550 @Override
551 public IQueryResultProvider peekExistingResultProvider(PQuery query) {
552 ensureInitialized();
553 return matchers.get(query);
554 }
555
556 @Override
557 public void dispose() {
558 killEngine();
559 }
560
561 @Override
562 public boolean isCaching() {
563 return true;
564 }
565
566 /**
567 * @since 1.5
568 * @noreference Internal API, subject to change
569 */
570 public IQueryBackendHintProvider getHintConfiguration() {
571 return hintConfigurator;
572 }
573
574 @Override
575 public IQueryBackendFactory getFactory() {
576 return ReteBackendFactory.INSTANCE;
577 }
578
579}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java
new file mode 100644
index 00000000..1f380b45
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java
@@ -0,0 +1,462 @@
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.rete.matcher;
11
12import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
13import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
14import tools.refinery.viatra.runtime.matchers.backend.IUpdateable;
15import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
16import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
20import tools.refinery.viatra.runtime.matchers.util.Accuracy;
21import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
22import tools.refinery.viatra.runtime.rete.index.Indexer;
23import tools.refinery.viatra.runtime.rete.index.IterableIndexer;
24import tools.refinery.viatra.runtime.rete.network.Node;
25import tools.refinery.viatra.runtime.rete.network.ProductionNode;
26import tools.refinery.viatra.runtime.rete.network.Receiver;
27import tools.refinery.viatra.runtime.rete.remote.Address;
28import tools.refinery.viatra.runtime.rete.single.CallbackNode;
29import tools.refinery.viatra.runtime.rete.single.TransformerNode;
30import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
31
32import java.util.Collection;
33import java.util.List;
34import java.util.Map;
35import java.util.Optional;
36import java.util.stream.Collectors;
37import java.util.stream.Stream;
38
39/**
40 * @author Gabor Bergmann
41 *
42 */
43public class RetePatternMatcher extends TransformerNode implements IQueryResultProvider {
44
45 protected ReteEngine engine;
46 protected IQueryRuntimeContext context;
47 protected ProductionNode productionNode;
48 protected RecipeTraceInfo productionNodeTrace;
49 protected Map<String, Integer> posMapping;
50 protected Map<Object, Receiver> taggedChildren = CollectionsFactory.createMap();
51 protected boolean connected = false; // is rete-wise connected to the
52 // production node?
53
54 /**
55 * @param productionNode
56 * a production node that matches this pattern without any parameter bindings
57 * @pre: Production must be local to the head container
58 */
59 public RetePatternMatcher(ReteEngine engine, RecipeTraceInfo productionNodeTrace) {
60 super(engine.getReteNet().getHeadContainer());
61 this.engine = engine;
62 this.context = engine.getRuntimeContext();
63 this.productionNodeTrace = productionNodeTrace;
64 final Address<? extends Node> productionAddress = reteContainer.getProvisioner()
65 .getOrCreateNodeByRecipe(productionNodeTrace);
66 if (!reteContainer.isLocal(productionAddress))
67 throw new IllegalArgumentException("@pre: Production must be local to the head container");
68 this.productionNode = (ProductionNode) reteContainer.resolveLocal(productionAddress);
69 this.posMapping = this.productionNode.getPosMapping();
70 this.reteContainer.getCommunicationTracker().registerDependency(this.productionNode, this);
71 }
72
73 /**
74 * @since 1.6
75 */
76 public ProductionNode getProductionNode() {
77 return productionNode;
78 }
79
80 public Tuple matchOneRandomly(Object[] inputMapping, boolean[] fixed) {
81 List<Tuple> allMatches = matchAll(inputMapping, fixed).collect(Collectors.toList());
82 if (allMatches == null || allMatches.isEmpty())
83 return null;
84 else
85 return allMatches.get((int) (Math.random() * allMatches.size()));
86 }
87
88 /**
89 * @since 2.0
90 */
91 public Stream<Tuple> matchAll(Object[] inputMapping, boolean[] fixed) {
92 // retrieving the projection
93 TupleMask mask = TupleMask.fromKeepIndicators(fixed);
94 Tuple inputSignature = mask.transform(Tuples.flatTupleOf(inputMapping));
95
96 return matchAll(mask, inputSignature);
97
98 }
99
100 /**
101 * @since 2.0
102 */
103 public Stream<Tuple> matchAll(TupleMask mask, ITuple inputSignature) {
104 AllMatchFetcher fetcher = new AllMatchFetcher(engine.accessProjection(productionNodeTrace, mask),
105 context.wrapTuple(inputSignature.toImmutable()));
106 engine.reteNet.waitForReteTermination(fetcher);
107 return fetcher.getMatches();
108
109 }
110
111 /**
112 * @since 2.0
113 */
114 public Optional<Tuple> matchOne(Object[] inputMapping, boolean[] fixed) {
115 // retrieving the projection
116 TupleMask mask = TupleMask.fromKeepIndicators(fixed);
117 Tuple inputSignature = mask.transform(Tuples.flatTupleOf(inputMapping));
118
119 return matchOne(mask, inputSignature);
120 }
121
122 /**
123 * @since 2.0
124 */
125 public Optional<Tuple> matchOne(TupleMask mask, ITuple inputSignature) {
126 SingleMatchFetcher fetcher = new SingleMatchFetcher(engine.accessProjection(productionNodeTrace, mask),
127 context.wrapTuple(inputSignature.toImmutable()));
128 engine.reteNet.waitForReteTermination(fetcher);
129 return Optional.ofNullable(fetcher.getMatch());
130 }
131
132 /**
133 * Counts the number of occurrences of the pattern that match inputMapping on positions where fixed is true.
134 *
135 * @return the number of occurrences
136 */
137 public int count(Object[] inputMapping, boolean[] fixed) {
138 TupleMask mask = TupleMask.fromKeepIndicators(fixed);
139 Tuple inputSignature = mask.transform(Tuples.flatTupleOf(inputMapping));
140
141 return count(mask, inputSignature);
142 }
143
144 /**
145 * Counts the number of occurrences of the pattern that match inputMapping on positions where fixed is true.
146 *
147 * @return the number of occurrences
148 * @since 1.7
149 */
150 public int count(TupleMask mask, ITuple inputSignature) {
151 CountFetcher fetcher = new CountFetcher(engine.accessProjection(productionNodeTrace, mask),
152 context.wrapTuple(inputSignature.toImmutable()));
153 engine.reteNet.waitForReteTermination(fetcher);
154
155 return fetcher.getCount();
156 }
157
158 /**
159 * Counts the number of distinct tuples attainable from the match set by projecting match tuples according to the given mask.
160 *
161 *
162 * @return the size of the projection
163 * @since 2.1
164 */
165 public int projectionSize(TupleMask groupMask) {
166 ProjectionSizeFetcher fetcher = new ProjectionSizeFetcher(
167 (IterableIndexer) engine.accessProjection(productionNodeTrace, groupMask));
168 engine.reteNet.waitForReteTermination(fetcher);
169
170 return fetcher.getSize();
171 }
172
173 /**
174 * Connects a new external receiver that will receive update notifications from now on. The receiver will
175 * practically connect to the production node, the added value is unwrapping the updates for external use.
176 *
177 * @param synchronize
178 * if true, the contents of the production node will be inserted into the receiver after the connection
179 * is established.
180 */
181 public synchronized void connect(Receiver receiver, boolean synchronize) {
182 if (!connected) { // connect to the production node as a RETE-child
183 reteContainer.connect(productionNode, this);
184 connected = true;
185 }
186 if (synchronize)
187 reteContainer.connectAndSynchronize(this, receiver);
188 else
189 reteContainer.connect(this, receiver);
190 }
191
192 /**
193 * Connects a new external receiver that will receive update notifications from now on. The receiver will
194 * practically connect to the production node, the added value is unwrapping the updates for external use.
195 *
196 * The external receiver will be disconnectable later based on its tag.
197 *
198 * @param tag
199 * an identifier to recognize the child node by.
200 *
201 * @param synchronize
202 * if true, the contents of the production node will be inserted into the receiver after the connection
203 * is established.
204 *
205 */
206 public synchronized void connect(Receiver receiver, Object tag, boolean synchronize) {
207 taggedChildren.put(tag, receiver);
208 connect(receiver, synchronize);
209 }
210
211 /**
212 * Disconnects a child node.
213 */
214 public synchronized void disconnect(Receiver receiver) {
215 reteContainer.disconnect(this, receiver);
216 }
217
218 /**
219 * Disconnects the child node that was connected by specifying the given tag.
220 *
221 * @return if a child node was found registered with this tag.
222 */
223 public synchronized boolean disconnectByTag(Object tag) {
224 final Receiver receiver = taggedChildren.remove(tag);
225 final boolean found = receiver != null;
226 if (found)
227 disconnect(receiver);
228 return found;
229 }
230
231 @Override
232 protected Tuple transform(Tuple input) {
233 return context.unwrapTuple(input);
234 }
235
236 abstract class AbstractMatchFetcher implements Runnable {
237 Indexer indexer;
238 Tuple signature;
239
240 public AbstractMatchFetcher(Indexer indexer, Tuple signature) {
241 super();
242 this.indexer = indexer;
243 this.signature = signature;
244 }
245
246 @Override
247 public void run() {
248 fetch(indexer.get(signature));
249 }
250
251 protected abstract void fetch(Collection<Tuple> matches);
252
253 }
254
255 class AllMatchFetcher extends AbstractMatchFetcher {
256
257 public AllMatchFetcher(Indexer indexer, Tuple signature) {
258 super(indexer, signature);
259 }
260
261 Stream<Tuple> matches = null;
262
263 public Stream<Tuple> getMatches() {
264 return matches;
265 }
266
267 @Override
268 protected void fetch(Collection<Tuple> matches) {
269 if (matches == null)
270 this.matches = Stream.of();
271 else {
272 this.matches = matches.stream().map(context::unwrapTuple);
273 }
274
275 }
276
277 }
278
279 class SingleMatchFetcher extends AbstractMatchFetcher {
280
281 public SingleMatchFetcher(Indexer indexer, Tuple signature) {
282 super(indexer, signature);
283 }
284
285 Tuple match = null;
286
287 public Tuple getMatch() {
288 return match;
289 }
290
291 @Override
292 protected void fetch(Collection<Tuple> matches) {
293 if (matches != null && !matches.isEmpty())
294 match = context.unwrapTuple(matches.iterator().next());
295 }
296
297 // public void run() {
298 // Collection<Tuple> unscopedMatches = indexer.get(signature);
299 //
300 // // checking scopes
301 // if (unscopedMatches != null) {
302 // for (Tuple um : /* productionNode */unscopedMatches) {
303 // match = inputConnector.unwrapTuple(um);
304 // return;
305 //
306 // // Tuple ps = inputConnector.unwrapTuple(um);
307 // // boolean ok = true;
308 // // if (!ignoreScope) for (int k = 0; (k < ps.getSize()) && ok; k++) {
309 // // if (pcs[k].getParameterMode() == ParameterMode.INPUT) {
310 // // // ok = ok && (inputMapping[k]==ps.elements[k]);
311 // // // should now be true
312 // // } else // ParameterMode.OUTPUT
313 // // {
314 // // IEntity scopeParent = (IEntity) pcs[k].getParameterScope().getParent();
315 // // Integer containmentMode = pcs[k].getParameterScope().getContainmentMode();
316 // // if (containmentMode == Scope.BELOW)
317 // // ok = ok && ((IModelElement) ps.get(k)).isBelowNamespace(scopeParent);
318 // // else
319 // // /* case Scope.IN: */
320 // // ok = ok && scopeParent.equals(((IModelElement) ps.get(k)).getNamespace());
321 // // // note: getNamespace returns null instead of the
322 // // // (imaginary) modelspace root entity for top level
323 // // // elements;
324 // // // this is not a problem here as Scope.IN implies
325 // // // scopeParent != root.
326 // //
327 // // }
328 // // }
329 // //
330 // // if (ok) {
331 // // reteMatching = new ReteMatching(ps, posMapping);
332 // // return;
333 // // }
334 // }
335 // }
336 //
337 // }
338
339 }
340
341 class CountFetcher extends AbstractMatchFetcher {
342
343 public CountFetcher(Indexer indexer, Tuple signature) {
344 super(indexer, signature);
345 }
346
347 int count = 0;
348
349 public int getCount() {
350 return count;
351 }
352
353 @Override
354 protected void fetch(Collection<Tuple> matches) {
355 count = matches == null ? 0 : matches.size();
356 }
357
358 }
359
360 class ProjectionSizeFetcher implements Runnable {
361 IterableIndexer indexer;
362 int size = 0;
363
364 public ProjectionSizeFetcher(IterableIndexer indexer) {
365 super();
366 this.indexer = indexer;
367 }
368
369 @Override
370 public void run() {
371 size = indexer.getBucketCount();
372 }
373
374 public int getSize() {
375 return size;
376 }
377
378 }
379
380 private boolean[] notNull(Object[] parameters) {
381 boolean[] notNull = new boolean[parameters.length];
382 for (int i = 0; i < parameters.length; ++i)
383 notNull[i] = parameters[i] != null;
384 return notNull;
385 }
386
387
388
389 @Override
390 public boolean hasMatch(Object[] parameters) {
391 return countMatches(parameters) > 0;
392 }
393
394 @Override
395 public boolean hasMatch(TupleMask parameterSeedMask, ITuple parameters) {
396 return count(parameterSeedMask, parameters) > 0;
397 }
398
399 @Override
400 public int countMatches(Object[] parameters) {
401 return count(parameters, notNull(parameters));
402 }
403
404 @Override
405 public int countMatches(TupleMask parameterSeedMask, ITuple parameters) {
406 return count(parameterSeedMask, parameters);
407 }
408
409
410 @Override
411 public Optional<Long> estimateCardinality(TupleMask groupMask, Accuracy requiredAccuracy) {
412 return Optional.of((long)projectionSize(groupMask)); // always accurate
413 }
414
415 @Override
416 public Optional<Tuple> getOneArbitraryMatch(Object[] parameters) {
417 return matchOne(parameters, notNull(parameters));
418 }
419
420 @Override
421 public Optional<Tuple> getOneArbitraryMatch(TupleMask parameterSeedMask, ITuple parameters) {
422 return matchOne(parameterSeedMask, parameters);
423 }
424
425 @Override
426 public Stream<Tuple> getAllMatches(Object[] parameters) {
427 return matchAll(parameters, notNull(parameters));
428 }
429
430 @Override
431 public Stream<Tuple> getAllMatches(TupleMask parameterSeedMask, ITuple parameters) {
432 return matchAll(parameterSeedMask, parameters);
433 }
434
435 @Override
436 public IQueryBackend getQueryBackend() {
437 return engine;
438 }
439
440 @Override
441 public void addUpdateListener(final IUpdateable listener, final Object listenerTag, boolean fireNow) {
442 // As a listener is added as a delayed command, they should be executed to make sure everything is consistent on
443 // return, see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=562369
444 engine.constructionWrapper(() -> {
445 final CallbackNode callbackNode = new CallbackNode(this.reteContainer, listener);
446 connect(callbackNode, listenerTag, fireNow);
447 return null;
448 });
449 }
450
451 @Override
452 public void removeUpdateListener(Object listenerTag) {
453 engine.constructionWrapper(() -> {
454 disconnectByTag(listenerTag);
455 return null;
456 });
457 }
458
459 public Indexer getInternalIndexer(TupleMask mask) {
460 return engine.accessProjection(productionNodeTrace, mask);
461 }
462}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyConfiguration.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyConfiguration.java
new file mode 100644
index 00000000..876ddc99
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyConfiguration.java
@@ -0,0 +1,61 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.rete.matcher;
10
11/**
12 * Configuration of timely evaluation.
13 *
14 * @author Tamas Szabo
15 * @since 2.4
16 */
17public class TimelyConfiguration {
18
19 private final AggregatorArchitecture aggregatorArchitecture;
20 private final TimelineRepresentation timelineRepresentation;
21
22 public TimelyConfiguration(final TimelineRepresentation timelineRepresentation,
23 final AggregatorArchitecture aggregatorArchitecture) {
24 this.aggregatorArchitecture = aggregatorArchitecture;
25 this.timelineRepresentation = timelineRepresentation;
26 }
27
28 public AggregatorArchitecture getAggregatorArchitecture() {
29 return aggregatorArchitecture;
30 }
31
32 public TimelineRepresentation getTimelineRepresentation() {
33 return timelineRepresentation;
34 }
35
36 public enum AggregatorArchitecture {
37 /**
38 * Aggregands are copied over from lower timestamps to higher timestamps.
39 */
40 PARALLEL,
41
42 /**
43 * Aggregands are only present at the timestamp where they are inserted at.
44 * Only aggregate results are pushed towards higher timestamps during folding.
45 */
46 SEQUENTIAL
47 }
48
49 public enum TimelineRepresentation {
50 /**
51 * Only the first moment (timestamp) of appearance is maintained per tuple.
52 */
53 FIRST_ONLY,
54
55 /**
56 * Complete timeline (series of appearance & disappearance) is maintained per tuple.
57 */
58 FAITHFUL
59 }
60
61}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyReteBackendFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyReteBackendFactory.java
new file mode 100644
index 00000000..2777f169
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyReteBackendFactory.java
@@ -0,0 +1,64 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.matcher;
10
11import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
12import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
13import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.AggregatorArchitecture;
14import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
15
16/**
17 * A {@link ReteBackendFactory} implementation that creates {@link ReteEngine}s that use non-scattered timely
18 * evaluation.
19 *
20 * @author Tamas Szabo
21 * @since 2.4
22 */
23public class TimelyReteBackendFactory extends ReteBackendFactory {
24
25 private final TimelyConfiguration configuration;
26
27 public static final TimelyReteBackendFactory FIRST_ONLY_SEQUENTIAL = new TimelyReteBackendFactory(
28 new TimelyConfiguration(TimelineRepresentation.FIRST_ONLY, AggregatorArchitecture.SEQUENTIAL));
29 public static final TimelyReteBackendFactory FIRST_ONLY_PARALLEL = new TimelyReteBackendFactory(
30 new TimelyConfiguration(TimelineRepresentation.FIRST_ONLY, AggregatorArchitecture.PARALLEL));
31 public static final TimelyReteBackendFactory FAITHFUL_SEQUENTIAL = new TimelyReteBackendFactory(
32 new TimelyConfiguration(TimelineRepresentation.FAITHFUL, AggregatorArchitecture.SEQUENTIAL));
33 public static final TimelyReteBackendFactory FAITHFUL_PARALLEL = new TimelyReteBackendFactory(
34 new TimelyConfiguration(TimelineRepresentation.FAITHFUL, AggregatorArchitecture.PARALLEL));
35
36 public TimelyReteBackendFactory(final TimelyConfiguration configuration) {
37 this.configuration = configuration;
38 }
39
40 @Override
41 public IQueryBackend create(final IQueryBackendContext context) {
42 return create(context, false, configuration);
43 }
44
45 @Override
46 public int hashCode() {
47 return TimelyReteBackendFactory.class.hashCode();
48 }
49
50 @Override
51 public boolean equals(final Object obj) {
52 if (this == obj) {
53 return true;
54 }
55 if (obj == null) {
56 return false;
57 }
58 if (!(obj instanceof TimelyReteBackendFactory)) {
59 return false;
60 }
61 return true;
62 }
63
64}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/Bag.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/Bag.java
new file mode 100644
index 00000000..4aef0f96
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/Bag.java
@@ -0,0 +1,43 @@
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.rete.misc;
11
12import java.util.Collection;
13import java.util.LinkedList;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.rete.network.ReteContainer;
18import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
19
20/**
21 * @author Gabor Bergmann
22 *
23 * A bag is a container that tuples can be dumped into. Does NOT propagate updates! Optimized for small contents
24 * size OR positive updates only.
25 */
26public class Bag extends SimpleReceiver {
27
28 public Collection<Tuple> contents;
29
30 public Bag(ReteContainer reteContainer) {
31 super(reteContainer);
32 contents = new LinkedList<Tuple>();
33 }
34
35 @Override
36 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
37 if (direction == Direction.INSERT)
38 contents.add(updateElement);
39 else
40 contents.remove(updateElement);
41 }
42
43}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/ConstantNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/ConstantNode.java
new file mode 100644
index 00000000..980d3eee
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/ConstantNode.java
@@ -0,0 +1,50 @@
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.rete.misc;
11
12import java.util.Collection;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.StandardNode;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21
22/**
23 * Node that always contains a single constant Tuple
24 *
25 * @author Gabor Bergmann
26 */
27public class ConstantNode extends StandardNode {
28
29 protected Tuple constant;
30
31 /**
32 * @param constant
33 * will be wrapped using {@link IQueryRuntimeContext#wrapTuple(Tuple)}
34 */
35 public ConstantNode(ReteContainer reteContainer, Tuple constant) {
36 super(reteContainer);
37 this.constant = reteContainer.getNetwork().getEngine().getRuntimeContext().wrapTuple(constant);
38 }
39
40 @Override
41 public void pullInto(Collection<Tuple> collector, boolean flush) {
42 collector.add(constant);
43 }
44
45 @Override
46 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
47 collector.put(constant, Timestamp.INSERT_AT_ZERO_TIMELINE);
48 }
49
50}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DefaultDeltaMonitor.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DefaultDeltaMonitor.java
new file mode 100644
index 00000000..efba3117
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DefaultDeltaMonitor.java
@@ -0,0 +1,43 @@
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.rete.misc;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.rete.network.Network;
14import tools.refinery.viatra.runtime.rete.network.ReteContainer;
15
16/**
17 * Default configuration for DeltaMonitor.
18 *
19 * @author Gabor Bergmann
20 *
21 */
22public class DefaultDeltaMonitor extends DeltaMonitor<Tuple> {
23
24 /**
25 * @param reteContainer
26 */
27 public DefaultDeltaMonitor(ReteContainer reteContainer) {
28 super(reteContainer);
29 }
30
31 /**
32 * @param network
33 */
34 public DefaultDeltaMonitor(Network network) {
35 super(network.getHeadContainer());
36 }
37
38 @Override
39 public Tuple statelessConvert(Tuple tuple) {
40 return tuple;
41 }
42
43}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DeltaMonitor.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DeltaMonitor.java
new file mode 100644
index 00000000..82b6ecda
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DeltaMonitor.java
@@ -0,0 +1,111 @@
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.rete.misc;
11
12import java.util.Collection;
13import java.util.LinkedHashSet;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Clearable;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20
21/**
22 * A monitoring object that connects to the rete network as a receiver to reflect changes since an arbitrary state
23 * acknowledged by the client. Match tuples are represented by a type MatchType.
24 *
25 * <p>
26 * <b>Usage</b>. If a new matching is found, it appears in the matchFoundEvents collection, and disappears when that
27 * particular matching cannot be found anymore. If the event of finding a match has been processed by the client, it can
28 * be removed manually. In this case, when a previously found matching is lost, the Tuple will appear in the
29 * matchLostEvents collection, and disappear upon finding the same matching again. "Matching lost" events can also be
30 * acknowledged by removing a Tuple from the collection. If the matching is found once again, it will return to
31 * matchFoundEvents.
32 *
33 * <p>
34 * <b>Technical notes</b>. Does NOT propagate updates!
35 *
36 * By overriding statelessConvert(), results can be stored to a MatchType. MatchType must provide equals() and
37 * hashCode() reflecting its contents. The default implementation (DefaultDeltaMonitor) uses Tuple as MatchType.
38 *
39 * By overriding statelessFilter(), some tuples can be filtered.
40 *
41 * @author Gabor Bergmann
42 *
43 */
44public abstract class DeltaMonitor<MatchType> extends SimpleReceiver implements Clearable {
45
46 /**
47 * matches that are newly found
48 */
49 public Collection<MatchType> matchFoundEvents;
50 /**
51 * matches that are newly lost
52 */
53 public Collection<MatchType> matchLostEvents;
54
55 /**
56 * @param reteContainer
57 */
58 public DeltaMonitor(ReteContainer reteContainer) {
59 super(reteContainer);
60 matchFoundEvents = new LinkedHashSet<MatchType>();
61 matchLostEvents = new LinkedHashSet<MatchType>();
62 reteContainer.registerClearable(this);
63 }
64
65 // /**
66 // * Build a delta monitor into the head container of the network.
67 // *
68 // * @param network
69 // */
70 // public DeltaMonitor(Network network) {
71 // this(network.getHeadContainer());
72 // }
73
74 /**
75 * Override this method to provide a lightweight, stateless filter on the tuples
76 *
77 * @param tuple
78 * the occurrence that is to be filtered
79 * @return true if this tuple should be monitored, false if ignored
80 */
81 public boolean statelessFilter(Tuple tuple) {
82 return true;
83 }
84
85 public abstract MatchType statelessConvert(Tuple tuple);
86
87 @Override
88 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
89 if (statelessFilter(updateElement)) {
90 MatchType match = statelessConvert(updateElement);
91 if (direction == Direction.INSERT) {
92 if (!matchLostEvents.remove(match)) // either had before but
93 // lost
94 matchFoundEvents.add(match); // or brand-new
95 } else // revoke
96 {
97 if (!matchFoundEvents.remove(match)) // either never found
98 // in the first
99 // place
100 matchLostEvents.add(match); // or newly lost
101 }
102 }
103 }
104
105 @Override
106 public void clear() {
107 matchFoundEvents.clear();
108 matchLostEvents.clear();
109 }
110
111}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/SimpleReceiver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/SimpleReceiver.java
new file mode 100644
index 00000000..dcf9ae78
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/SimpleReceiver.java
@@ -0,0 +1,109 @@
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.rete.misc;
10
11import java.util.Collection;
12import java.util.Collections;
13
14import tools.refinery.viatra.runtime.rete.network.BaseNode;
15import tools.refinery.viatra.runtime.rete.network.Receiver;
16import tools.refinery.viatra.runtime.rete.network.ReteContainer;
17import tools.refinery.viatra.runtime.rete.network.Supplier;
18import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
19import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
20import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
21import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
22
23/**
24 * @author Bergmann Gabor
25 *
26 */
27public abstract class SimpleReceiver extends BaseNode implements Receiver {
28
29 protected Supplier parent = null;
30 /**
31 * @since 1.6
32 */
33 protected final Mailbox mailbox;
34
35 /**
36 * @param reteContainer
37 */
38 public SimpleReceiver(ReteContainer reteContainer) {
39 super(reteContainer);
40 mailbox = instantiateMailbox();
41 reteContainer.registerClearable(mailbox);
42 }
43
44 /**
45 * Instantiates the {@link Mailbox} of this receiver.
46 * Subclasses may override this method to provide their own mailbox implementation.
47 *
48 * @return the mailbox
49 * @since 2.0
50 */
51 protected Mailbox instantiateMailbox() {
52 if (this.reteContainer.isTimelyEvaluation()) {
53 return new TimelyMailbox(this, this.reteContainer);
54 } else {
55 return new BehaviorChangingMailbox(this, this.reteContainer);
56 }
57 }
58
59 @Override
60 public Mailbox getMailbox() {
61 return this.mailbox;
62 }
63
64 @Override
65 public void appendParent(Supplier supplier) {
66 if (parent == null)
67 parent = supplier;
68 else
69 throw new UnsupportedOperationException("Illegal RETE edge: " + this + " already has a parent (" + parent
70 + ") and cannot connect to additional parent (" + supplier
71 + ") as it is not a Uniqueness Enforcer Node. ");
72 }
73
74 @Override
75 public void removeParent(Supplier supplier) {
76 if (parent == supplier)
77 parent = null;
78 else
79 throw new IllegalArgumentException("Illegal RETE edge removal: the parent of " + this + " is not "
80 + supplier);
81 }
82
83 @Override
84 public Collection<Supplier> getParents() {
85 if (parent == null)
86 return Collections.emptySet();
87 else
88 return Collections.singleton(parent);
89 }
90
91 /**
92 * Disconnects this node from the network. Can be called publicly.
93 *
94 * @pre: child nodes, if any, must already be disconnected.
95 */
96 public void disconnectFromNetwork() {
97 if (parent != null)
98 reteContainer.disconnect(parent, this);
99 }
100
101 @Override
102 public void assignTraceInfo(TraceInfo traceInfo) {
103 super.assignTraceInfo(traceInfo);
104 if (traceInfo.propagateFromStandardNodeToSupplierParent())
105 if (parent != null)
106 parent.acceptPropagatedTraceInfo(traceInfo);
107 }
108
109} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/BaseNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/BaseNode.java
new file mode 100644
index 00000000..2469d6bd
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/BaseNode.java
@@ -0,0 +1,108 @@
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.rete.network;
10
11import java.util.Collections;
12import java.util.HashSet;
13import java.util.Set;
14import java.util.TreeSet;
15
16import tools.refinery.viatra.runtime.rete.traceability.PatternTraceInfo;
17import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
18
19/**
20 * Base implementation for a Rete node.
21 *
22 * @author Bergmann Gabor
23 *
24 */
25public abstract class BaseNode implements Node {
26
27 protected ReteContainer reteContainer;
28 protected long nodeId;
29 protected Object tag;
30 protected Set<TraceInfo> traceInfos;
31
32 /**
33 * @param reteContainer
34 * the container to create this node in
35 */
36 public BaseNode(ReteContainer reteContainer) {
37 super();
38 this.reteContainer = reteContainer;
39 this.nodeId = reteContainer.registerNode(this);
40 this.traceInfos = new HashSet<TraceInfo>();
41 }
42
43 @Override
44 public String toString() {
45 if (tag != null)
46 return toStringCore() + "->" + getTraceInfoPatternsEnumerated() + "{" + tag.toString() + "}";
47 else
48 return toStringCore() + "->" + getTraceInfoPatternsEnumerated();
49 }
50
51 /**
52 * clients should override this to append before the tag / trace indicators
53 */
54 protected String toStringCore() {
55 return "[" + nodeId + "]" + getClass().getSimpleName();
56 }
57
58 @Override
59 public ReteContainer getContainer() {
60 return reteContainer;
61 }
62
63 @Override
64 public long getNodeId() {
65 return nodeId;
66 }
67
68 @Override
69 public Object getTag() {
70 return tag;
71 }
72
73 @Override
74 public void setTag(Object tag) {
75 this.tag = tag;
76 }
77
78 @Override
79 public Set<TraceInfo> getTraceInfos() {
80 return Collections.unmodifiableSet(traceInfos);
81 }
82
83 @Override
84 public void assignTraceInfo(TraceInfo traceInfo) {
85 traceInfos.add(traceInfo);
86 traceInfo.assignNode(this);
87 }
88
89 @Override
90 public void acceptPropagatedTraceInfo(TraceInfo traceInfo) {
91 assignTraceInfo(traceInfo);
92 }
93
94 /**
95 * Descendants should use this in e.g. logging
96 */
97 protected String getTraceInfoPatternsEnumerated() {
98 TreeSet<String> patternNames = new TreeSet<String>();
99 for (TraceInfo trInfo : traceInfos) {
100 if (trInfo instanceof PatternTraceInfo) {
101 final String pName = ((PatternTraceInfo) trInfo).getPatternName();
102 patternNames.add(pName);
103 }
104 }
105 return patternNames.toString();
106 }
107
108} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java
new file mode 100644
index 00000000..6606a75d
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java
@@ -0,0 +1,170 @@
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.rete.network;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import tools.refinery.viatra.runtime.rete.aggregation.IndexerBasedAggregatorNode;
13import tools.refinery.viatra.runtime.rete.boundary.InputConnector;
14import tools.refinery.viatra.runtime.rete.eval.RelationEvaluatorNode;
15import tools.refinery.viatra.runtime.rete.index.DualInputNode;
16import tools.refinery.viatra.runtime.rete.index.Indexer;
17import tools.refinery.viatra.runtime.rete.index.IterableIndexer;
18import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
19import tools.refinery.viatra.runtime.rete.recipes.*;
20import tools.refinery.viatra.runtime.rete.remote.Address;
21import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
22
23import java.util.ArrayList;
24import java.util.Collection;
25import java.util.List;
26
27/**
28 * Class responsible for connecting freshly instantiating Rete nodes to their parents.
29 *
30 * @author Bergmann Gabor
31 *
32 */
33class ConnectionFactory {
34 ReteContainer reteContainer;
35
36 public ConnectionFactory(ReteContainer reteContainer) {
37 super();
38 this.reteContainer = reteContainer;
39 }
40
41 // TODO move to node implementation instead?
42 private boolean isStateful(ReteNodeRecipe recipe) {
43 return recipe instanceof ProjectionIndexerRecipe || recipe instanceof IndexerBasedAggregatorRecipe
44 || recipe instanceof SingleColumnAggregatorRecipe || recipe instanceof ExpressionEnforcerRecipe
45 || recipe instanceof TransitiveClosureRecipe || recipe instanceof ProductionRecipe
46 || recipe instanceof UniquenessEnforcerRecipe || recipe instanceof RelationEvaluationRecipe;
47
48 }
49
50 /**
51 * PRE: nodes for parent recipes must already be created and registered
52 * <p>
53 * PRE: must not be an input node (for which {@link InputConnector} is responsible)
54 */
55 public void connectToParents(RecipeTraceInfo recipeTrace, Node freshNode) {
56 final ReteNodeRecipe recipe = recipeTrace.getRecipe();
57 if (recipe instanceof ConstantRecipe) {
58 // NO-OP
59 } else if (recipe instanceof InputRecipe) {
60 throw new IllegalArgumentException(
61 ConnectionFactory.class.getSimpleName() + " not intended for input connection: " + recipe);
62 } else if (recipe instanceof SingleParentNodeRecipe) {
63 final Receiver receiver = (Receiver) freshNode;
64 ReteNodeRecipe parentRecipe = ((SingleParentNodeRecipe) recipe).getParent();
65 connectToParent(recipe, receiver, parentRecipe);
66 } else if (recipe instanceof RelationEvaluationRecipe) {
67 List<ReteNodeRecipe> parentRecipes = ((MultiParentNodeRecipe) recipe).getParents();
68 List<Supplier> parentSuppliers = new ArrayList<Supplier>();
69 for (final ReteNodeRecipe parentRecipe : parentRecipes) {
70 parentSuppliers.add(getSupplierForRecipe(parentRecipe));
71 }
72 ((RelationEvaluatorNode) freshNode).connectToParents(parentSuppliers);
73 } else if (recipe instanceof BetaRecipe) {
74 final DualInputNode beta = (DualInputNode) freshNode;
75 final ArrayList<RecipeTraceInfo> parentTraces = new ArrayList<RecipeTraceInfo>(
76 recipeTrace.getParentRecipeTraces());
77 Slots slots = avoidActiveNodeConflict(parentTraces.get(0), parentTraces.get(1));
78 beta.connectToIndexers(slots.primary, slots.secondary);
79 } else if (recipe instanceof IndexerBasedAggregatorRecipe) {
80 final IndexerBasedAggregatorNode aggregator = (IndexerBasedAggregatorNode) freshNode;
81 final IndexerBasedAggregatorRecipe aggregatorRecipe = (IndexerBasedAggregatorRecipe) recipe;
82 aggregator.initializeWith((ProjectionIndexer) resolveIndexer(aggregatorRecipe.getParent()));
83 } else if (recipe instanceof MultiParentNodeRecipe) {
84 final Receiver receiver = (Receiver) freshNode;
85 List<ReteNodeRecipe> parentRecipes = ((MultiParentNodeRecipe) recipe).getParents();
86 for (ReteNodeRecipe parentRecipe : parentRecipes) {
87 connectToParent(recipe, receiver, parentRecipe);
88 }
89 }
90 }
91
92 private Indexer resolveIndexer(final IndexerRecipe indexerRecipe) {
93 final Address<? extends Node> address = reteContainer.getNetwork().getExistingNodeByRecipe(indexerRecipe);
94 return (Indexer) reteContainer.resolveLocal(address);
95 }
96
97 private void connectToParent(ReteNodeRecipe recipe, Receiver freshNode, ReteNodeRecipe parentRecipe) {
98 final Supplier parentSupplier = getSupplierForRecipe(parentRecipe);
99
100 // special synch
101 if (freshNode instanceof ReinitializedNode) {
102 Collection<Tuple> tuples = new ArrayList<Tuple>();
103 parentSupplier.pullInto(tuples, true);
104 ((ReinitializedNode) freshNode).reinitializeWith(tuples);
105 reteContainer.connect(parentSupplier, freshNode);
106 } else { // default case
107 // stateless nodes do not have to be synced with contents UNLESS they already have children (recursive
108 // corner case)
109 if (isStateful(recipe)
110 || ((freshNode instanceof Supplier) && !((Supplier) freshNode).getReceivers().isEmpty())) {
111 reteContainer.connectAndSynchronize(parentSupplier, freshNode);
112 } else {
113 // stateless node, no synch
114 reteContainer.connect(parentSupplier, freshNode);
115 }
116 }
117 }
118
119 private Supplier getSupplierForRecipe(ReteNodeRecipe recipe) {
120 @SuppressWarnings("unchecked")
121 final Address<? extends Supplier> parentAddress = (Address<? extends Supplier>) reteContainer.getNetwork()
122 .getExistingNodeByRecipe(recipe);
123 final Supplier supplier = reteContainer.getProvisioner().asSupplier(parentAddress);
124 return supplier;
125 }
126
127 /**
128 * If two indexers share their active node, joining them via DualInputNode is error-prone. Exception: coincidence of
129 * the two indexers is supported.
130 *
131 * @return a replacement for the secondary Indexers, if needed
132 */
133 private Slots avoidActiveNodeConflict(final RecipeTraceInfo primarySlot, final RecipeTraceInfo secondarySlot) {
134 Slots result = new Slots() {
135 {
136 primary = (IterableIndexer) resolveIndexer((ProjectionIndexerRecipe) primarySlot.getRecipe());
137 secondary = resolveIndexer((IndexerRecipe) secondarySlot.getRecipe());
138 }
139 };
140 if (activeNodeConflict(result.primary, result.secondary))
141 if (result.secondary instanceof IterableIndexer)
142 result.secondary = resolveActiveIndexer(secondarySlot);
143 else
144 result.primary = (IterableIndexer) resolveActiveIndexer(primarySlot);
145 return result;
146 }
147
148 private Indexer resolveActiveIndexer(final RecipeTraceInfo inactiveIndexerTrace) {
149 final RecipeTraceInfo activeIndexerTrace = reteContainer.getProvisioner()
150 .accessActiveIndexer(inactiveIndexerTrace);
151 reteContainer.getProvisioner().getOrCreateNodeByRecipe(activeIndexerTrace);
152 return resolveIndexer((ProjectionIndexerRecipe) activeIndexerTrace.getRecipe());
153 }
154
155 private static class Slots {
156 IterableIndexer primary;
157 Indexer secondary;
158 }
159
160 /**
161 * If two indexers share their active node, joining them via DualInputNode is error-prone. Exception: coincidence of
162 * the two indexers is supported.
163 *
164 * @return true if there is a conflict of active nodes.
165 */
166 private boolean activeNodeConflict(Indexer primarySlot, Indexer secondarySlot) {
167 return !primarySlot.equals(secondarySlot) && primarySlot.getActiveNode().equals(secondarySlot.getActiveNode());
168 }
169
170}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/IGroupable.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/IGroupable.java
new file mode 100644
index 00000000..c22b06d8
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/IGroupable.java
@@ -0,0 +1,31 @@
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.rete.network;
10
11import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
12
13/**
14 * @author Gabor Bergmann
15 * @since 1.7
16 */
17public interface IGroupable {
18
19 /**
20 * @return the current group of the mailbox
21 * @since 1.7
22 */
23 CommunicationGroup getCurrentGroup();
24
25 /**
26 * Sets the current group of the mailbox
27 * @since 1.7
28 */
29 void setCurrentGroup(CommunicationGroup group);
30
31} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Network.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Network.java
new file mode 100644
index 00000000..64f59ff3
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Network.java
@@ -0,0 +1,408 @@
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.rete.network;
11
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.List;
16import java.util.Map;
17import java.util.Map.Entry;
18import java.util.Set;
19import java.util.concurrent.locks.Lock;
20import java.util.concurrent.locks.ReadWriteLock;
21import java.util.concurrent.locks.ReentrantReadWriteLock;
22
23import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
24import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
25import tools.refinery.viatra.runtime.matchers.util.Direction;
26import tools.refinery.viatra.runtime.rete.boundary.InputConnector;
27import tools.refinery.viatra.runtime.rete.matcher.ReteEngine;
28import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
29import tools.refinery.viatra.runtime.rete.remote.Address;
30import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
31import tools.refinery.viatra.runtime.rete.util.Options;
32
33/**
34 * @author Gabor Bergmann
35 *
36 */
37public class Network {
38 final int threads;
39
40 protected ArrayList<ReteContainer> containers;
41 ReteContainer headContainer;
42 private int firstContainer = 0;
43 private int nextContainer = 0;
44
45 // the following fields exist only if threads > 0
46 protected Map<ReteContainer, Long> globalTerminationCriteria = null;
47 protected Map<ReteContainer, Long> reportedClocks = null;
48 protected Lock updateLock = null; // grab during normal update operations
49 protected Lock structuralChangeLock = null; // grab if the network structure
50 // is to
51 // be changed
52
53 // Knowledge of the outside world
54 private ReteEngine engine;
55 protected NodeFactory nodeFactory;
56 protected InputConnector inputConnector;
57
58 // Node and recipe administration
59 // incl. addresses for existing nodes by recipe (where available)
60 // Maintained by NodeProvisioner of each container
61 Map<ReteNodeRecipe, Address<? extends Node>> nodesByRecipe = CollectionsFactory.createMap();
62 Set<RecipeTraceInfo> recipeTraces = CollectionsFactory.createSet();
63
64 /**
65 * @throws IllegalStateException
66 * if no node has been constructed for the recipe
67 */
68 public synchronized Address<? extends Node> getExistingNodeByRecipe(ReteNodeRecipe recipe) {
69 final Address<? extends Node> node = nodesByRecipe.get(recipe);
70 if (node == null)
71 throw new IllegalStateException(String.format("Rete node for recipe %s not constructed yet.", recipe));
72 return node;
73 }
74
75 /**
76 * @return null if no node has been constructed for the recipe
77 */
78 public synchronized Address<? extends Node> getNodeByRecipeIfExists(ReteNodeRecipe recipe) {
79 final Address<? extends Node> node = nodesByRecipe.get(recipe);
80 return node;
81 }
82
83 /**
84 * @param threads
85 * the number of threads to operate the network with; 0 means single-threaded operation, 1 starts an
86 * asynchronous thread to operate the RETE net, >1 uses multiple RETE containers.
87 */
88 public Network(int threads, ReteEngine engine) {
89 super();
90 this.threads = threads;
91 this.engine = engine;
92 this.inputConnector = new InputConnector(this);
93 this.nodeFactory = new NodeFactory(engine.getLogger());
94
95 containers = new ArrayList<ReteContainer>();
96 firstContainer = (threads > 1) ? Options.firstFreeContainer : 0; // NOPMD
97 nextContainer = firstContainer;
98
99 if (threads > 0) {
100 globalTerminationCriteria = CollectionsFactory.createMap();
101 reportedClocks = CollectionsFactory.createMap();
102 ReadWriteLock rwl = new ReentrantReadWriteLock();
103 updateLock = rwl.readLock();
104 structuralChangeLock = rwl.writeLock();
105 for (int i = 0; i < threads; ++i)
106 containers.add(new ReteContainer(this, true));
107 } else
108 containers.add(new ReteContainer(this, false));
109
110 headContainer = containers.get(0);
111 }
112
113 /**
114 * Kills this Network along with all containers and message consumption cycles.
115 */
116 public void kill() {
117 for (ReteContainer container : containers) {
118 container.kill();
119 }
120 containers.clear();
121 }
122
123 /**
124 * Returns the head container, that is guaranteed to reside in the same JVM as the Network object.
125 */
126 public ReteContainer getHeadContainer() {
127 return headContainer;
128 }
129
130 /**
131 * Returns the next container in round-robin fashion. Configurable not to yield head container.
132 */
133 public ReteContainer getNextContainer() {
134 if (nextContainer >= containers.size())
135 nextContainer = firstContainer;
136 return containers.get(nextContainer++);
137 }
138
139 /**
140 * Internal message delivery method.
141 *
142 * @pre threads > 0
143 */
144 private void sendUpdate(Address<? extends Receiver> receiver, Direction direction, Tuple updateElement) {
145 ReteContainer affectedContainer = receiver.getContainer();
146 synchronized (globalTerminationCriteria) {
147 long newCriterion = affectedContainer.sendUpdateToLocalAddress(receiver, direction, updateElement);
148 terminationCriterion(affectedContainer, newCriterion);
149 }
150 }
151
152 /**
153 * Internal message delivery method for single-threaded operation
154 *
155 * @pre threads == 0
156 */
157 private void sendUpdateSingleThreaded(Address<? extends Receiver> receiver, Direction direction,
158 Tuple updateElement) {
159 ReteContainer affectedContainer = receiver.getContainer();
160 affectedContainer.sendUpdateToLocalAddressSingleThreaded(receiver, direction, updateElement);
161 }
162
163 /**
164 * Internal message delivery method.
165 *
166 * @pre threads > 0
167 */
168 private void sendUpdates(Address<? extends Receiver> receiver, Direction direction,
169 Collection<Tuple> updateElements) {
170 if (updateElements.isEmpty())
171 return;
172 ReteContainer affectedContainer = receiver.getContainer();
173 synchronized (globalTerminationCriteria) {
174 long newCriterion = affectedContainer.sendUpdatesToLocalAddress(receiver, direction, updateElements);
175 terminationCriterion(affectedContainer, newCriterion);
176 }
177 }
178
179 /**
180 * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The node may
181 * reside in any of the containers associated with this network. To be called from a user thread during normal
182 * operation, NOT during construction.
183 *
184 * @since 2.4
185 */
186 public void sendExternalUpdate(Address<? extends Receiver> receiver, Direction direction, Tuple updateElement) {
187 if (threads > 0) {
188 try {
189 updateLock.lock();
190 sendUpdate(receiver, direction, updateElement);
191 } finally {
192 updateLock.unlock();
193 }
194 } else {
195 sendUpdateSingleThreaded(receiver, direction, updateElement);
196 // getHeadContainer().
197 }
198 }
199
200 /**
201 * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The node may
202 * reside in any of the containers associated with this network. To be called from a user thread during
203 * construction.
204 *
205 * @pre: structuralChangeLock MUST be grabbed by the sequence (but not necessarily this thread, as the sequence may
206 * span through network calls, that's why it's not enforced here )
207 *
208 * @return the value of the target container's clock at the time when the message was accepted into its message
209 * queue
210 * @since 2.4
211 */
212 public void sendConstructionUpdate(Address<? extends Receiver> receiver, Direction direction, Tuple updateElement) {
213 // structuralChangeLock.lock();
214 if (threads > 0)
215 sendUpdate(receiver, direction, updateElement);
216 else
217 receiver.getContainer().sendUpdateToLocalAddressSingleThreaded(receiver, direction, updateElement);
218 // structuralChangeLock.unlock();
219 }
220
221 /**
222 * Sends multiple update messages atomically to the receiver node, indicating a newly found or lost partial
223 * matching. The node may reside in any of the containers associated with this network. To be called from a user
224 * thread during construction.
225 *
226 * @pre: structuralChangeLock MUST be grabbed by the sequence (but not necessarily this thread, as the sequence may
227 * span through network calls, that's why it's not enforced here )
228 *
229 * @since 2.4
230 */
231 public void sendConstructionUpdates(Address<? extends Receiver> receiver, Direction direction,
232 Collection<Tuple> updateElements) {
233 // structuralChangeLock.lock();
234 if (threads > 0)
235 sendUpdates(receiver, direction, updateElements);
236 else
237 receiver.getContainer().sendUpdatesToLocalAddressSingleThreaded(receiver, direction, updateElements);
238 // structuralChangeLock.unlock();
239 }
240
241 /**
242 * Establishes connection between a supplier and a receiver node, regardless which container they are in. Not to be
243 * called remotely, because this method enforces the structural lock.
244 *
245 * @param supplier
246 * @param receiver
247 * @param synchronise
248 * indicates whether the receiver should be synchronised to the current contents of the supplier
249 */
250 public void connectRemoteNodes(Address<? extends Supplier> supplier, Address<? extends Receiver> receiver,
251 boolean synchronise) {
252 try {
253 if (threads > 0)
254 structuralChangeLock.lock();
255 receiver.getContainer().connectRemoteNodes(supplier, receiver, synchronise);
256 } finally {
257 if (threads > 0)
258 structuralChangeLock.unlock();
259 }
260 }
261
262 /**
263 * Severs connection between a supplier and a receiver node, regardless which container they are in. Not to be
264 * called remotely, because this method enforces the structural lock.
265 *
266 * @param supplier
267 * @param receiver
268 * @param desynchronise
269 * indicates whether the current contents of the supplier should be subtracted from the receiver
270 */
271 public void disconnectRemoteNodes(Address<? extends Supplier> supplier, Address<? extends Receiver> receiver,
272 boolean desynchronise) {
273 try {
274 if (threads > 0)
275 structuralChangeLock.lock();
276 receiver.getContainer().disconnectRemoteNodes(supplier, receiver, desynchronise);
277 } finally {
278 if (threads > 0)
279 structuralChangeLock.unlock();
280 }
281 }
282
283 /**
284 * Containers use this method to report whenever they run out of messages in their queue.
285 *
286 * To be called from the thread of the reporting container.
287 *
288 * @pre threads > 0.
289 * @param reportingContainer
290 * the container reporting the emptiness of its message queue.
291 * @param clock
292 * the value of the container's clock when reporting.
293 * @param localTerminationCriteria
294 * the latest clock values this container has received from other containers since the last time it
295 * reported termination.
296 */
297 void reportLocalUpdateTermination(ReteContainer reportingContainer, long clock,
298 Map<ReteContainer, Long> localTerminationCriteria) {
299 synchronized (globalTerminationCriteria) {
300 for (Entry<ReteContainer, Long> criterion : localTerminationCriteria.entrySet()) {
301 terminationCriterion(criterion.getKey(), criterion.getValue());
302 }
303
304 reportedClocks.put(reportingContainer, clock);
305 Long criterion = globalTerminationCriteria.get(reportingContainer);
306 if (criterion != null && criterion < clock)
307 globalTerminationCriteria.remove(reportingContainer);
308
309 if (globalTerminationCriteria.isEmpty())
310 globalTerminationCriteria.notifyAll();
311 }
312 }
313
314 /**
315 * @pre threads > 0
316 */
317 private void terminationCriterion(ReteContainer affectedContainer, long newCriterion) {
318 synchronized (globalTerminationCriteria) {
319 Long oldCriterion = globalTerminationCriteria.get(affectedContainer);
320 Long oldClock = reportedClocks.get(affectedContainer);
321 long relevantClock = oldClock == null ? 0 : oldClock;
322 if ((relevantClock <= newCriterion) && (oldCriterion == null || oldCriterion < newCriterion)) {
323 globalTerminationCriteria.put(affectedContainer, newCriterion);
324 }
325 }
326 }
327
328 /**
329 * Waits until all rete update operations are settled in all containers. Returns immediately, if no updates are
330 * pending.
331 *
332 * To be called from any user thread.
333 */
334 public void waitForReteTermination() {
335 if (threads > 0) {
336 synchronized (globalTerminationCriteria) {
337 while (!globalTerminationCriteria.isEmpty()) {
338 try {
339 globalTerminationCriteria.wait();
340 } catch (InterruptedException e) {
341
342 }
343 }
344 }
345 } else
346 headContainer.deliverMessagesSingleThreaded();
347 }
348
349 /**
350 * Waits to execute action until all rete update operations are settled in all containers. Runs action and returns
351 * immediately, if no updates are pending. The given action is guaranteed to be run when the terminated state still
352 * persists.
353 *
354 * @param action
355 * the action to be run when reaching the steady-state.
356 *
357 * To be called from any user thread.
358 */
359 public void waitForReteTermination(Runnable action) {
360 if (threads > 0) {
361 synchronized (globalTerminationCriteria) {
362 while (!globalTerminationCriteria.isEmpty()) {
363 try {
364 globalTerminationCriteria.wait();
365 } catch (InterruptedException e) {
366
367 }
368 }
369 action.run();
370 }
371 } else {
372 headContainer.deliverMessagesSingleThreaded();
373 action.run();
374 }
375
376 }
377
378 /**
379 * @return an unmodifiable set of known recipe traces
380 */
381 public Set<RecipeTraceInfo> getRecipeTraces() {
382 return Collections.unmodifiableSet(recipeTraces);
383 }
384
385 /**
386 * @return an unmodifiable list of containers
387 */
388 public List<ReteContainer> getContainers() {
389 return Collections.unmodifiableList(containers);
390 }
391
392 public Lock getStructuralChangeLock() {
393 return structuralChangeLock;
394 }
395
396 public NodeFactory getNodeFactory() {
397 return nodeFactory;
398 }
399
400 public InputConnector getInputConnector() {
401 return inputConnector;
402 }
403
404 public ReteEngine getEngine() {
405 return engine;
406 }
407
408}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NetworkStructureChangeSensitiveNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NetworkStructureChangeSensitiveNode.java
new file mode 100644
index 00000000..c6ba34c4
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NetworkStructureChangeSensitiveNode.java
@@ -0,0 +1,30 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network;
10
11import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
12
13/**
14 * {@link Node}s implementing this interface are sensitive to changes in the dependency graph maintained by the
15 * {@link CommunicationTracker}. The {@link CommunicationTracker} notifies these nodes whenever the SCC of this node is
16 * affected by changes to the dependency graph. Depending on whether this node is contained in a recursive group or not,
17 * it may behave differently, and the {@link NetworkStructureChangeSensitiveNode#networkStructureChanged()} method can
18 * be used to perform changes in behavior.
19 *
20 * @author Tamas Szabo
21 * @since 2.3
22 */
23public interface NetworkStructureChangeSensitiveNode extends Node {
24
25 /**
26 * At the time of the invocation, the dependency graph has already been updated.
27 */
28 public void networkStructureChanged();
29
30}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Node.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Node.java
new file mode 100644
index 00000000..e8ab615a
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Node.java
@@ -0,0 +1,62 @@
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.rete.network;
11
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
15import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
16
17/**
18 * A node of a rete network, should be uniquely identified by network and nodeId. NodeId can be requested by registering
19 * at the Network on construction.
20 *
21 * @author Gabor Bergmann
22 */
23public interface Node {
24 /**
25 * @return the network this node belongs to.
26 */
27 ReteContainer getContainer();
28
29 /**
30 * @return the identifier unique to this node within the network.
31 */
32 long getNodeId();
33
34 /**
35 * Assigns a descriptive tag to the node
36 */
37 void setTag(Object tag);
38
39 /**
40 * @return the tag of the node
41 */
42 Object getTag();
43
44 /**
45 * @return unmodifiable view of the list of traceability infos assigned to this node
46 */
47 Set<TraceInfo> getTraceInfos();
48
49 /**
50 * assigns new traceability info to this node
51 */
52 void assignTraceInfo(TraceInfo traceInfo);
53 /**
54 * accepts traceability info propagated to this node
55 */
56 void acceptPropagatedTraceInfo(TraceInfo traceInfo);
57
58 default CommunicationTracker getCommunicationTracker() {
59 return getContainer().getCommunicationTracker();
60 }
61
62}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java
new file mode 100644
index 00000000..a40a8b7f
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java
@@ -0,0 +1,375 @@
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.rete.network;
10
11import org.apache.log4j.Logger;
12import org.eclipse.emf.common.util.EMap;
13import tools.refinery.viatra.runtime.base.itc.alg.representative.RepresentativeElectionAlgorithm;
14import tools.refinery.viatra.runtime.base.itc.alg.representative.StronglyConnectedComponentAlgorithm;
15import tools.refinery.viatra.runtime.base.itc.alg.representative.WeaklyConnectedComponentAlgorithm;
16import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
17import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator;
18import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator;
19import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
20import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
22import tools.refinery.viatra.runtime.rete.aggregation.ColumnAggregatorNode;
23import tools.refinery.viatra.runtime.rete.aggregation.CountNode;
24import tools.refinery.viatra.runtime.rete.aggregation.IAggregatorNode;
25import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode;
26import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode;
27import tools.refinery.viatra.runtime.rete.aggregation.timely.FirstOnlyParallelTimelyColumnAggregatorNode;
28import tools.refinery.viatra.runtime.rete.aggregation.timely.FirstOnlySequentialTimelyColumnAggregatorNode;
29import tools.refinery.viatra.runtime.rete.boundary.ExternalInputEnumeratorNode;
30import tools.refinery.viatra.runtime.rete.boundary.ExternalInputStatelessFilterNode;
31import tools.refinery.viatra.runtime.rete.eval.EvaluatorCore;
32import tools.refinery.viatra.runtime.rete.eval.MemorylessEvaluatorNode;
33import tools.refinery.viatra.runtime.rete.eval.OutputCachingEvaluatorNode;
34import tools.refinery.viatra.runtime.rete.eval.RelationEvaluatorNode;
35import tools.refinery.viatra.runtime.rete.index.ExistenceNode;
36import tools.refinery.viatra.runtime.rete.index.Indexer;
37import tools.refinery.viatra.runtime.rete.index.JoinNode;
38import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration;
39import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.AggregatorArchitecture;
40import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
41import tools.refinery.viatra.runtime.rete.misc.ConstantNode;
42import tools.refinery.viatra.runtime.rete.recipes.*;
43import tools.refinery.viatra.runtime.rete.single.*;
44import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
45
46import java.util.HashMap;
47import java.util.List;
48import java.util.Map;
49
50/**
51 * Factory for instantiating Rete nodes. The created nodes are not connected to the network yet.
52 *
53 * @author Bergmann Gabor
54 *
55 */
56class NodeFactory {
57 Logger logger;
58
59 public NodeFactory(Logger logger) {
60 super();
61 this.logger = logger;
62 }
63
64 /**
65 * PRE: parent node must already be created
66 */
67 public Indexer createIndexer(ReteContainer reteContainer, IndexerRecipe recipe, Supplier parentNode,
68 TraceInfo... traces) {
69
70 if (recipe instanceof ProjectionIndexerRecipe) {
71 return parentNode.constructIndex(toMask(recipe.getMask()), traces);
72 // already traced
73 } else if (recipe instanceof AggregatorIndexerRecipe) {
74 int indexOfAggregateResult = recipe.getParent().getArity();
75 int resultPosition = recipe.getMask().getSourceIndices().lastIndexOf(indexOfAggregateResult);
76
77 IAggregatorNode aggregatorNode = (IAggregatorNode) parentNode;
78 final Indexer result = (resultPosition == -1) ? aggregatorNode.getAggregatorOuterIndexer()
79 : aggregatorNode.getAggregatorOuterIdentityIndexer(resultPosition);
80
81 for (TraceInfo traceInfo : traces)
82 result.assignTraceInfo(traceInfo);
83 return result;
84 } else
85 throw new IllegalArgumentException("Unkown Indexer recipe: " + recipe);
86 }
87
88 /**
89 * PRE: recipe is not an indexer recipe.
90 */
91 public Supplier createNode(ReteContainer reteContainer, ReteNodeRecipe recipe, TraceInfo... traces) {
92 if (recipe instanceof IndexerRecipe)
93 throw new IllegalArgumentException("Indexers are not created by NodeFactory: " + recipe);
94
95 Supplier result = instantiateNodeDispatch(reteContainer, recipe);
96 for (TraceInfo traceInfo : traces)
97 result.assignTraceInfo(traceInfo);
98 return result;
99 }
100
101 private Supplier instantiateNodeDispatch(ReteContainer reteContainer, ReteNodeRecipe recipe) {
102
103 // Parentless
104
105 if (recipe instanceof ConstantRecipe)
106 return instantiateNode(reteContainer, (ConstantRecipe) recipe);
107 if (recipe instanceof InputRecipe)
108 return instantiateNode(reteContainer, (InputRecipe) recipe);
109
110 // SingleParentNodeRecipe
111
112 // if (recipe instanceof ProjectionIndexer)
113 // return instantiateNode((ProjectionIndexer)recipe);
114 if (recipe instanceof InputFilterRecipe)
115 return instantiateNode(reteContainer, (InputFilterRecipe) recipe);
116 if (recipe instanceof InequalityFilterRecipe)
117 return instantiateNode(reteContainer, (InequalityFilterRecipe) recipe);
118 if (recipe instanceof EqualityFilterRecipe)
119 return instantiateNode(reteContainer, (EqualityFilterRecipe) recipe);
120 if (recipe instanceof TransparentRecipe)
121 return instantiateNode(reteContainer, (TransparentRecipe) recipe);
122 if (recipe instanceof TrimmerRecipe)
123 return instantiateNode(reteContainer, (TrimmerRecipe) recipe);
124 if (recipe instanceof TransitiveClosureRecipe)
125 return instantiateNode(reteContainer, (TransitiveClosureRecipe) recipe);
126 if (recipe instanceof RepresentativeElectionRecipe)
127 return instantiateNode(reteContainer, (RepresentativeElectionRecipe) recipe);
128 if (recipe instanceof RelationEvaluationRecipe)
129 return instantiateNode(reteContainer, (RelationEvaluationRecipe) recipe);
130 if (recipe instanceof ExpressionEnforcerRecipe)
131 return instantiateNode(reteContainer, (ExpressionEnforcerRecipe) recipe);
132 if (recipe instanceof CountAggregatorRecipe)
133 return instantiateNode(reteContainer, (CountAggregatorRecipe) recipe);
134 if (recipe instanceof SingleColumnAggregatorRecipe)
135 return instantiateNode(reteContainer, (SingleColumnAggregatorRecipe) recipe);
136 if (recipe instanceof DiscriminatorDispatcherRecipe)
137 return instantiateNode(reteContainer, (DiscriminatorDispatcherRecipe) recipe);
138 if (recipe instanceof DiscriminatorBucketRecipe)
139 return instantiateNode(reteContainer, (DiscriminatorBucketRecipe) recipe);
140
141 // MultiParentNodeRecipe
142 if (recipe instanceof UniquenessEnforcerRecipe)
143 return instantiateNode(reteContainer, (UniquenessEnforcerRecipe) recipe);
144 if (recipe instanceof ProductionRecipe)
145 return instantiateNode(reteContainer, (ProductionRecipe) recipe);
146
147 // BetaNodeRecipe
148 if (recipe instanceof JoinRecipe)
149 return instantiateNode(reteContainer, (JoinRecipe) recipe);
150 if (recipe instanceof SemiJoinRecipe)
151 return instantiateNode(reteContainer, (SemiJoinRecipe) recipe);
152 if (recipe instanceof AntiJoinRecipe)
153 return instantiateNode(reteContainer, (AntiJoinRecipe) recipe);
154
155 // ... else
156 throw new IllegalArgumentException("Unsupported recipe type: " + recipe);
157 }
158
159 // INSTANTIATION for recipe types
160
161 private Supplier instantiateNode(ReteContainer reteContainer, InputRecipe recipe) {
162 return new ExternalInputEnumeratorNode(reteContainer);
163 }
164
165 private Supplier instantiateNode(ReteContainer reteContainer, InputFilterRecipe recipe) {
166 return new ExternalInputStatelessFilterNode(reteContainer, toMaskOrNull(recipe.getMask()));
167 }
168
169 private Supplier instantiateNode(ReteContainer reteContainer, CountAggregatorRecipe recipe) {
170 return new CountNode(reteContainer);
171 }
172
173 private Supplier instantiateNode(ReteContainer reteContainer, TransparentRecipe recipe) {
174 return new TransparentNode(reteContainer);
175 }
176
177 private Supplier instantiateNode(ReteContainer reteContainer, ExpressionEnforcerRecipe recipe) {
178 final IExpressionEvaluator evaluator = toIExpressionEvaluator(recipe.getExpression());
179 final Map<String, Integer> posMapping = toStringIndexMap(recipe.getMappedIndices());
180 final int sourceTupleWidth = recipe.getParent().getArity();
181 EvaluatorCore core = null;
182 if (recipe instanceof CheckRecipe) {
183 core = new EvaluatorCore.PredicateEvaluatorCore(logger, evaluator, posMapping, sourceTupleWidth);
184 } else if (recipe instanceof EvalRecipe) {
185 final boolean isUnwinding = ((EvalRecipe) recipe).isUnwinding();
186 core = new EvaluatorCore.FunctionEvaluatorCore(logger, evaluator, posMapping, sourceTupleWidth, isUnwinding);
187 } else {
188 throw new IllegalArgumentException("Unhandled expression enforcer recipe: " + recipe.getClass() + "!");
189 }
190 if (recipe.isCacheOutput()) {
191 return new OutputCachingEvaluatorNode(reteContainer, core);
192 } else {
193 return new MemorylessEvaluatorNode(reteContainer, core);
194 }
195 }
196
197 @SuppressWarnings({ "rawtypes", "unchecked" })
198 private Supplier instantiateNode(ReteContainer reteContainer, SingleColumnAggregatorRecipe recipe) {
199 final IMultisetAggregationOperator operator = recipe.getMultisetAggregationOperator();
200 TupleMask coreMask = null;
201 if (recipe.getOptionalMonotonicityInfo() != null) {
202 coreMask = toMask(recipe.getOptionalMonotonicityInfo().getCoreMask());
203 } else {
204 coreMask = toMask(recipe.getGroupByMask());
205 }
206
207 if (reteContainer.isTimelyEvaluation()) {
208 final TimelyConfiguration timelyConfiguration = reteContainer.getTimelyConfiguration();
209 final AggregatorArchitecture aggregatorArchitecture = timelyConfiguration.getAggregatorArchitecture();
210 final TimelineRepresentation timelineRepresentation = timelyConfiguration.getTimelineRepresentation();
211
212 TupleMask posetMask = null;
213
214 if (recipe.getOptionalMonotonicityInfo() != null) {
215 posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask());
216 } else {
217 final int aggregatedColumn = recipe.getAggregableIndex();
218 posetMask = TupleMask.selectSingle(aggregatedColumn, coreMask.sourceWidth);
219 }
220
221 if (timelineRepresentation == TimelineRepresentation.FIRST_ONLY
222 && aggregatorArchitecture == AggregatorArchitecture.SEQUENTIAL) {
223 return new FirstOnlySequentialTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask);
224 } else if (timelineRepresentation == TimelineRepresentation.FIRST_ONLY
225 && aggregatorArchitecture == AggregatorArchitecture.PARALLEL) {
226 return new FirstOnlyParallelTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask);
227 } else if (timelineRepresentation == TimelineRepresentation.FAITHFUL
228 && aggregatorArchitecture == AggregatorArchitecture.SEQUENTIAL) {
229 return new FaithfulSequentialTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask);
230 } else if (timelineRepresentation == TimelineRepresentation.FAITHFUL
231 && aggregatorArchitecture == AggregatorArchitecture.PARALLEL) {
232 return new FaithfulParallelTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask);
233 } else {
234 throw new IllegalArgumentException("Unsupported timely configuration!");
235 }
236 } else if (recipe.isDeleteRederiveEvaluation() && recipe.getOptionalMonotonicityInfo() != null) {
237 final TupleMask posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask());
238 final IPosetComparator posetComparator = (IPosetComparator) recipe.getOptionalMonotonicityInfo()
239 .getPosetComparator();
240 return new ColumnAggregatorNode(reteContainer, operator, recipe.isDeleteRederiveEvaluation(), coreMask,
241 posetMask, posetComparator);
242 } else {
243 final int aggregatedColumn = recipe.getAggregableIndex();
244 return new ColumnAggregatorNode(reteContainer, operator, coreMask, aggregatedColumn);
245 }
246 }
247
248 private Supplier instantiateNode(ReteContainer reteContainer, TransitiveClosureRecipe recipe) {
249 return new TransitiveClosureNode(reteContainer);
250 }
251
252 private Supplier instantiateNode(ReteContainer reteContainer, RepresentativeElectionRecipe recipe) {
253 RepresentativeElectionAlgorithm.Factory algorithmFactory = switch (recipe.getConnectivity()) {
254 case STRONG -> StronglyConnectedComponentAlgorithm::new;
255 case WEAK -> WeaklyConnectedComponentAlgorithm::new;
256 };
257 return new RepresentativeElectionNode(reteContainer, algorithmFactory);
258 }
259
260 private Supplier instantiateNode(ReteContainer reteContainer, RelationEvaluationRecipe recipe) {
261 return new RelationEvaluatorNode(reteContainer, toIRelationEvaluator(recipe.getEvaluator()));
262 }
263
264 private Supplier instantiateNode(ReteContainer reteContainer, ProductionRecipe recipe) {
265 if (reteContainer.isTimelyEvaluation()) {
266 return new TimelyProductionNode(reteContainer, toStringIndexMap(recipe.getMappedIndices()));
267 } else if (recipe.isDeleteRederiveEvaluation() && recipe.getOptionalMonotonicityInfo() != null) {
268 TupleMask coreMask = toMask(recipe.getOptionalMonotonicityInfo().getCoreMask());
269 TupleMask posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask());
270 IPosetComparator posetComparator = (IPosetComparator) recipe.getOptionalMonotonicityInfo()
271 .getPosetComparator();
272 return new DefaultProductionNode(reteContainer, toStringIndexMap(recipe.getMappedIndices()),
273 recipe.isDeleteRederiveEvaluation(), coreMask, posetMask, posetComparator);
274 } else {
275 return new DefaultProductionNode(reteContainer, toStringIndexMap(recipe.getMappedIndices()),
276 recipe.isDeleteRederiveEvaluation());
277 }
278 }
279
280 private Supplier instantiateNode(ReteContainer reteContainer, UniquenessEnforcerRecipe recipe) {
281 if (reteContainer.isTimelyEvaluation()) {
282 return new TimelyUniquenessEnforcerNode(reteContainer, recipe.getArity());
283 } else if (recipe.isDeleteRederiveEvaluation() && recipe.getOptionalMonotonicityInfo() != null) {
284 TupleMask coreMask = toMask(recipe.getOptionalMonotonicityInfo().getCoreMask());
285 TupleMask posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask());
286 IPosetComparator posetComparator = (IPosetComparator) recipe.getOptionalMonotonicityInfo()
287 .getPosetComparator();
288 return new UniquenessEnforcerNode(reteContainer, recipe.getArity(), recipe.isDeleteRederiveEvaluation(),
289 coreMask, posetMask, posetComparator);
290 } else {
291 return new UniquenessEnforcerNode(reteContainer, recipe.getArity(), recipe.isDeleteRederiveEvaluation());
292 }
293 }
294
295 private Supplier instantiateNode(ReteContainer reteContainer, ConstantRecipe recipe) {
296 final List<Object> constantValues = recipe.getConstantValues();
297 final Object[] constantArray = constantValues.toArray(new Object[constantValues.size()]);
298 return new ConstantNode(reteContainer, Tuples.flatTupleOf(constantArray));
299 }
300
301 private Supplier instantiateNode(ReteContainer reteContainer, DiscriminatorBucketRecipe recipe) {
302 return new DiscriminatorBucketNode(reteContainer, recipe.getBucketKey());
303 }
304
305 private Supplier instantiateNode(ReteContainer reteContainer, DiscriminatorDispatcherRecipe recipe) {
306 return new DiscriminatorDispatcherNode(reteContainer, recipe.getDiscriminationColumnIndex());
307 }
308
309 private Supplier instantiateNode(ReteContainer reteContainer, TrimmerRecipe recipe) {
310 return new TrimmerNode(reteContainer, toMask(recipe.getMask()));
311 }
312
313 private Supplier instantiateNode(ReteContainer reteContainer, InequalityFilterRecipe recipe) {
314 Tunnel result = new InequalityFilterNode(reteContainer, recipe.getSubject(),
315 TupleMask.fromSelectedIndices(recipe.getParent().getArity(), recipe.getInequals()));
316 return result;
317 }
318
319 private Supplier instantiateNode(ReteContainer reteContainer, EqualityFilterRecipe recipe) {
320 final int[] equalIndices = TupleMask.integersToIntArray(recipe.getIndices());
321 return new EqualityFilterNode(reteContainer, equalIndices);
322 }
323
324 private Supplier instantiateNode(ReteContainer reteContainer, AntiJoinRecipe recipe) {
325 return new ExistenceNode(reteContainer, true);
326 }
327
328 private Supplier instantiateNode(ReteContainer reteContainer, SemiJoinRecipe recipe) {
329 return new ExistenceNode(reteContainer, false);
330 }
331
332 private Supplier instantiateNode(ReteContainer reteContainer, JoinRecipe recipe) {
333 return new JoinNode(reteContainer, toMask(recipe.getRightParentComplementaryMask()));
334 }
335
336 // HELPERS
337
338 private IExpressionEvaluator toIExpressionEvaluator(ExpressionDefinition expressionDefinition) {
339 final Object evaluator = expressionDefinition.getEvaluator();
340 if (evaluator instanceof IExpressionEvaluator) {
341 return (IExpressionEvaluator) evaluator;
342 }
343 throw new IllegalArgumentException("No runtime support for expression evaluator: " + evaluator);
344 }
345
346 private IRelationEvaluator toIRelationEvaluator(ExpressionDefinition expressionDefinition) {
347 final Object evaluator = expressionDefinition.getEvaluator();
348 if (evaluator instanceof IRelationEvaluator) {
349 return (IRelationEvaluator) evaluator;
350 }
351 throw new IllegalArgumentException("No runtime support for relation evaluator: " + evaluator);
352 }
353
354 private Map<String, Integer> toStringIndexMap(final EMap<String, Integer> mappedIndices) {
355 final HashMap<String, Integer> result = new HashMap<String, Integer>();
356 for (java.util.Map.Entry<String, Integer> entry : mappedIndices) {
357 result.put(entry.getKey(), entry.getValue());
358 }
359 return result;
360 }
361
362 /** Mask can be null */
363 private TupleMask toMaskOrNull(Mask mask) {
364 if (mask == null)
365 return null;
366 else
367 return toMask(mask);
368 }
369
370 /** Mask is non-null. */
371 private TupleMask toMask(Mask mask) {
372 return TupleMask.fromSelectedIndices(mask.getSourceArity(), mask.getSourceIndices());
373 }
374
375}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java
new file mode 100644
index 00000000..9121fc44
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java
@@ -0,0 +1,346 @@
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.rete.network;
11
12import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
15import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
16import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
17import tools.refinery.viatra.runtime.rete.boundary.InputConnector;
18import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper;
19import tools.refinery.viatra.runtime.rete.index.Indexer;
20import tools.refinery.viatra.runtime.rete.index.OnetimeIndexer;
21import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
22import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand;
23import tools.refinery.viatra.runtime.rete.recipes.*;
24import tools.refinery.viatra.runtime.rete.recipes.helper.RecipeRecognizer;
25import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper;
26import tools.refinery.viatra.runtime.rete.remote.Address;
27import tools.refinery.viatra.runtime.rete.remote.RemoteReceiver;
28import tools.refinery.viatra.runtime.rete.remote.RemoteSupplier;
29import tools.refinery.viatra.runtime.rete.traceability.ActiveNodeConflictTrace;
30import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
31import tools.refinery.viatra.runtime.rete.traceability.UserRequestTrace;
32import tools.refinery.viatra.runtime.rete.util.Options;
33
34import java.util.Map;
35import java.util.Set;
36
37/**
38 * Stores the internal parts of a rete network. Nodes are stored according to type and parameters.
39 *
40 * @author Gabor Bergmann
41 */
42public class NodeProvisioner {
43
44 // boolean activeStorage = true;
45
46 ReteContainer reteContainer;
47 NodeFactory nodeFactory;
48 ConnectionFactory connectionFactory;
49 InputConnector inputConnector;
50 IQueryRuntimeContext runtimeContext;
51
52 // TODO as recipe?
53 Map<Supplier, RemoteReceiver> remoteReceivers = CollectionsFactory.createMap();
54 Map<Address<? extends Supplier>, RemoteSupplier> remoteSuppliers = CollectionsFactory.createMap();
55
56 private RecipeRecognizer recognizer;
57
58 /**
59 * PRE: NodeFactory, ConnectionFactory must exist
60 *
61 * @param reteContainer
62 * the ReteNet whose interior is to be mapped.
63 */
64 public NodeProvisioner(ReteContainer reteContainer) {
65 super();
66 this.reteContainer = reteContainer;
67 this.nodeFactory = reteContainer.getNodeFactory();
68 this.connectionFactory = reteContainer.getConnectionFactory();
69 this.inputConnector = reteContainer.getInputConnectionFactory();
70 runtimeContext = reteContainer.getNetwork().getEngine().getRuntimeContext();
71 recognizer = new RecipeRecognizer(runtimeContext);
72 }
73
74 public synchronized Address<? extends Node> getOrCreateNodeByRecipe(RecipeTraceInfo recipeTrace) {
75 ReteNodeRecipe recipe = recipeTrace.getRecipe();
76 Address<? extends Node> result = getNodesByRecipe().get(recipe);
77 if (result != null) {
78 // NODE ALREADY CONSTRUCTED FOR RECIPE, only needs to add trace
79 if (getRecipeTraces().add(recipeTrace))
80 result.getNodeCache().assignTraceInfo(recipeTrace);
81 } else {
82 // No node for this recipe object - but equivalent recipes still
83 // reusable
84 ReteNodeRecipe canonicalRecipe = recognizer.canonicalizeRecipe(recipe);
85 if (canonicalRecipe != recipe) {
86 // FOUND EQUIVALENT RECIPE
87 result = getNodesByRecipe().get(canonicalRecipe);
88 if (result != null) {
89 // NODE ALREADY CONSTRUCTED FOR EQUIVALENT RECIPE
90 recipeTrace.shadowWithEquivalentRecipe(canonicalRecipe);
91 getNodesByRecipe().put(recipe, result);
92 if (getRecipeTraces().add(recipeTrace))
93 result.getNodeCache().assignTraceInfo(recipeTrace);
94 // Bug 491922: ensure that recipe shadowing propagates to
95 // parent traces
96 // note that if equivalentRecipes() becomes more
97 // sophisticated
98 // and considers recipes with different parents, this might
99 // have to be changed
100 ensureParents(recipeTrace);
101 } else {
102 // CONSTRUCTION IN PROGRESS FOR EQUIVALENT RECIPE
103 if (recipe instanceof IndexerRecipe) {
104 // this is allowed for indexers;
105 // go on with the construction, as the same indexer node
106 // will be obtained anyways
107 } else {
108 throw new IllegalStateException(
109 "This should not happen: " + "non-indexer nodes are are supposed to be constructed "
110 + "as soon as they are designated as canonical recipes");
111 }
112 }
113 }
114 if (result == null) {
115 // MUST INSTANTIATE NEW NODE FOR RECIPE
116 final Node freshNode = instantiateNodeForRecipe(recipeTrace, recipe);
117 result = reteContainer.makeAddress(freshNode);
118 }
119 }
120 return result;
121 }
122
123 private Set<RecipeTraceInfo> getRecipeTraces() {
124 return reteContainer.network.recipeTraces;
125 }
126
127 private Node instantiateNodeForRecipe(RecipeTraceInfo recipeTrace, final ReteNodeRecipe recipe) {
128 this.getRecipeTraces().add(recipeTrace);
129 if (recipe instanceof IndexerRecipe) {
130
131 // INSTANTIATE AND HOOK UP
132 // (cannot delay hooking up, because parent determines indexer
133 // implementation)
134 ensureParents(recipeTrace);
135 final ReteNodeRecipe parentRecipe = recipeTrace.getParentRecipeTraces().iterator().next().getRecipe();
136 final Indexer result = nodeFactory.createIndexer(reteContainer, (IndexerRecipe) recipe,
137 asSupplier(
138 (Address<? extends Supplier>) reteContainer.network.getExistingNodeByRecipe(parentRecipe)),
139 recipeTrace);
140
141 // REMEMBER
142 if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) {
143 getNodesByRecipe().put(recipe, reteContainer.makeAddress(result));
144 }
145
146 return result;
147 } else {
148
149 // INSTANTIATE
150 Node result = nodeFactory.createNode(reteContainer, recipe, recipeTrace);
151
152 // REMEMBER
153 if (Options.nodeSharingOption == Options.NodeSharingOption.ALL) {
154 getNodesByRecipe().put(recipe, reteContainer.makeAddress(result));
155 }
156
157 // HOOK UP
158 // (recursion-tolerant due to this delayed order of initialization)
159 if (recipe instanceof InputRecipe) {
160 inputConnector.connectInput((InputRecipe) recipe, result);
161 } else {
162 if (recipe instanceof InputFilterRecipe)
163 inputConnector.connectInputFilter((InputFilterRecipe) recipe, result);
164 ensureParents(recipeTrace);
165 connectionFactory.connectToParents(recipeTrace, result);
166 }
167 return result;
168 }
169 }
170
171 private Map<ReteNodeRecipe, Address<? extends Node>> getNodesByRecipe() {
172 return reteContainer.network.nodesByRecipe;
173 }
174
175 private void ensureParents(RecipeTraceInfo recipeTrace) {
176 for (RecipeTraceInfo parentTrace : recipeTrace.getParentRecipeTraces()) {
177 getOrCreateNodeByRecipe(parentTrace);
178 }
179 }
180
181 //// Remoting - TODO eliminate?
182
183 synchronized RemoteReceiver accessRemoteReceiver(Address<? extends Supplier> address) {
184 throw new UnsupportedOperationException("Multi-container Rete not supported yet");
185 // if (!reteContainer.isLocal(address))
186 // return
187 // address.getContainer().getProvisioner().accessRemoteReceiver(address);
188 // Supplier localSupplier = reteContainer.resolveLocal(address);
189 // RemoteReceiver result = remoteReceivers.get(localSupplier);
190 // if (result == null) {
191 // result = new RemoteReceiver(reteContainer);
192 // reteContainer.connect(localSupplier, result); // stateless node, no
193 // // synch required
194 //
195 // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER)
196 // remoteReceivers.put(localSupplier, result);
197 // }
198 // return result;
199 }
200
201 /**
202 * @pre: address is NOT local
203 */
204 synchronized RemoteSupplier accessRemoteSupplier(Address<? extends Supplier> address) {
205 throw new UnsupportedOperationException("Multi-container Rete not supported yet");
206 // RemoteSupplier result = remoteSuppliers.get(address);
207 // if (result == null) {
208 // result = new RemoteSupplier(reteContainer,
209 // address.getContainer().getProvisioner()
210 // .accessRemoteReceiver(address));
211 // // network.connectAndSynchronize(supplier, result);
212 //
213 // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER)
214 // remoteSuppliers.put(address, result);
215 // }
216 // return result;
217 }
218
219 /**
220 * The powerful method for accessing any (supplier) Address as a local supplier.
221 */
222 public Supplier asSupplier(Address<? extends Supplier> address) {
223 if (!reteContainer.isLocal(address))
224 return accessRemoteSupplier(address);
225 else
226 return reteContainer.resolveLocal(address);
227 }
228
229 /** the composite key tuple is formed as (RecipeTraceInfo, TupleMask) */
230 private Map<Tuple, UserRequestTrace> projectionIndexerUserRequests = CollectionsFactory.createMap();
231
232 // local version
233 // TODO remove?
234 public synchronized ProjectionIndexer accessProjectionIndexer(RecipeTraceInfo productionTrace, TupleMask mask) {
235 Tuple tableKey = Tuples.staticArityFlatTupleOf(productionTrace, mask);
236 UserRequestTrace indexerTrace = projectionIndexerUserRequests.computeIfAbsent(tableKey, k -> {
237 final ProjectionIndexerRecipe projectionIndexerRecipe = projectionIndexerRecipe(
238 productionTrace, mask);
239 return new UserRequestTrace(projectionIndexerRecipe, productionTrace);
240 });
241 final Address<? extends Node> address = getOrCreateNodeByRecipe(indexerTrace);
242 return (ProjectionIndexer) reteContainer.resolveLocal(address);
243 }
244
245 // local version
246 public synchronized ProjectionIndexer accessProjectionIndexerOnetime(RecipeTraceInfo supplierTrace,
247 TupleMask mask) {
248 if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER)
249 return accessProjectionIndexer(supplierTrace, mask);
250
251 final Address<? extends Node> supplierAddress = getOrCreateNodeByRecipe(supplierTrace);
252 Supplier supplier = (Supplier) reteContainer.resolveLocal(supplierAddress);
253
254 OnetimeIndexer result = new OnetimeIndexer(reteContainer, mask);
255 reteContainer.getDelayedCommandQueue().add(new DelayedConnectCommand(supplier, result, reteContainer));
256
257 return result;
258 }
259
260 // local, read-only version
261 public synchronized ProjectionIndexer peekProjectionIndexer(RecipeTraceInfo supplierTrace, TupleMask mask) {
262 final Address<? extends Node> address = getNodesByRecipe().get(projectionIndexerRecipe(supplierTrace, mask));
263 return address == null ? null : (ProjectionIndexer) reteContainer.resolveLocal(address);
264 }
265
266 private ProjectionIndexerRecipe projectionIndexerRecipe(
267 RecipeTraceInfo parentTrace, TupleMask mask) {
268 final ReteNodeRecipe parentRecipe = parentTrace.getRecipe();
269 Tuple tableKey = Tuples.staticArityFlatTupleOf(parentRecipe, mask);
270 ProjectionIndexerRecipe projectionIndexerRecipe = resultSeedRecipes.computeIfAbsent(tableKey, k ->
271 RecipesHelper.projectionIndexerRecipe(parentRecipe, CompilerHelper.toRecipeMask(mask))
272 );
273 return projectionIndexerRecipe;
274 }
275
276 /** the composite key tuple is formed as (ReteNodeRecipe, TupleMask) */
277 private Map<Tuple, ProjectionIndexerRecipe> resultSeedRecipes = CollectionsFactory.createMap();
278
279 // public synchronized Address<? extends Supplier>
280 // accessValueBinderFilterNode(
281 // Address<? extends Supplier> supplierAddress, int bindingIndex, Object
282 // bindingValue) {
283 // Supplier supplier = asSupplier(supplierAddress);
284 // Object[] paramsArray = { supplier.getNodeId(), bindingIndex, bindingValue
285 // };
286 // Tuple params = new FlatTuple(paramsArray);
287 // ValueBinderFilterNode result = valueBinderFilters.get(params);
288 // if (result == null) {
289 // result = new ValueBinderFilterNode(reteContainer, bindingIndex,
290 // bindingValue);
291 // reteContainer.connect(supplier, result); // stateless node, no synch
292 // // required
293 //
294 // if (Options.nodeSharingOption == Options.NodeSharingOption.ALL)
295 // valueBinderFilters.put(params, result);
296 // }
297 // return reteContainer.makeAddress(result);
298 // }
299
300 /**
301 * Returns a copy of the given indexer that is an active node by itself (created if does not exist). (Convention:
302 * attached with same mask to a transparent node that is attached to parent node.) Node is created if it does not
303 * exist yet.
304 *
305 * @return an identical but active indexer
306 */
307 // TODO rethink traceability
308 RecipeTraceInfo accessActiveIndexer(RecipeTraceInfo inactiveIndexerRecipeTrace) {
309 final RecipeTraceInfo parentRecipeTrace = inactiveIndexerRecipeTrace.getParentRecipeTraces().iterator().next();
310 final ProjectionIndexerRecipe inactiveIndexerRecipe = (ProjectionIndexerRecipe) inactiveIndexerRecipeTrace
311 .getRecipe();
312
313 final TransparentRecipe transparentRecipe = RecipesFactory.eINSTANCE.createTransparentRecipe();
314 transparentRecipe.setParent(parentRecipeTrace.getRecipe());
315 final ActiveNodeConflictTrace transparentRecipeTrace = new ActiveNodeConflictTrace(transparentRecipe,
316 parentRecipeTrace, inactiveIndexerRecipeTrace);
317
318 final ProjectionIndexerRecipe activeIndexerRecipe = RecipesFactory.eINSTANCE
319 .createProjectionIndexerRecipe();
320 activeIndexerRecipe.setParent(transparentRecipe);
321 activeIndexerRecipe.setMask(inactiveIndexerRecipe.getMask());
322 final ActiveNodeConflictTrace activeIndexerRecipeTrace = new ActiveNodeConflictTrace(activeIndexerRecipe,
323 transparentRecipeTrace, inactiveIndexerRecipeTrace);
324
325 return activeIndexerRecipeTrace;
326 }
327
328 // /**
329 // * @param parent
330 // * @return
331 // */
332 // private TransparentNode accessTransparentNodeInternal(Supplier parent) {
333 // nodeFactory.
334 // return null;
335 // }
336
337 // public synchronized void registerSpecializedProjectionIndexer(Node node,
338 // ProjectionIndexer indexer) {
339 // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) {
340 // Object[] paramsArray = { node.getNodeId(), indexer.getMask() };
341 // Tuple params = new FlatTuple(paramsArray);
342 // projectionIndexers.put(params, indexer);
343 // }
344 // }
345
346}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/PosetAwareReceiver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/PosetAwareReceiver.java
new file mode 100644
index 00000000..1eaa18e7
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/PosetAwareReceiver.java
@@ -0,0 +1,39 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.rete.network;
10
11import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
14import tools.refinery.viatra.runtime.matchers.util.Direction;
15
16/**
17 * @author Tamas Szabo
18 * @since 2.0
19 */
20public interface PosetAwareReceiver extends Receiver {
21
22 public TupleMask getCoreMask();
23
24 public TupleMask getPosetMask();
25
26 public IPosetComparator getPosetComparator();
27
28 /**
29 * Updates the receiver with a newly found or lost partial matching also providing information
30 * whether the update is a monotone change or not.
31 *
32 * @param direction the direction of the update
33 * @param update the update tuple
34 * @param monotone true if the update is monotone, false otherwise
35 * @since 2.4
36 */
37 public void updateWithPosetInfo(Direction direction, Tuple update, boolean monotone);
38
39}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ProductionNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ProductionNode.java
new file mode 100644
index 00000000..211194c0
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ProductionNode.java
@@ -0,0 +1,28 @@
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.rete.network;
11
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15
16/**
17 * Interface intended for nodes containing complete matches.
18 *
19 * @author Gabor Bergmann
20 */
21public interface ProductionNode extends Tunnel, Iterable<Tuple> {
22
23 /**
24 * @return the position mapping of this particular pattern that maps members of the tuple type to their positions
25 */
26 Map<String, Integer> getPosMapping();
27
28}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Receiver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Receiver.java
new file mode 100644
index 00000000..3dc9aad7
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Receiver.java
@@ -0,0 +1,85 @@
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.rete.network;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Map.Entry;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
19import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
20
21/**
22 * ALL METHODS: FOR INTERNAL USE ONLY; ONLY INVOKE FROM {@link ReteContainer}
23 *
24 * @author Gabor Bergmann
25 * @noimplement This interface is not intended to be implemented by external clients.
26 */
27public interface Receiver extends Node {
28
29 /**
30 * Updates the receiver with a newly found or lost partial matching.
31 *
32 * @since 2.4
33 */
34 public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp);
35
36 /**
37 * Updates the receiver in batch style with a collection of updates. The input collection consists of pairs in the
38 * form (t, c) where t is an update tuple and c is the count. The count can also be negative, and it specifies how
39 * many times the tuple t gets deleted or inserted. The default implementation of this method simply calls
40 * {@link #update(Direction, Tuple, Timestamp)} individually for all updates.
41 *
42 * @since 2.8
43 */
44 public default void batchUpdate(final Collection<Map.Entry<Tuple, Integer>> updates, final Timestamp timestamp) {
45 for (final Entry<Tuple, Integer> entry : updates) {
46 int count = entry.getValue();
47
48 Direction direction;
49 if (count < 0) {
50 direction = Direction.DELETE;
51 count = -count;
52 } else {
53 direction = Direction.INSERT;
54 }
55
56 for (int i = 0; i < count; i++) {
57 update(direction, entry.getKey(), timestamp);
58 }
59 }
60 }
61
62 /**
63 * Returns the {@link Mailbox} of this receiver.
64 *
65 * @return the mailbox
66 * @since 2.0
67 */
68 public Mailbox getMailbox();
69
70 /**
71 * appends a parent that will continuously send insert and revoke updates to this supplier
72 */
73 void appendParent(final Supplier supplier);
74
75 /**
76 * removes a parent
77 */
78 void removeParent(final Supplier supplier);
79
80 /**
81 * access active parent
82 */
83 Collection<Supplier> getParents();
84
85}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/RederivableNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/RederivableNode.java
new file mode 100644
index 00000000..cae78d37
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/RederivableNode.java
@@ -0,0 +1,34 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.rete.network;
10
11/**
12 * A rederivable node can potentially re-derive tuples after the Rete network has finished the delivery of messages.
13 *
14 * @author Tamas Szabo
15 * @since 1.6
16 */
17public interface RederivableNode extends Node, IGroupable {
18
19 /**
20 * The method is called by the {@link ReteContainer} to re-derive tuples after the normal messages have been
21 * delivered and consumed. The re-derivation process may trigger the creation and delivery of further messages
22 * and further re-derivation rounds.
23 */
24 public void rederiveOne();
25
26 /**
27 * Returns true if this node actually runs in DRed mode (not necessarily).
28 *
29 * @return true if the node is operating in DRed mode
30 * @since 2.0
31 */
32 public boolean isInDRedMode();
33
34}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReinitializedNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReinitializedNode.java
new file mode 100644
index 00000000..09bff29e
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReinitializedNode.java
@@ -0,0 +1,14 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.viatra.runtime.rete.network;
7
8import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
9
10import java.util.Collection;
11
12public interface ReinitializedNode {
13 void reinitializeWith(Collection<Tuple> tuples);
14}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java
new file mode 100644
index 00000000..16e290fd
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java
@@ -0,0 +1,729 @@
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.rete.network;
11
12import java.util.ArrayDeque;
13import java.util.ArrayList;
14import java.util.Collection;
15import java.util.Deque;
16import java.util.HashSet;
17import java.util.LinkedHashSet;
18import java.util.LinkedList;
19import java.util.Map;
20import java.util.Set;
21import java.util.function.Function;
22
23import org.apache.log4j.Logger;
24import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
25import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
26import tools.refinery.viatra.runtime.matchers.util.Clearable;
27import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
28import tools.refinery.viatra.runtime.matchers.util.Direction;
29import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
30import tools.refinery.viatra.runtime.rete.boundary.InputConnector;
31import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration;
32import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
33import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
34import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
35import tools.refinery.viatra.runtime.rete.network.communication.timeless.TimelessCommunicationTracker;
36import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyCommunicationTracker;
37import tools.refinery.viatra.runtime.rete.network.delayed.DelayedCommand;
38import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand;
39import tools.refinery.viatra.runtime.rete.network.delayed.DelayedDisconnectCommand;
40import tools.refinery.viatra.runtime.rete.remote.Address;
41import tools.refinery.viatra.runtime.rete.single.SingleInputNode;
42import tools.refinery.viatra.runtime.rete.single.TrimmerNode;
43import tools.refinery.viatra.runtime.rete.util.Options;
44
45/**
46 * @author Gabor Bergmann
47 *
48 * Mutexes: externalMessageLock - enlisting messages into and retrieving from the external message queue
49 * @since 2.2
50 */
51public final class ReteContainer {
52
53 protected Thread consumerThread = null;
54 protected boolean killed = false;
55
56 protected Network network;
57
58 protected LinkedList<Clearable> clearables;
59 protected Map<Long, Node> nodesById;
60 protected long nextId = 0;
61
62 protected ConnectionFactory connectionFactory;
63 protected NodeProvisioner nodeProvisioner;
64
65 protected Deque<UpdateMessage> internalMessageQueue = new ArrayDeque<UpdateMessage>();
66 protected/* volatile */Deque<UpdateMessage> externalMessageQueue = new ArrayDeque<UpdateMessage>();
67 protected Object externalMessageLock = new Object();
68 protected Long clock = 1L; // even: steady state, odd: active queue; access
69 // ONLY with messageQueue locked!
70 protected Map<ReteContainer, Long> terminationCriteria = null;
71 protected final Logger logger;
72 protected final CommunicationTracker tracker;
73
74 protected final IQueryBackendContext backendContext;
75
76 protected Set<DelayedCommand> delayedCommandQueue;
77 protected Set<DelayedCommand> delayedCommandBuffer;
78 protected boolean executingDelayedCommands;
79
80 protected final TimelyConfiguration timelyConfiguration;
81
82 /**
83 * @param threaded
84 * false if operating in a single-threaded environment
85 */
86 public ReteContainer(Network network, boolean threaded) {
87 super();
88 this.network = network;
89 this.backendContext = network.getEngine().getBackendContext();
90 this.timelyConfiguration = network.getEngine().getTimelyConfiguration();
91
92 this.delayedCommandQueue = new LinkedHashSet<DelayedCommand>();
93 this.delayedCommandBuffer = new LinkedHashSet<DelayedCommand>();
94 this.executingDelayedCommands = false;
95
96 if (this.isTimelyEvaluation()) {
97 this.tracker = new TimelyCommunicationTracker(this.getTimelyConfiguration());
98 } else {
99 this.tracker = new TimelessCommunicationTracker();
100 }
101
102 this.nodesById = CollectionsFactory.createMap();
103 this.clearables = new LinkedList<Clearable>();
104 this.logger = network.getEngine().getLogger();
105
106 this.connectionFactory = new ConnectionFactory(this);
107 this.nodeProvisioner = new NodeProvisioner(this);
108
109 if (threaded) {
110 this.terminationCriteria = CollectionsFactory.createMap();
111 this.consumerThread = new Thread("Rete thread of " + ReteContainer.super.toString()) {
112 @Override
113 public void run() {
114 messageConsumptionCycle();
115 }
116 };
117 this.consumerThread.start();
118 }
119 }
120
121 /**
122 * @since 2.4
123 */
124 public boolean isTimelyEvaluation() {
125 return this.timelyConfiguration != null;
126 }
127
128 /**
129 * @since 2.4
130 */
131 public TimelyConfiguration getTimelyConfiguration() {
132 return this.timelyConfiguration;
133 }
134
135 /**
136 * @since 1.6
137 * @return the communication graph of the nodes, incl. message scheduling
138 */
139 public CommunicationTracker getCommunicationTracker() {
140 return tracker;
141 }
142
143 /**
144 * Stops this container. To be called by Network.kill()
145 */
146 public void kill() {
147 killed = true;
148 if (consumerThread != null)
149 consumerThread.interrupt();
150 }
151
152 /**
153 * Establishes connection between a supplier and a receiver node, regardless which container they are in. Assumption
154 * is that this container is the home of the receiver, but it is not strictly necessary.
155 *
156 * @param synchronise
157 * indicates whether the receiver should be synchronised to the current contents of the supplier
158 */
159 public void connectRemoteNodes(Address<? extends Supplier> supplier, Address<? extends Receiver> receiver,
160 boolean synchronise) {
161 if (!isLocal(receiver))
162 receiver.getContainer().connectRemoteNodes(supplier, receiver, synchronise);
163 else {
164 Receiver child = resolveLocal(receiver);
165 connectRemoteSupplier(supplier, child, synchronise);
166 }
167 }
168
169 /**
170 * Severs connection between a supplier and a receiver node, regardless which container they are in. Assumption is
171 * that this container is the home of the receiver, but it is not strictly necessary.
172 *
173 * @param desynchronise
174 * indicates whether the current contents of the supplier should be subtracted from the receiver
175 */
176 public void disconnectRemoteNodes(Address<? extends Supplier> supplier, Address<? extends Receiver> receiver,
177 boolean desynchronise) {
178 if (!isLocal(receiver))
179 receiver.getContainer().disconnectRemoteNodes(supplier, receiver, desynchronise);
180 else {
181 Receiver child = resolveLocal(receiver);
182 disconnectRemoteSupplier(supplier, child, desynchronise);
183 }
184 }
185
186 /**
187 * Establishes connection between a remote supplier and a local receiver node.
188 *
189 * @param synchronise
190 * indicates whether the receiver should be synchronised to the current contents of the supplier
191 */
192 public void connectRemoteSupplier(Address<? extends Supplier> supplier, Receiver receiver, boolean synchronise) {
193 Supplier parent = nodeProvisioner.asSupplier(supplier);
194 if (synchronise)
195 connectAndSynchronize(parent, receiver);
196 else
197 connect(parent, receiver);
198 }
199
200 /**
201 * Severs connection between a remote supplier and a local receiver node.
202 *
203 * @param desynchronise
204 * indicates whether the current contents of the supplier should be subtracted from the receiver
205 */
206 public void disconnectRemoteSupplier(Address<? extends Supplier> supplier, Receiver receiver,
207 boolean desynchronise) {
208 Supplier parent = nodeProvisioner.asSupplier(supplier);
209 if (desynchronise)
210 disconnectAndDesynchronize(parent, receiver);
211 else
212 disconnect(parent, receiver);
213 }
214
215 /**
216 * Connects a receiver to a supplier
217 */
218 public void connect(Supplier supplier, Receiver receiver) {
219 supplier.appendChild(receiver);
220 receiver.appendParent(supplier);
221 tracker.registerDependency(supplier, receiver);
222 }
223
224 /**
225 * Disconnects a receiver from a supplier
226 */
227 public void disconnect(Supplier supplier, Receiver receiver) {
228 supplier.removeChild(receiver);
229 receiver.removeParent(supplier);
230 tracker.unregisterDependency(supplier, receiver);
231 }
232
233 /**
234 * @since 2.3
235 */
236 public boolean isExecutingDelayedCommands() {
237 return this.executingDelayedCommands;
238 }
239
240 /**
241 * @since 2.3
242 */
243 public Set<DelayedCommand> getDelayedCommandQueue() {
244 if (this.executingDelayedCommands) {
245 return this.delayedCommandBuffer;
246 } else {
247 return this.delayedCommandQueue;
248 }
249 }
250
251 /**
252 * Connects a receiver to a remote supplier, and synchronizes it to the current contents of the supplier
253 */
254 public void connectAndSynchronize(Supplier supplier, Receiver receiver) {
255 supplier.appendChild(receiver);
256 receiver.appendParent(supplier);
257 tracker.registerDependency(supplier, receiver);
258 getDelayedCommandQueue().add(new DelayedConnectCommand(supplier, receiver, this));
259 }
260
261 /**
262 * Disconnects a receiver from a supplier
263 */
264 public void disconnectAndDesynchronize(Supplier supplier, Receiver receiver) {
265 final boolean wasInSameSCC = this.isTimelyEvaluation() && this.tracker.areInSameGroup(supplier, receiver);
266 supplier.removeChild(receiver);
267 receiver.removeParent(supplier);
268 tracker.unregisterDependency(supplier, receiver);
269 getDelayedCommandQueue().add(new DelayedDisconnectCommand(supplier, receiver, this, wasInSameSCC));
270 }
271
272 /**
273 * @since 2.3
274 */
275 public void executeDelayedCommands() {
276 if (!this.delayedCommandQueue.isEmpty()) {
277 flushUpdates();
278 this.executingDelayedCommands = true;
279 for (final DelayedCommand command : this.delayedCommandQueue) {
280 command.run();
281 }
282 this.delayedCommandQueue = this.delayedCommandBuffer;
283 this.delayedCommandBuffer = new LinkedHashSet<DelayedCommand>();
284 flushUpdates();
285 this.executingDelayedCommands = false;
286 }
287 }
288
289 /**
290 * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The receiver is
291 * indicated by the Address. Designed to be called by the Network, DO NOT use in any other way. @pre:
292 * address.container == this, e.g. address MUST be local
293 *
294 * @return the value of the container's clock at the time when the message was accepted into the local message queue
295 */
296 long sendUpdateToLocalAddress(Address<? extends Receiver> address, Direction direction, Tuple updateElement) {
297 long timestamp;
298 Receiver receiver = resolveLocal(address);
299 UpdateMessage message = new UpdateMessage(receiver, direction, updateElement);
300 synchronized (externalMessageLock) {
301 externalMessageQueue.add(message);
302 timestamp = clock;
303 externalMessageLock.notifyAll();
304 }
305
306 return timestamp;
307
308 }
309
310 /**
311 * Sends multiple update messages atomically to the receiver node, indicating a newly found or lost partial
312 * matching. The receiver is indicated by the Address. Designed to be called by the Network, DO NOT use in any other
313 * way. @pre: address.container == this, e.g. address MUST be local @pre: updateElements is nonempty!
314 *
315 * @return the value of the container's clock at the time when the message was accepted into the local message queue
316 */
317 long sendUpdatesToLocalAddress(Address<? extends Receiver> address, Direction direction,
318 Collection<Tuple> updateElements) {
319
320 long timestamp;
321 Receiver receiver = resolveLocal(address);
322 // UpdateMessage message = new UpdateMessage(receiver, direction,
323 // updateElement);
324 synchronized (externalMessageLock) {
325 for (Tuple ps : updateElements)
326 externalMessageQueue.add(new UpdateMessage(receiver, direction, ps));
327 // messageQueue.add(new UpdateMessage(resolveLocal(address),
328 // direction, updateElement));
329 // this.sendUpdateInternal(resolveLocal(address), direction,
330 // updateElement);
331 timestamp = clock;
332 externalMessageLock.notifyAll();
333 }
334
335 return timestamp;
336 }
337
338 /**
339 * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The receiver is
340 * indicated by the Address. Designed to be called by the Network in single-threaded operation, DO NOT use in any
341 * other way.
342 */
343 void sendUpdateToLocalAddressSingleThreaded(Address<? extends Receiver> address, Direction direction,
344 Tuple updateElement) {
345 Receiver receiver = resolveLocal(address);
346 UpdateMessage message = new UpdateMessage(receiver, direction, updateElement);
347 internalMessageQueue.add(message);
348 }
349
350 /**
351 * Sends multiple update messages to the receiver node, indicating a newly found or lost partial matching. The
352 * receiver is indicated by the Address. Designed to be called by the Network in single-threaded operation, DO NOT
353 * use in any other way.
354 *
355 * @pre: address.container == this, e.g. address MUST be local
356 */
357 void sendUpdatesToLocalAddressSingleThreaded(Address<? extends Receiver> address, Direction direction,
358 Collection<Tuple> updateElements) {
359 Receiver receiver = resolveLocal(address);
360 for (Tuple ps : updateElements)
361 internalMessageQueue.add(new UpdateMessage(receiver, direction, ps));
362 }
363
364 /**
365 * Sends an update message to a node in a different container. The receiver is indicated by the Address. Designed to
366 * be called by RemoteReceivers, DO NOT use in any other way.
367 *
368 * @since 2.4
369 */
370 public void sendUpdateToRemoteAddress(Address<? extends Receiver> address, Direction direction,
371 Tuple updateElement) {
372 ReteContainer otherContainer = address.getContainer();
373 long otherClock = otherContainer.sendUpdateToLocalAddress(address, direction, updateElement);
374 // Long criterion = terminationCriteria.get(otherContainer);
375 // if (criterion==null || otherClock > criterion)
376 terminationCriteria.put(otherContainer, otherClock);
377 }
378
379 /**
380 * Finalises all update sequences and returns. To be called from user threads (e.g. network construction).
381 */
382 public void flushUpdates() {
383 network.waitForReteTermination();
384 // synchronized (messageQueue)
385 // {
386 // while (!messageQueue.isEmpty())
387 // {
388 // try {
389 // UpdateMessage message = messageQueue.take();
390 // message.receiver.update(message.direction, message.updateElement);
391 // } catch (InterruptedException e) {}
392 // }
393 // }
394 }
395
396 /**
397 * Retrieves a safe copy of the contents of a supplier.
398 *
399 * <p> Note that there may be multiple copies of a Tuple in case of a {@link TrimmerNode}, so the result is not always a set.
400 *
401 * @param flush if true, a flush is performed before pulling the contents
402 * @since 2.3
403 */
404 public Collection<Tuple> pullContents(final Supplier supplier, final boolean flush) {
405 if (flush) {
406 flushUpdates();
407 }
408 final Collection<Tuple> collector = new ArrayList<Tuple>();
409 supplier.pullInto(collector, flush);
410 return collector;
411 }
412
413 /**
414 * @since 2.4
415 */
416 public Map<Tuple, Timeline<Timestamp>> pullContentsWithTimeline(final Supplier supplier, final boolean flush) {
417 if (flush) {
418 flushUpdates();
419 }
420 final Map<Tuple, Timeline<Timestamp>> collector = CollectionsFactory.createMap();
421 supplier.pullIntoWithTimeline(collector, flush);
422 return collector;
423 }
424
425 /**
426 * Retrieves the contents of a SingleInputNode's parentage.
427 *
428 * @since 2.3
429 */
430 public Collection<Tuple> pullPropagatedContents(final SingleInputNode supplier, final boolean flush) {
431 if (flush) {
432 flushUpdates();
433 }
434 final Collection<Tuple> collector = new LinkedList<Tuple>();
435 supplier.propagatePullInto(collector, flush);
436 return collector;
437 }
438
439 /**
440 * Retrieves the timestamp-aware contents of a SingleInputNode's parentage.
441 *
442 * @since 2.3
443 */
444 public Map<Tuple, Timeline<Timestamp>> pullPropagatedContentsWithTimestamp(final SingleInputNode supplier,
445 final boolean flush) {
446 if (flush) {
447 flushUpdates();
448 }
449 final Map<Tuple, Timeline<Timestamp>> collector = CollectionsFactory.createMap();
450 supplier.propagatePullIntoWithTimestamp(collector, flush);
451 return collector;
452 }
453
454 /**
455 * Retrieves the contents of a supplier for a remote caller. Assumption is that this container is the home of the
456 * supplier, but it is not strictly necessary.
457 *
458 * @param supplier
459 * the address of the supplier to be pulled.
460 * @since 2.3
461 */
462 public Collection<Tuple> remotePull(Address<? extends Supplier> supplier, boolean flush) {
463 if (!isLocal(supplier))
464 return supplier.getContainer().remotePull(supplier, flush);
465 return pullContents(resolveLocal(supplier), flush);
466 }
467
468 /**
469 * Proxies for the getPosMapping() of Production nodes. Retrieves the posmapping of a remote or local Production to
470 * a remote or local caller.
471 */
472 public Map<String, Integer> remotePosMapping(Address<? extends ProductionNode> production) {
473 if (!isLocal(production))
474 return production.getContainer().remotePosMapping(production);
475 return resolveLocal(production).getPosMapping();
476 }
477
478 /**
479 * Continually consumes update messages. Should be run on a dedicated thread.
480 */
481 void messageConsumptionCycle() {
482 while (!killed) // deliver messages on and on and on....
483 {
484 long incrementedClock = 0;
485 UpdateMessage message = null;
486
487 if (!internalMessageQueue.isEmpty()) // take internal messages first
488 message = internalMessageQueue.removeFirst();
489 else
490 // no internal message, take an incoming message
491 synchronized (externalMessageLock) { // no sleeping allowed,
492 // because external
493 // queue is locked for
494 // precise clocking of
495 // termination point!
496 if (!externalMessageQueue.isEmpty()) { // if external queue
497 // is non-empty,
498 // retrieve the next
499 // message instantly
500 message = takeExternalMessage();
501 } else { // if external queue is found empty (and this is
502 // the first time in a row)
503 incrementedClock = ++clock; // local termination point
504 // synchronized(clock){incrementedClock = ++clock;}
505 }
506 }
507
508 if (message == null) // both queues were empty
509 {
510 localUpdateTermination(incrementedClock); // report local
511 // termination point
512 while (message == null) // wait for a message while external
513 // queue is still empty
514 {
515 synchronized (externalMessageLock) {
516 while (externalMessageQueue.isEmpty()) {
517 try {
518 externalMessageLock.wait();
519 } catch (InterruptedException e) {
520 if (killed)
521 return;
522 }
523 }
524 message = takeExternalMessage();
525 }
526
527 }
528 }
529
530 // now we have a message to deliver
531 // NOTE: this method is not compatible with differential dataflow
532 message.receiver.update(message.direction, message.updateElement, Timestamp.ZERO);
533 }
534 }
535
536 /**
537 * @since 1.6
538 */
539 public static final Function<Node, String> NAME_MAPPER = input -> input.toString().substring(0,
540 Math.min(30, input.toString().length()));
541
542 /**
543 * Sends out all pending messages to their receivers. The delivery is governed by the communication tracker.
544 *
545 * @since 1.6
546 */
547 public void deliverMessagesSingleThreaded() {
548 if (!backendContext.areUpdatesDelayed()) {
549 if (Options.MONITOR_VIOLATION_OF_RETE_NODEGROUP_TOPOLOGICAL_SORTING) {
550 // known unreachable; enable for debugging only
551
552 CommunicationGroup lastGroup = null;
553 Set<CommunicationGroup> seenInThisCycle = new HashSet<>();
554
555 while (!tracker.isEmpty()) {
556 final CommunicationGroup group = tracker.getAndRemoveFirstGroup();
557
558 /**
559 * The current group does not violate the communication schema iff (1) it was not seen before OR (2)
560 * the last one that was seen is exactly the same as the current one this can happen if the group
561 * was added back because of in-group message passing
562 */
563 boolean okGroup = (group == lastGroup) || seenInThisCycle.add(group);
564
565 if (!okGroup) {
566 logger.error(
567 "[INTERNAL ERROR] Violation of communication schema! The communication component with representative "
568 + group.getRepresentative() + " has already been processed!");
569 }
570
571 group.deliverMessages();
572
573 lastGroup = group;
574 }
575
576 } else {
577 while (!tracker.isEmpty()) {
578 final CommunicationGroup group = tracker.getAndRemoveFirstGroup();
579 group.deliverMessages();
580 }
581 }
582 }
583 }
584
585 private void localUpdateTermination(long incrementedClock) {
586 network.reportLocalUpdateTermination(this, incrementedClock, terminationCriteria);
587 terminationCriteria.clear();
588
589 // synchronized(clock){++clock;} // +1 incrementing for parity and easy
590 // comparison
591 }
592
593 // @pre: externalMessageQueue synchronized && nonempty
594 private UpdateMessage takeExternalMessage() {
595 UpdateMessage message = externalMessageQueue.removeFirst();
596 if (!externalMessageQueue.isEmpty()) { // copy the whole queue over
597 // for speedup
598 Deque<UpdateMessage> temp = externalMessageQueue;
599 externalMessageQueue = internalMessageQueue;
600 internalMessageQueue = temp;
601 }
602 return message;
603 }
604
605 /**
606 * Provides an external address for the selected node.
607 *
608 * @pre node belongs to this container.
609 */
610 public <N extends Node> Address<N> makeAddress(N node) {
611 return new Address<N>(node);
612 }
613
614 /**
615 * Checks whether a certain address points to a node at this container.
616 */
617 public boolean isLocal(Address<? extends Node> address) {
618 return address.getContainer() == this;
619 }
620
621 /**
622 * Returns an addressed node at this container.
623 *
624 * @pre: address.container == this, e.g. address MUST be local
625 * @throws IllegalArgumentException
626 * if address is non-local
627 */
628 @SuppressWarnings("unchecked")
629 public <N extends Node> N resolveLocal(Address<N> address) {
630 if (this != address.getContainer())
631 throw new IllegalArgumentException(String.format("Address %s non-local at container %s", address, this));
632
633 N cached = address.getNodeCache();
634 if (cached != null)
635 return cached;
636 else {
637 N node = (N) nodesById.get(address.getNodeId());
638 address.setNodeCache(node);
639 return node;
640 }
641 }
642
643 /**
644 * Registers a node into the rete network (should be called by constructor). Every node MUST be registered by its
645 * constructor.
646 *
647 * @return the unique nodeId issued to the node.
648 */
649 public long registerNode(Node n) {
650 long id = nextId++;
651 nodesById.put(id, n);
652 return id;
653 }
654
655 /**
656 * Unregisters a node from the rete network. Do NOT call if node is still connected to other Nodes, or Adressed or
657 * otherwise referenced.
658 */
659 public void unregisterNode(Node n) {
660 nodesById.remove(n.getNodeId());
661 }
662
663 /**
664 * Registers a pattern memory into the rete network. Every memory MUST be registered by its owner node.
665 */
666 public void registerClearable(Clearable c) {
667 clearables.addFirst(c);
668 }
669
670 /**
671 * Unregisters a pattern memory from the rete network.
672 */
673 public void unregisterClearable(Clearable c) {
674 clearables.remove(c);
675 }
676
677 /**
678 * Clears all memory contents in the network. Reverts to initial state.
679 */
680 public void clearAll() {
681 for (Clearable c : clearables) {
682 c.clear();
683 }
684 }
685
686 public NodeFactory getNodeFactory() {
687 return network.getNodeFactory();
688 }
689
690 public ConnectionFactory getConnectionFactory() {
691 return connectionFactory;
692 }
693
694 public NodeProvisioner getProvisioner() {
695 return nodeProvisioner;
696 }
697
698 public Network getNetwork() {
699 return network;
700 }
701
702 @Override
703 public String toString() {
704 StringBuilder sb = new StringBuilder();
705 String separator = System.getProperty("line.separator");
706 sb.append(super.toString() + "[[[" + separator);
707 java.util.List<Long> keys = new java.util.ArrayList<Long>(nodesById.keySet());
708 java.util.Collections.sort(keys);
709 for (Long key : keys) {
710 sb.append(key + " -> " + nodesById.get(key) + separator);
711 }
712 sb.append("]]] of " + network);
713 return sb.toString();
714 }
715
716 /**
717 * Access all the Rete nodes inside this container.
718 *
719 * @return the collection of {@link Node} instances
720 */
721 public Collection<Node> getAllNodes() {
722 return nodesById.values();
723 }
724
725 public InputConnector getInputConnectionFactory() {
726 return network.getInputConnector();
727 }
728
729}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java
new file mode 100644
index 00000000..e7ec36dc
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java
@@ -0,0 +1,121 @@
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.rete.network;
11
12import java.util.Collection;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Set;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
20import tools.refinery.viatra.runtime.matchers.util.Direction;
21import tools.refinery.viatra.runtime.rete.index.GenericProjectionIndexer;
22import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
23import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
24import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
25import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
26
27/**
28 * Base implementation for a supplier node.
29 *
30 * @author Gabor Bergmann
31 *
32 */
33public abstract class StandardNode extends BaseNode implements Supplier, NetworkStructureChangeSensitiveNode {
34 protected final List<Receiver> children = CollectionsFactory.createObserverList();
35 /**
36 * @since 2.2
37 */
38 protected final List<Mailbox> childMailboxes = CollectionsFactory.createObserverList();
39
40 public StandardNode(final ReteContainer reteContainer) {
41 super(reteContainer);
42 }
43
44 /**
45 * @since 2.4
46 */
47 protected void propagateUpdate(final Direction direction, final Tuple updateElement, final Timestamp timestamp) {
48 for (final Mailbox childMailbox : childMailboxes) {
49 childMailbox.postMessage(direction, updateElement, timestamp);
50 }
51 }
52
53 @Override
54 public void appendChild(final Receiver receiver) {
55 children.add(receiver);
56 childMailboxes.add(this.getCommunicationTracker().proxifyMailbox(this, receiver.getMailbox()));
57 }
58
59 @Override
60 public void removeChild(final Receiver receiver) {
61 children.remove(receiver);
62 Mailbox mailboxToRemove = null;
63 for (final Mailbox mailbox : childMailboxes) {
64 if (mailbox.getReceiver() == receiver) {
65 mailboxToRemove = mailbox;
66 break;
67 }
68 }
69 assert mailboxToRemove != null;
70 childMailboxes.remove(mailboxToRemove);
71 }
72
73 @Override
74 public void networkStructureChanged() {
75 childMailboxes.clear();
76 for (final Receiver receiver : children) {
77 childMailboxes.add(this.getCommunicationTracker().proxifyMailbox(this, receiver.getMailbox()));
78 }
79 }
80
81 @Override
82 public Collection<Receiver> getReceivers() {
83 return children;
84 }
85
86 /**
87 * @since 2.2
88 */
89 public Collection<Mailbox> getChildMailboxes() {
90 return this.childMailboxes;
91 }
92
93 @Override
94 public Set<Tuple> getPulledContents(final boolean flush) {
95 final HashSet<Tuple> results = new HashSet<Tuple>();
96 pullInto(results, flush);
97 return results;
98 }
99
100 @Override
101 public ProjectionIndexer constructIndex(final TupleMask mask, final TraceInfo... traces) {
102 final GenericProjectionIndexer indexer = new GenericProjectionIndexer(reteContainer, mask);
103 for (final TraceInfo traceInfo : traces) {
104 indexer.assignTraceInfo(traceInfo);
105 }
106 reteContainer.connectAndSynchronize(this, indexer);
107 return indexer;
108 }
109
110 /**
111 * @since 1.6
112 */
113 protected void issueError(final String message, final Exception ex) {
114 if (ex == null) {
115 this.reteContainer.getNetwork().getEngine().getLogger().error(message);
116 } else {
117 this.reteContainer.getNetwork().getEngine().getLogger().error(message, ex);
118 }
119 }
120
121}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Supplier.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Supplier.java
new file mode 100644
index 00000000..1917a7cf
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Supplier.java
@@ -0,0 +1,82 @@
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.rete.network;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21import tools.refinery.viatra.runtime.rete.single.TrimmerNode;
22import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
23
24/**
25 * @author Gabor Bergmann
26 *
27 * A supplier is an object that can propagate insert or revoke events towards receivers.
28 */
29public interface Supplier extends Node {
30
31 /**
32 * Pulls the contents of this object in this particular moment into a target collection.
33 *
34 * @param flush if true, flushing of messages is allowed during the pull, otherwise flushing is not allowed
35 * @since 2.3
36 */
37 public void pullInto(Collection<Tuple> collector, boolean flush);
38
39 /**
40 * @since 2.4
41 */
42 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush);
43
44 /**
45 * Returns the contents of this object in this particular moment.
46 * For memoryless nodes, this may involve a costly recomputation of contents.
47 *
48 * The result is returned as a Set, even when it has multiplicities (at the output of {@link TrimmerNode}).
49 *
50 * <p> Intended mainly for debug purposes; therefore flushing is performed only if explicitly requested
51 * During runtime, flushing may be preferred; see {@link ReteContainer#pullContents(Supplier)}
52 * @since 2.3
53 */
54 public Set<Tuple> getPulledContents(boolean flush);
55
56 default public Set<Tuple> getPulledContents() {
57 return getPulledContents(true);
58 }
59
60 /**
61 * appends a receiver that will continously receive insert and revoke updates from this supplier
62 */
63 void appendChild(Receiver receiver);
64
65 /**
66 * removes a receiver
67 */
68 void removeChild(Receiver receiver);
69
70 /**
71 * Instantiates (or reuses, depending on implementation) an index according to the given mask.
72 *
73 * Intended for internal use; clients should invoke through Library instead to enable reusing.
74 */
75 ProjectionIndexer constructIndex(TupleMask mask, TraceInfo... traces);
76
77 /**
78 * lists receivers
79 */
80 Collection<Receiver> getReceivers();
81
82}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Tunnel.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Tunnel.java
new file mode 100644
index 00000000..f238f47b
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Tunnel.java
@@ -0,0 +1,19 @@
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.rete.network;
11
12/**
13 * @author Gabor Bergmann
14 *
15 * A Tunnel is an interface into which elments can be instered and from which productions can be extracted.
16 */
17public interface Tunnel extends Supplier, Receiver {
18
19}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/UpdateMessage.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/UpdateMessage.java
new file mode 100644
index 00000000..1334a3a9
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/UpdateMessage.java
@@ -0,0 +1,31 @@
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.rete.network;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.matchers.util.Direction;
14
15class UpdateMessage {
16 public Receiver receiver;
17 public Direction direction;
18 public Tuple updateElement;
19
20 public UpdateMessage(Receiver receiver, Direction direction, Tuple updateElement) {
21 this.receiver = receiver;
22 this.direction = direction;
23 this.updateElement = updateElement;
24 }
25
26 @Override
27 public String toString() {
28 return "M." + direction + ": " + updateElement + " -> " + receiver;
29 }
30
31}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationGroup.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationGroup.java
new file mode 100644
index 00000000..8cedeb11
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationGroup.java
@@ -0,0 +1,103 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Tamas Szabo, 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.rete.network.communication;
10
11import java.util.Collection;
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.rete.network.Node;
15import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
16
17/**
18 * A communication group represents a set of nodes in the communication graph that form a strongly connected component.
19 *
20 * @author Tamas Szabo
21 * @since 1.6
22 */
23public abstract class CommunicationGroup implements Comparable<CommunicationGroup> {
24
25 public static final String UNSUPPORTED_MESSAGE_KIND = "Unsupported message kind ";
26
27 /**
28 * Marker for the {@link CommunicationTracker}
29 */
30 public boolean isEnqueued = false;
31
32 protected final Node representative;
33
34 /**
35 * May be changed during bumping in {@link CommunicationTracker.registerDependency}
36 */
37 protected int identifier;
38
39 /**
40 * @since 1.7
41 */
42 protected final CommunicationTracker tracker;
43
44 /**
45 * @since 1.7
46 */
47 public CommunicationGroup(final CommunicationTracker tracker, final Node representative, final int identifier) {
48 this.tracker = tracker;
49 this.representative = representative;
50 this.identifier = identifier;
51 }
52
53 public abstract void deliverMessages();
54
55 public Node getRepresentative() {
56 return representative;
57 }
58
59 public abstract boolean isEmpty();
60
61 /**
62 * @since 2.0
63 */
64 public abstract void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind);
65
66 /**
67 * @since 2.0
68 */
69 public abstract void notifyHasMessage(final Mailbox mailbox, final MessageSelector kind);
70
71 public abstract Map<MessageSelector, Collection<Mailbox>> getMailboxes();
72
73 public abstract boolean isRecursive();
74
75 @Override
76 public int hashCode() {
77 return this.identifier;
78 }
79
80 @Override
81 public String toString() {
82 return this.getClass().getSimpleName() + " " + this.identifier + " - representative: " + this.representative
83 + " - isEmpty: " + isEmpty();
84 }
85
86 @Override
87 public boolean equals(final Object obj) {
88 if (obj == null || this.getClass() != obj.getClass()) {
89 return false;
90 } else if (this == obj) {
91 return true;
92 } else {
93 final CommunicationGroup that = (CommunicationGroup) obj;
94 return this.identifier == that.identifier;
95 }
96 }
97
98 @Override
99 public int compareTo(final CommunicationGroup that) {
100 return this.identifier - that.identifier;
101 }
102
103}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java
new file mode 100644
index 00000000..8435a547
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java
@@ -0,0 +1,467 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Tamas Szabo, 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.rete.network.communication;
10
11import java.util.HashMap;
12import java.util.HashSet;
13import java.util.List;
14import java.util.Map;
15import java.util.PriorityQueue;
16import java.util.Queue;
17import java.util.Set;
18
19import tools.refinery.viatra.runtime.base.itc.alg.incscc.IncSCCAlg;
20import tools.refinery.viatra.runtime.base.itc.alg.misc.topsort.TopologicalSorting;
21import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph;
22import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
23import tools.refinery.viatra.runtime.rete.aggregation.IAggregatorNode;
24import tools.refinery.viatra.runtime.rete.boundary.ExternalInputEnumeratorNode;
25import tools.refinery.viatra.runtime.rete.eval.RelationEvaluatorNode;
26import tools.refinery.viatra.runtime.rete.index.DualInputNode;
27import tools.refinery.viatra.runtime.rete.index.ExistenceNode;
28import tools.refinery.viatra.runtime.rete.index.Indexer;
29import tools.refinery.viatra.runtime.rete.index.IndexerListener;
30import tools.refinery.viatra.runtime.rete.index.IterableIndexer;
31import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer;
32import tools.refinery.viatra.runtime.rete.network.IGroupable;
33import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
34import tools.refinery.viatra.runtime.rete.network.Node;
35import tools.refinery.viatra.runtime.rete.network.ProductionNode;
36import tools.refinery.viatra.runtime.rete.network.Receiver;
37import tools.refinery.viatra.runtime.rete.network.ReteContainer;
38import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyIndexerListenerProxy;
39import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyMailboxProxy;
40import tools.refinery.viatra.runtime.rete.network.mailbox.FallThroughCapableMailbox;
41import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
42import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
43import tools.refinery.viatra.runtime.rete.single.TransitiveClosureNode;
44import tools.refinery.viatra.runtime.rete.single.TrimmerNode;
45
46/**
47 * An instance of this class is associated with every {@link ReteContainer}. The tracker serves two purposes: <br>
48 * (1) It allows RETE nodes to register their communication dependencies on-the-fly. These dependencies can be
49 * registered or unregistered when nodes are disposed of. <br>
50 * (2) It allows RETE nodes to register their mailboxes as dirty, that is, they can tell the tracker that they have
51 * something to send to other nodes in the network. The tracker is then responsible for ordering these messages (more
52 * precisely, the mailboxes that contain the messages) for the associated {@link ReteContainer}. The ordering is
53 * governed by the strongly connected components in the dependency network and follows a topological sorting scheme;
54 * those mailboxes will be emptied first whose owner nodes do not depend on other undelivered messages.
55 *
56 * @author Tamas Szabo
57 * @since 1.6
58 *
59 */
60public abstract class CommunicationTracker {
61
62 /**
63 * The minimum group id assigned so far
64 */
65 protected int minGroupId;
66
67 /**
68 * The maximum group id assigned so far
69 */
70 protected int maxGroupId;
71
72 /**
73 * The dependency graph of the communications in the RETE network
74 */
75 protected final Graph<Node> dependencyGraph;
76
77 /**
78 * Incremental SCC information about the dependency graph
79 */
80 protected final IncSCCAlg<Node> sccInformationProvider;
81
82 /**
83 * Precomputed node -> communication group map
84 */
85 protected final Map<Node, CommunicationGroup> groupMap;
86
87 /**
88 * Priority queue of active communication groups
89 */
90 protected final Queue<CommunicationGroup> groupQueue;
91
92 // groups should have a simple integer flag which represents its position in a priority queue
93 // priority queue only contains the ACTIVE groups
94
95 public CommunicationTracker() {
96 this.dependencyGraph = new Graph<Node>();
97 this.sccInformationProvider = new IncSCCAlg<Node>(this.dependencyGraph);
98 this.groupQueue = new PriorityQueue<CommunicationGroup>();
99 this.groupMap = new HashMap<Node, CommunicationGroup>();
100 }
101
102 public Graph<Node> getDependencyGraph() {
103 return dependencyGraph;
104 }
105
106 public CommunicationGroup getGroup(final Node node) {
107 return this.groupMap.get(node);
108 }
109
110 private void precomputeGroups() {
111 groupMap.clear();
112
113 // reconstruct group map from dependency graph
114 final Graph<Node> reducedGraph = sccInformationProvider.getReducedGraph();
115 final List<Node> representatives = TopologicalSorting.compute(reducedGraph);
116
117 for (int i = 0; i < representatives.size(); i++) { // groups for SCC representatives
118 final Node representative = representatives.get(i);
119 createAndStoreGroup(representative, i);
120 }
121
122 minGroupId = 0;
123 maxGroupId = representatives.size() - 1;
124
125 for (final Node node : dependencyGraph.getAllNodes()) { // extend group map to the rest of nodes
126 final Node representative = sccInformationProvider.getRepresentative(node);
127 final CommunicationGroup group = groupMap.get(representative);
128 if (representative != node) {
129 addToGroup(node, group);
130 }
131 }
132
133 for (final Node node : dependencyGraph.getAllNodes()) {
134 // set fall-through flags of default mailboxes
135 precomputeFallThroughFlag(node);
136 // perform further tracker-specific post-processing
137 postProcessNode(node);
138 }
139
140 // reconstruct new queue contents based on new group map
141 if (!groupQueue.isEmpty()) {
142 final Set<CommunicationGroup> oldActiveGroups = new HashSet<CommunicationGroup>(groupQueue);
143 groupQueue.clear();
144 reconstructQueueContents(oldActiveGroups);
145 }
146
147 // post process the groups
148 for (final CommunicationGroup group : groupMap.values()) {
149 postProcessGroup(group);
150 }
151 }
152
153 /**
154 * This method is responsible for reconstructing the active queue contents after the network structure has changed.
155 * It it defined as abstract because the reconstruction logic is specific to each {@link CommunicationTracker}.
156 * @since 2.4
157 */
158 protected abstract void reconstructQueueContents(final Set<CommunicationGroup> oldActiveGroups);
159
160 private void addToGroup(final Node node, final CommunicationGroup group) {
161 groupMap.put(node, group);
162 if (node instanceof Receiver) {
163 ((Receiver) node).getMailbox().setCurrentGroup(group);
164 if (node instanceof IGroupable) {
165 ((IGroupable) node).setCurrentGroup(group);
166 }
167 }
168 }
169
170 /**
171 * Depends on the groups, as well as the parent nodes of the argument, so recomputation is needed if these change
172 */
173 private void precomputeFallThroughFlag(final Node node) {
174 CommunicationGroup group = groupMap.get(node);
175 if (node instanceof Receiver) {
176 IGroupable mailbox = ((Receiver) node).getMailbox();
177 if (mailbox instanceof FallThroughCapableMailbox) {
178 Set<Node> directParents = dependencyGraph.getSourceNodes(node).distinctValues();
179 // decide between using quick&cheap fall-through, or allowing for update cancellation
180 boolean fallThrough =
181 // disallow fallthrough: updates at production nodes should cancel, if they can be trimmed or
182 // disjunctive
183 (!(node instanceof ProductionNode && ( // it is a production node...
184 // with more than one parent
185 directParents.size() > 0 ||
186 // or true trimming in its sole parent
187 directParents.size() == 1 && trueTrimming(directParents.iterator().next())))) &&
188 // disallow fallthrough: external updates should be stored (if updates are delayed)
189 (!(node instanceof ExternalInputEnumeratorNode)) &&
190 // disallow fallthrough: RelationEvaluatorNode needs to be notified in batch-style, and the batching is done by the mailbox
191 // however, it is not the RelationEvaluatorNode itself that is interesting here, as that indirectly uses the BatchingReceiver
192 // so we need to disable fall-through for the BatchingReceiver
193 (!(node instanceof RelationEvaluatorNode.BatchingReceiver));
194 // do additional checks
195 if (fallThrough) {
196 // recursive parent groups generate excess updates that should be cancelled after delete&rederive
197 // phases
198 // aggregator and transitive closure parent nodes also generate excess updates that should be
199 // cancelled
200 directParentLoop: for (Node directParent : directParents) {
201 Set<Node> parentsToCheck = new HashSet<>();
202 // check the case where a direct parent is the reason for mailbox usage
203 parentsToCheck.add(directParent);
204 // check the case where an indirect parent (join slot) is the reason for mailbox usage
205 if (directParent instanceof DualInputNode) {
206 // in case of existence join (typically antijoin), a mailbox should allow
207 // an insertion and deletion (at the secondary slot) to cancel each other out
208 if (directParent instanceof ExistenceNode) {
209 fallThrough = false;
210 break directParentLoop;
211 }
212 // in beta nodes, indexer slots (or their active nodes) are considered indirect parents
213 DualInputNode dualInput = (DualInputNode) directParent;
214 IterableIndexer primarySlot = dualInput.getPrimarySlot();
215 if (primarySlot != null)
216 parentsToCheck.add(primarySlot.getActiveNode());
217 Indexer secondarySlot = dualInput.getSecondarySlot();
218 if (secondarySlot != null)
219 parentsToCheck.add(secondarySlot.getActiveNode());
220 }
221 for (Node parent : parentsToCheck) {
222 CommunicationGroup parentGroup = groupMap.get(parent);
223 if ( // parent is in a different, recursive group
224 (group != parentGroup && parentGroup.isRecursive()) ||
225 // node and parent within the same recursive group, and...
226 (group == parentGroup && group.isRecursive() && (
227 // parent is a transitive closure or aggregator node, or a trimmer
228 // allow trimmed or disjunctive tuple updates to cancel each other
229 (parent instanceof TransitiveClosureNode) || (parent instanceof IAggregatorNode)
230 || trueTrimming(parent)))) {
231 fallThrough = false;
232 break directParentLoop;
233 }
234 }
235 }
236 }
237 // overwrite fallthrough flag with newly computed value
238 ((FallThroughCapableMailbox) mailbox).setFallThrough(fallThrough);
239 }
240 }
241 }
242
243 /**
244 * A trimmer node that actually eliminates some columns (not just reorders)
245 */
246 private boolean trueTrimming(Node node) {
247 if (node instanceof TrimmerNode) {
248 TupleMask mask = ((TrimmerNode) node).getMask();
249 return (mask.indices.length != mask.sourceWidth);
250 }
251 return false;
252 }
253
254 public void activateUnenqueued(final CommunicationGroup group) {
255 groupQueue.add(group);
256 group.isEnqueued = true;
257 }
258
259 public void deactivate(final CommunicationGroup group) {
260 groupQueue.remove(group);
261 group.isEnqueued = false;
262 }
263
264 public CommunicationGroup getAndRemoveFirstGroup() {
265 final CommunicationGroup group = groupQueue.poll();
266 group.isEnqueued = false;
267 return group;
268 }
269
270 public boolean isEmpty() {
271 return groupQueue.isEmpty();
272 }
273
274 protected abstract CommunicationGroup createGroup(final Node representative, final int index);
275
276 protected CommunicationGroup createAndStoreGroup(final Node representative, final int index) {
277 final CommunicationGroup group = createGroup(representative, index);
278 addToGroup(representative, group);
279 return group;
280 }
281
282 /**
283 * Registers the dependency that the target {@link Node} depends on the source {@link Node}. In other words, source
284 * may send messages to target in the RETE network. If the dependency edge is already present, this method call is a
285 * noop.
286 *
287 * @param source
288 * the source node
289 * @param target
290 * the target node
291 */
292 public void registerDependency(final Node source, final Node target) {
293 // nodes can be immediately inserted, if they already exist in the graph, this is a noop
294 dependencyGraph.insertNode(source);
295 dependencyGraph.insertNode(target);
296
297 if (!this.dependencyGraph.getTargetNodes(source).containsNonZero(target)) {
298
299 // query all these information before the actual edge insertion
300 // because SCCs may be unified during the process
301 final Node sourceRepresentative = sccInformationProvider.getRepresentative(source);
302 final Node targetRepresentative = sccInformationProvider.getRepresentative(target);
303 final boolean targetHadOutgoingEdges = sccInformationProvider.hasOutgoingEdges(targetRepresentative);
304
305 // insert the edge
306 dependencyGraph.insertEdge(source, target);
307
308 // create groups if they do not yet exist
309 CommunicationGroup sourceGroup = groupMap.get(sourceRepresentative);
310 if (sourceGroup == null) {
311 // create on-demand with the next smaller group id
312 sourceGroup = createAndStoreGroup(sourceRepresentative, --minGroupId);
313 }
314 final int sourceIndex = sourceGroup.identifier;
315
316 CommunicationGroup targetGroup = groupMap.get(targetRepresentative);
317 if (targetGroup == null) {
318 // create on-demand with the next larger group id
319 targetGroup = createAndStoreGroup(targetRepresentative, ++maxGroupId);
320 }
321 final int targetIndex = targetGroup.identifier;
322
323 if (sourceIndex <= targetIndex) {
324 // indices obey current topological ordering
325 refreshFallThroughFlag(target);
326 postProcessNode(source);
327 postProcessNode(target);
328 postProcessGroup(sourceGroup);
329 if (sourceGroup != targetGroup) {
330 postProcessGroup(targetGroup);
331 }
332 } else if (sourceIndex > targetIndex && !targetHadOutgoingEdges) {
333 // indices violate current topological ordering, but we can simply bump the target index
334 final boolean wasEnqueued = targetGroup.isEnqueued;
335 if (wasEnqueued) {
336 groupQueue.remove(targetGroup);
337 }
338 targetGroup.identifier = ++maxGroupId;
339 if (wasEnqueued) {
340 groupQueue.add(targetGroup);
341 }
342
343 refreshFallThroughFlag(target);
344 postProcessNode(source);
345 postProcessNode(target);
346 postProcessGroup(sourceGroup);
347 postProcessGroup(targetGroup);
348 } else {
349 // needs a full re-computation because of more complex change
350 precomputeGroups();
351 }
352 }
353 }
354
355 /**
356 * Returns true if the given {@link Node} is in a recursive {@link CommunicationGroup}, false otherwise.
357 */
358 public boolean isInRecursiveGroup(final Node node) {
359 final CommunicationGroup group = this.getGroup(node);
360 if (group == null) {
361 return false;
362 } else {
363 return group.isRecursive();
364 }
365 }
366
367 /**
368 * Returns true if the given two {@link Node}s are in the same {@link CommunicationGroup}.
369 */
370 public boolean areInSameGroup(final Node left, final Node right) {
371 final CommunicationGroup leftGroup = this.getGroup(left);
372 final CommunicationGroup rightGroup = this.getGroup(right);
373 return leftGroup != null && leftGroup == rightGroup;
374 }
375
376 /**
377 * Unregisters a dependency between source and target.
378 *
379 * @param source
380 * the source node
381 * @param target
382 * the target node
383 */
384 public void unregisterDependency(final Node source, final Node target) {
385 // delete the edge first, and then query the SCC info provider
386 this.dependencyGraph.deleteEdgeIfExists(source, target);
387
388 final Node sourceRepresentative = sccInformationProvider.getRepresentative(source);
389 final Node targetRepresentative = sccInformationProvider.getRepresentative(target);
390
391 // if they are still in the same SCC,
392 // then this deletion did not affect the SCCs,
393 // and it is sufficient to recompute affected fall-through flags;
394 // otherwise, we need a new pre-computation for the groupMap and groupQueue
395 if (sourceRepresentative.equals(targetRepresentative)) {
396 // this deletion could not have affected the split flags
397 refreshFallThroughFlag(target);
398 postProcessNode(source);
399 postProcessNode(target);
400 } else {
401 // preComputeGroups takes care of the split flag maintenance
402 precomputeGroups();
403 }
404 }
405
406 /**
407 * Refresh fall-through flags if dependencies change for given target, but no SCC change
408 */
409 private void refreshFallThroughFlag(final Node target) {
410 precomputeFallThroughFlag(target);
411 if (target instanceof DualInputNode) {
412 for (final Node indirectTarget : dependencyGraph.getTargetNodes(target).distinctValues()) {
413 precomputeFallThroughFlag(indirectTarget);
414 }
415 }
416 }
417
418 /**
419 * Returns true if the given source-target edge in the communication network acts as a recursion cut point.
420 * The current implementation considers edges leading into {@link ProductionNode}s as cut point iff
421 * both source and target belong to the same group.
422 *
423 * @param source the source node
424 * @param target the target node
425 * @return true if the edge is a cut point, false otherwise
426 * @since 2.4
427 */
428 protected boolean isRecursionCutPoint(final Node source, final Node target) {
429 final Node effectiveSource = source instanceof SpecializedProjectionIndexer
430 ? ((SpecializedProjectionIndexer) source).getActiveNode()
431 : source;
432 final CommunicationGroup sourceGroup = this.getGroup(effectiveSource);
433 final CommunicationGroup targetGroup = this.getGroup(target);
434 return sourceGroup != null && sourceGroup == targetGroup && target instanceof ProductionNode;
435 }
436
437 /**
438 * This hook allows concrete tracker implementations to perform tracker-specific post processing on nodes (cf.
439 * {@link NetworkStructureChangeSensitiveNode} and {@link BehaviorChangingMailbox}). At the time of the invocation,
440 * the network topology has already been updated.
441 */
442 protected abstract void postProcessNode(final Node node);
443
444 /**
445 * This hook allows concrete tracker implementations to perform tracker-specific post processing on groups. At the
446 * time of the invocation, the network topology has already been updated.
447 * @since 2.4
448 */
449 protected abstract void postProcessGroup(final CommunicationGroup group);
450
451 /**
452 * Creates a proxy for the given {@link Mailbox} for the given requester {@link Node}. The proxy creation is
453 * {@link CommunicationTracker}-specific and depends on the identity of the requester. This method is primarily used
454 * to create {@link TimelyMailboxProxy}s depending on the network topology. There is no guarantee that the same
455 * proxy instance is returned when this method is called multiple times with the same arguments.
456 */
457 public abstract Mailbox proxifyMailbox(final Node requester, final Mailbox original);
458
459 /**
460 * Creates a proxy for the given {@link IndexerListener} for the given requester {@link Node}. The proxy creation is
461 * {@link CommunicationTracker}-specific and depends on the identity of the requester. This method is primarily used
462 * to create {@link TimelyIndexerListenerProxy}s depending on the network topology. There is no guarantee that the
463 * same proxy instance is returned when this method is called multiple times with the same arguments.
464 */
465 public abstract IndexerListener proxifyIndexerListener(final Node requester, final IndexerListener original);
466
467}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/MessageSelector.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/MessageSelector.java
new file mode 100644
index 00000000..e1a61693
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/MessageSelector.java
@@ -0,0 +1,19 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, 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.rete.network.communication;
10
11/**
12 * Subclasses of this interface represent meta data of update messages in Rete.
13 *
14 * @author Tamas Szabo
15 * @since 2.3
16 */
17public interface MessageSelector {
18
19}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/NodeComparator.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/NodeComparator.java
new file mode 100644
index 00000000..27779352
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/NodeComparator.java
@@ -0,0 +1,32 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network.communication;
10
11import java.util.Comparator;
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.rete.network.Node;
15
16/**
17 * @since 2.4
18 */
19public class NodeComparator implements Comparator<Node> {
20
21 protected final Map<Node, Integer> nodeMap;
22
23 public NodeComparator(final Map<Node, Integer> nodeMap) {
24 this.nodeMap = nodeMap;
25 }
26
27 @Override
28 public int compare(final Node left, final Node right) {
29 return this.nodeMap.get(left) - this.nodeMap.get(right);
30 }
31
32} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/PhasedSelector.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/PhasedSelector.java
new file mode 100644
index 00000000..41cd8cd3
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/PhasedSelector.java
@@ -0,0 +1,34 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Tamas Szabo, 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.rete.network.communication;
10
11/**
12 * A default message selector that can be used to associate phases to messages.
13 *
14 * @author Tamas Szabo
15 * @since 2.3
16 */
17public enum PhasedSelector implements MessageSelector {
18
19 /**
20 * No special distinguishing feature
21 */
22 DEFAULT,
23
24 /**
25 * Inserts and delete-insert monotone change pairs
26 */
27 MONOTONE,
28
29 /**
30 * Deletes
31 */
32 ANTI_MONOTONE
33
34}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/Timestamp.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/Timestamp.java
new file mode 100644
index 00000000..a50a63a8
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/Timestamp.java
@@ -0,0 +1,124 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, 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.rete.network.communication;
10
11import java.util.AbstractMap;
12import java.util.Collection;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines;
18
19/**
20 * A timestamp associated with update messages in timely evaluation.
21 *
22 * @author Tamas Szabo
23 * @since 2.3
24 */
25public class Timestamp implements Comparable<Timestamp>, MessageSelector {
26
27 protected final int value;
28 public static final Timestamp ZERO = new Timestamp(0);
29 /**
30 * @since 2.4
31 */
32 public static final Timeline<Timestamp> INSERT_AT_ZERO_TIMELINE = Timelines.createFrom(Timestamp.ZERO);
33
34 public Timestamp(final int value) {
35 this.value = value;
36 }
37
38 public int getValue() {
39 return value;
40 }
41
42 public Timestamp max(final Timestamp that) {
43 if (this.value >= that.value) {
44 return this;
45 } else {
46 return that;
47 }
48 }
49
50 /**
51 * @since 2.4
52 */
53 public Timestamp min(final Timestamp that) {
54 if (this.value <= that.value) {
55 return this;
56 } else {
57 return that;
58 }
59 }
60
61 @Override
62 public int compareTo(final Timestamp that) {
63 return this.value - that.value;
64 }
65
66 @Override
67 public boolean equals(final Object obj) {
68 if (obj == null || !(obj instanceof Timestamp)) {
69 return false;
70 } else {
71 return this.value == ((Timestamp) obj).value;
72 }
73 }
74
75 @Override
76 public int hashCode() {
77 return this.value;
78 }
79
80 @Override
81 public String toString() {
82 return Integer.toString(this.value);
83 }
84
85 /**
86 * A {@link Map} implementation that associates the zero timestamp with every key. There is no suppor for
87 * {@link Map#entrySet()} due to performance reasons.
88 *
89 * @author Tamas Szabo
90 */
91 public static final class AllZeroMap<T> extends AbstractMap<T, Timeline<Timestamp>> {
92
93 private final Collection<T> wrapped;
94
95 public AllZeroMap(Set<T> wrapped) {
96 this.wrapped = wrapped;
97 }
98
99 @Override
100 public Set<Entry<T, Timeline<Timestamp>>> entrySet() {
101 throw new UnsupportedOperationException("Use the combination of keySet() and get()!");
102 }
103
104 /**
105 * @since 2.4
106 */
107 @Override
108 public Timeline<Timestamp> get(final Object key) {
109 return INSERT_AT_ZERO_TIMELINE;
110 }
111
112 @Override
113 public Set<T> keySet() {
114 return (Set<T>) this.wrapped;
115 }
116
117 @Override
118 public String toString() {
119 return this.getClass().getSimpleName() + ": " + this.keySet().toString();
120 }
121
122 }
123
124}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/RecursiveCommunicationGroup.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/RecursiveCommunicationGroup.java
new file mode 100644
index 00000000..d8260384
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/RecursiveCommunicationGroup.java
@@ -0,0 +1,164 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network.communication.timeless;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.EnumMap;
14import java.util.LinkedHashSet;
15import java.util.Map;
16import java.util.Set;
17
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19import tools.refinery.viatra.runtime.rete.network.Node;
20import tools.refinery.viatra.runtime.rete.network.RederivableNode;
21import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
22import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
23import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
24import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector;
25import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
26
27/**
28 * A communication group representing either a single node where the
29 * node is a monotonicity aware one or a set of nodes that form an SCC.
30 *
31 * @author Tamas Szabo
32 * @since 2.4
33 */
34public class RecursiveCommunicationGroup extends CommunicationGroup {
35
36 private final Set<Mailbox> antiMonotoneMailboxes;
37 private final Set<Mailbox> monotoneMailboxes;
38 private final Set<Mailbox> defaultMailboxes;
39 private final Set<RederivableNode> rederivables;
40 private boolean currentlyDelivering;
41
42 /**
43 * @since 1.7
44 */
45 public RecursiveCommunicationGroup(final CommunicationTracker tracker, final Node representative, final int identifier) {
46 super(tracker, representative, identifier);
47 this.antiMonotoneMailboxes = CollectionsFactory.createSet();
48 this.monotoneMailboxes = CollectionsFactory.createSet();
49 this.defaultMailboxes = CollectionsFactory.createSet();
50 this.rederivables = new LinkedHashSet<RederivableNode>();
51 this.currentlyDelivering = false;
52 }
53
54 @Override
55 public void deliverMessages() {
56 this.currentlyDelivering = true;
57
58 // ANTI-MONOTONE PHASE
59 while (!this.antiMonotoneMailboxes.isEmpty() || !this.defaultMailboxes.isEmpty()) {
60 while (!this.antiMonotoneMailboxes.isEmpty()) {
61 final Mailbox mailbox = this.antiMonotoneMailboxes.iterator().next();
62 this.antiMonotoneMailboxes.remove(mailbox);
63 mailbox.deliverAll(PhasedSelector.ANTI_MONOTONE);
64 }
65 while (!this.defaultMailboxes.isEmpty()) {
66 final Mailbox mailbox = this.defaultMailboxes.iterator().next();
67 this.defaultMailboxes.remove(mailbox);
68 mailbox.deliverAll(PhasedSelector.DEFAULT);
69 }
70 }
71
72 // REDERIVE PHASE
73 while (!this.rederivables.isEmpty()) {
74 // re-derivable nodes take care of their unregistration!!
75 final RederivableNode node = this.rederivables.iterator().next();
76 node.rederiveOne();
77 }
78
79 // MONOTONE PHASE
80 while (!this.monotoneMailboxes.isEmpty() || !this.defaultMailboxes.isEmpty()) {
81 while (!this.monotoneMailboxes.isEmpty()) {
82 final Mailbox mailbox = this.monotoneMailboxes.iterator().next();
83 this.monotoneMailboxes.remove(mailbox);
84 mailbox.deliverAll(PhasedSelector.MONOTONE);
85 }
86 while (!this.defaultMailboxes.isEmpty()) {
87 final Mailbox mailbox = this.defaultMailboxes.iterator().next();
88 this.defaultMailboxes.remove(mailbox);
89 mailbox.deliverAll(PhasedSelector.DEFAULT);
90 }
91 }
92
93 this.currentlyDelivering = false;
94 }
95
96 @Override
97 public boolean isEmpty() {
98 return this.rederivables.isEmpty() && this.antiMonotoneMailboxes.isEmpty()
99 && this.monotoneMailboxes.isEmpty() && this.defaultMailboxes.isEmpty();
100 }
101
102 @Override
103 public void notifyHasMessage(final Mailbox mailbox, final MessageSelector kind) {
104 final Collection<Mailbox> mailboxes = getMailboxContainer(kind);
105 mailboxes.add(mailbox);
106 if (!this.isEnqueued && !this.currentlyDelivering) {
107 this.tracker.activateUnenqueued(this);
108 }
109 }
110
111 @Override
112 public void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind) {
113 final Collection<Mailbox> mailboxes = getMailboxContainer(kind);
114 mailboxes.remove(mailbox);
115 if (isEmpty()) {
116 this.tracker.deactivate(this);
117 }
118 }
119
120 private Collection<Mailbox> getMailboxContainer(final MessageSelector kind) {
121 if (kind == PhasedSelector.ANTI_MONOTONE) {
122 return this.antiMonotoneMailboxes;
123 } else if (kind == PhasedSelector.MONOTONE) {
124 return this.monotoneMailboxes;
125 } else if (kind == PhasedSelector.DEFAULT) {
126 return this.defaultMailboxes;
127 } else {
128 throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind);
129 }
130 }
131
132 public void addRederivable(final RederivableNode node) {
133 this.rederivables.add(node);
134 if (!this.isEnqueued) {
135 this.tracker.activateUnenqueued(this);
136 }
137 }
138
139 public void removeRederivable(final RederivableNode node) {
140 this.rederivables.remove(node);
141 if (isEmpty()) {
142 this.tracker.deactivate(this);
143 }
144 }
145
146 public Collection<RederivableNode> getRederivables() {
147 return this.rederivables;
148 }
149
150 @Override
151 public Map<MessageSelector, Collection<Mailbox>> getMailboxes() {
152 Map<PhasedSelector, Collection<Mailbox>> map = new EnumMap<>(PhasedSelector.class);
153 map.put(PhasedSelector.ANTI_MONOTONE, antiMonotoneMailboxes);
154 map.put(PhasedSelector.MONOTONE, monotoneMailboxes);
155 map.put(PhasedSelector.DEFAULT, defaultMailboxes);
156 return Collections.unmodifiableMap(map);
157 }
158
159 @Override
160 public boolean isRecursive() {
161 return true;
162 }
163
164}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/SingletonCommunicationGroup.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/SingletonCommunicationGroup.java
new file mode 100644
index 00000000..c51c7dbf
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/SingletonCommunicationGroup.java
@@ -0,0 +1,86 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network.communication.timeless;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.rete.network.Node;
16import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
17import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
18import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
19import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector;
20import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
21
22/**
23 * A communication group containing only a single node with a single default
24 * mailbox.
25 *
26 * @author Tamas Szabo
27 * @since 1.6
28 */
29public class SingletonCommunicationGroup extends CommunicationGroup {
30
31 private Mailbox mailbox;
32
33 /**
34 * @since 1.7
35 */
36 public SingletonCommunicationGroup(final CommunicationTracker tracker, final Node representative, final int identifier) {
37 super(tracker, representative, identifier);
38 }
39
40 @Override
41 public void deliverMessages() {
42 this.mailbox.deliverAll(PhasedSelector.DEFAULT);
43 }
44
45 @Override
46 public boolean isEmpty() {
47 return this.mailbox == null;
48 }
49
50 @Override
51 public void notifyHasMessage(final Mailbox mailbox, final MessageSelector kind) {
52 if (kind == PhasedSelector.DEFAULT) {
53 this.mailbox = mailbox;
54 if (!this.isEnqueued) {
55 this.tracker.activateUnenqueued(this);
56 }
57 } else {
58 throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind);
59 }
60 }
61
62 @Override
63 public void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind) {
64 if (kind == PhasedSelector.DEFAULT) {
65 this.mailbox = null;
66 this.tracker.deactivate(this);
67 } else {
68 throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind);
69 }
70 }
71
72 @Override
73 public Map<MessageSelector, Collection<Mailbox>> getMailboxes() {
74 if (mailbox != null) {
75 return Collections.singletonMap(PhasedSelector.DEFAULT, Collections.singleton(mailbox));
76 } else {
77 return Collections.emptyMap();
78 }
79 }
80
81 @Override
82 public boolean isRecursive() {
83 return false;
84 }
85
86}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/TimelessCommunicationTracker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/TimelessCommunicationTracker.java
new file mode 100644
index 00000000..1c18c1cd
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/TimelessCommunicationTracker.java
@@ -0,0 +1,149 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network.communication.timeless;
10
11import java.util.Collection;
12import java.util.HashSet;
13import java.util.Set;
14import java.util.Map.Entry;
15
16import tools.refinery.viatra.runtime.rete.index.DualInputNode;
17import tools.refinery.viatra.runtime.rete.index.Indexer;
18import tools.refinery.viatra.runtime.rete.index.IndexerListener;
19import tools.refinery.viatra.runtime.rete.index.IterableIndexer;
20import tools.refinery.viatra.runtime.rete.network.Node;
21import tools.refinery.viatra.runtime.rete.network.Receiver;
22import tools.refinery.viatra.runtime.rete.network.RederivableNode;
23import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
24import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
25import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
26import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
27import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
28
29/**
30 * Timeless implementation of the communication tracker.
31 *
32 * @author Tamas Szabo
33 * @since 2.2
34 */
35public class TimelessCommunicationTracker extends CommunicationTracker {
36
37 @Override
38 protected CommunicationGroup createGroup(Node representative, int index) {
39 final boolean isSingleton = this.sccInformationProvider.sccs.getPartition(representative).size() == 1;
40 final boolean isReceiver = representative instanceof Receiver;
41 final boolean isPosetIndifferent = isReceiver
42 && ((Receiver) representative).getMailbox() instanceof BehaviorChangingMailbox;
43 final boolean isSingletonInDRedMode = isSingleton && (representative instanceof RederivableNode)
44 && ((RederivableNode) representative).isInDRedMode();
45
46 CommunicationGroup group = null;
47 // we can only use a singleton group iff
48 // (1) the SCC has one node AND
49 // (2) either we have a poset-indifferent mailbox OR the node is not even a receiver AND
50 // (3) the node does not run in DRed mode in a singleton group
51 if (isSingleton && (isPosetIndifferent || !isReceiver) && !isSingletonInDRedMode) {
52 group = new SingletonCommunicationGroup(this, representative, index);
53 } else {
54 group = new RecursiveCommunicationGroup(this, representative, index);
55 }
56
57 return group;
58 }
59
60 @Override
61 protected void reconstructQueueContents(final Set<CommunicationGroup> oldActiveGroups) {
62 for (final CommunicationGroup oldGroup : oldActiveGroups) {
63 for (final Entry<MessageSelector, Collection<Mailbox>> entry : oldGroup.getMailboxes().entrySet()) {
64 for (final Mailbox mailbox : entry.getValue()) {
65 final CommunicationGroup newGroup = this.groupMap.get(mailbox.getReceiver());
66 newGroup.notifyHasMessage(mailbox, entry.getKey());
67 }
68 }
69
70 if (oldGroup instanceof RecursiveCommunicationGroup) {
71 for (final RederivableNode node : ((RecursiveCommunicationGroup) oldGroup).getRederivables()) {
72 final CommunicationGroup newGroup = this.groupMap.get(node);
73 if (!(newGroup instanceof RecursiveCommunicationGroup)) {
74 throw new IllegalStateException("The new group must also be recursive! " + newGroup);
75 }
76 ((RecursiveCommunicationGroup) newGroup).addRederivable(node);
77 }
78 }
79 }
80 }
81
82 @Override
83 public Mailbox proxifyMailbox(final Node requester, final Mailbox original) {
84 return original;
85 }
86
87 @Override
88 public IndexerListener proxifyIndexerListener(final Node requester, final IndexerListener original) {
89 return original;
90 }
91
92 @Override
93 protected void postProcessNode(final Node node) {
94 if (node instanceof Receiver) {
95 final Mailbox mailbox = ((Receiver) node).getMailbox();
96 if (mailbox instanceof BehaviorChangingMailbox) {
97 final CommunicationGroup group = this.groupMap.get(node);
98 final Set<Node> sccNodes = this.sccInformationProvider.sccs.getPartition(node);
99 // a default mailbox must split its messages iff
100 // (1) its receiver is in a recursive group and
101 final boolean c1 = group.isRecursive();
102 // (2) its receiver is at the SCC boundary of that group
103 final boolean c2 = isAtSCCBoundary(node);
104 // (3) its group consists of more than one node
105 final boolean c3 = sccNodes.size() > 1;
106 ((BehaviorChangingMailbox) mailbox).setSplitFlag(c1 && c2 && c3);
107 }
108 }
109 }
110
111 @Override
112 protected void postProcessGroup(final CommunicationGroup group) {
113
114 }
115
116 /**
117 * @since 2.0
118 */
119 private boolean isAtSCCBoundary(final Node node) {
120 final CommunicationGroup ownGroup = this.groupMap.get(node);
121 assert ownGroup != null;
122 for (final Node source : this.dependencyGraph.getSourceNodes(node).distinctValues()) {
123 final Set<Node> sourcesToCheck = new HashSet<Node>();
124 sourcesToCheck.add(source);
125 // DualInputNodes must be checked additionally because they do not use a mailbox directly.
126 // It can happen that their indexers actually belong to other SCCs.
127 if (source instanceof DualInputNode) {
128 final DualInputNode dualInput = (DualInputNode) source;
129 final IterableIndexer primarySlot = dualInput.getPrimarySlot();
130 if (primarySlot != null) {
131 sourcesToCheck.add(primarySlot.getActiveNode());
132 }
133 final Indexer secondarySlot = dualInput.getSecondarySlot();
134 if (secondarySlot != null) {
135 sourcesToCheck.add(secondarySlot.getActiveNode());
136 }
137 }
138 for (final Node current : sourcesToCheck) {
139 final CommunicationGroup otherGroup = this.groupMap.get(current);
140 assert otherGroup != null;
141 if (!ownGroup.equals(otherGroup)) {
142 return true;
143 }
144 }
145 }
146 return false;
147 }
148
149}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/ResumableNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/ResumableNode.java
new file mode 100644
index 00000000..8097bd91
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/ResumableNode.java
@@ -0,0 +1,36 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.rete.network.communication.timely;
10
11import tools.refinery.viatra.runtime.rete.network.IGroupable;
12import tools.refinery.viatra.runtime.rete.network.Node;
13import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
14
15/**
16 * {@link Node}s that implement this interface can resume folding of their states when instructed during timely evaluation.
17 *
18 * @since 2.3
19 * @author Tamas Szabo
20 */
21public interface ResumableNode extends Node, IGroupable {
22
23 /**
24 * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to
25 * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more
26 * folding to do towards higher timestamps.
27 */
28 public void resumeAt(final Timestamp timestamp);
29
30 /**
31 * Returns the smallest timestamp where lazy folding shall be resumed, or null if there is no more folding to do in this
32 * resumable.
33 */
34 public Timestamp getResumableTimestamp();
35
36}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationGroup.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationGroup.java
new file mode 100644
index 00000000..0394d92c
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationGroup.java
@@ -0,0 +1,171 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network.communication.timely;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Comparator;
14import java.util.HashMap;
15import java.util.Map;
16import java.util.Map.Entry;
17import java.util.Set;
18import java.util.TreeMap;
19import java.util.TreeSet;
20
21import org.apache.log4j.Logger;
22import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
23import tools.refinery.viatra.runtime.rete.network.Node;
24import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
25import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
26import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
27import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
28import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
29import tools.refinery.viatra.runtime.rete.util.Options;
30
31/**
32 * A timely communication group implementation. {@link TimelyMailbox}es and {@link LazyFoldingNode}s are ordered in the
33 * increasing order of timestamps.
34 *
35 * @author Tamas Szabo
36 * @since 2.3
37 */
38public class TimelyCommunicationGroup extends CommunicationGroup {
39
40 private final boolean isSingleton;
41 private final TreeMap<Timestamp, Set<Mailbox>> mailboxQueue;
42 // may be null - only used in the scattered case where we need to take care of mailboxes and resumables too
43 private Comparator<Node> nodeComparator;
44 private boolean currentlyDelivering;
45 private Timestamp currentlyDeliveredTimestamp;
46
47 public TimelyCommunicationGroup(final TimelyCommunicationTracker tracker, final Node representative,
48 final int identifier, final boolean isSingleton) {
49 super(tracker, representative, identifier);
50 this.isSingleton = isSingleton;
51 this.mailboxQueue = CollectionsFactory.createTreeMap();
52 this.currentlyDelivering = false;
53 }
54
55 /**
56 * Sets the {@link Comparator} to be used to order the {@link Mailbox}es at a given {@link Timestamp} in the mailbox
57 * queue. Additionally, reorders already queued {@link Mailbox}es to reflect the new comparator. The comparator may
58 * be null, in this case, no set ordering will be enforced among the {@link Mailbox}es.
59 */
60 public void setComparatorAndReorderMailboxes(final Comparator<Node> nodeComparator) {
61 this.nodeComparator = nodeComparator;
62 if (!this.mailboxQueue.isEmpty()) {
63 final HashMap<Timestamp, Set<Mailbox>> queueCopy = new HashMap<Timestamp, Set<Mailbox>>(this.mailboxQueue);
64 this.mailboxQueue.clear();
65 for (final Entry<Timestamp, Set<Mailbox>> entry : queueCopy.entrySet()) {
66 for (final Mailbox mailbox : entry.getValue()) {
67 this.notifyHasMessage(mailbox, entry.getKey());
68 }
69 }
70 }
71 }
72
73 @Override
74 public void deliverMessages() {
75 this.currentlyDelivering = true;
76 while (!this.mailboxQueue.isEmpty()) {
77 // care must be taken here how we iterate over the mailboxes
78 // it is not okay to loop over the mailboxes at once because a mailbox may disappear from the collection as
79 // a result of delivering messages from another mailboxes under the same timestamp
80 // because of this, it is crucial that we pick the mailboxes one by one
81 final Entry<Timestamp, Set<Mailbox>> entry = this.mailboxQueue.firstEntry();
82 final Timestamp timestamp = entry.getKey();
83 final Set<Mailbox> mailboxes = entry.getValue();
84 final Mailbox mailbox = mailboxes.iterator().next();
85 mailboxes.remove(mailbox);
86 if (mailboxes.isEmpty()) {
87 this.mailboxQueue.pollFirstEntry();
88 }
89 assert mailbox instanceof TimelyMailbox;
90 /* debug */ this.currentlyDeliveredTimestamp = timestamp;
91 mailbox.deliverAll(timestamp);
92 /* debug */ this.currentlyDeliveredTimestamp = null;
93 }
94 this.currentlyDelivering = false;
95 }
96
97 @Override
98 public boolean isEmpty() {
99 return this.mailboxQueue.isEmpty();
100 }
101
102 @Override
103 public void notifyHasMessage(final Mailbox mailbox, MessageSelector kind) {
104 if (kind instanceof Timestamp) {
105 final Timestamp timestamp = (Timestamp) kind;
106 if (Options.MONITOR_VIOLATION_OF_DIFFERENTIAL_DATAFLOW_TIMESTAMPS) {
107 if (timestamp.compareTo(this.currentlyDeliveredTimestamp) < 0) {
108 final Logger logger = this.representative.getContainer().getNetwork().getEngine().getLogger();
109 logger.error(
110 "[INTERNAL ERROR] Violation of differential dataflow communication schema! The communication component with representative "
111 + this.representative + " observed decreasing timestamp during message delivery!");
112 }
113 }
114 final Set<Mailbox> mailboxes = this.mailboxQueue.computeIfAbsent(timestamp, k -> {
115 if (this.nodeComparator == null) {
116 return CollectionsFactory.createSet();
117 } else {
118 return new TreeSet<Mailbox>(new Comparator<Mailbox>() {
119 @Override
120 public int compare(final Mailbox left, final Mailbox right) {
121 return nodeComparator.compare(left.getReceiver(), right.getReceiver());
122 }
123 });
124 }
125 });
126 mailboxes.add(mailbox);
127 if (!this.isEnqueued && !this.currentlyDelivering) {
128 this.tracker.activateUnenqueued(this);
129 }
130 } else {
131 throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind);
132 }
133 }
134
135 @Override
136 public void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind) {
137 if (kind instanceof Timestamp) {
138 final Timestamp timestamp = (Timestamp) kind;
139 this.mailboxQueue.compute(timestamp, (k, v) -> {
140 if (v == null) {
141 throw new IllegalStateException("No mailboxes registered at timestamp " + timestamp + "!");
142 }
143 if (!v.remove(mailbox)) {
144 throw new IllegalStateException(
145 "The mailbox " + mailbox + " was not registered at timestamp " + timestamp + "!");
146 }
147 if (v.isEmpty()) {
148 return null;
149 } else {
150 return v;
151 }
152 });
153 if (this.mailboxQueue.isEmpty()) {
154 this.tracker.deactivate(this);
155 }
156 } else {
157 throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind);
158 }
159 }
160
161 @Override
162 public Map<MessageSelector, Collection<Mailbox>> getMailboxes() {
163 return Collections.unmodifiableMap(this.mailboxQueue);
164 }
165
166 @Override
167 public boolean isRecursive() {
168 return !this.isSingleton;
169 }
170
171}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java
new file mode 100644
index 00000000..1ff69882
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java
@@ -0,0 +1,216 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network.communication.timely;
10
11import java.util.Collection;
12import java.util.List;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Set;
16import java.util.function.Function;
17
18import tools.refinery.viatra.runtime.base.itc.alg.misc.topsort.TopologicalSorting;
19import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.rete.index.IndexerListener;
22import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer;
23import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer.ListenerSubscription;
24import tools.refinery.viatra.runtime.rete.index.StandardIndexer;
25import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration;
26import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
27import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
28import tools.refinery.viatra.runtime.rete.network.Node;
29import tools.refinery.viatra.runtime.rete.network.ProductionNode;
30import tools.refinery.viatra.runtime.rete.network.StandardNode;
31import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
32import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
33import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
34import tools.refinery.viatra.runtime.rete.network.communication.NodeComparator;
35import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
36import tools.refinery.viatra.runtime.rete.single.DiscriminatorDispatcherNode;
37
38/**
39 * Timely (DDF) implementation of the {@link CommunicationTracker}.
40 *
41 * @author Tamas Szabo
42 * @since 2.3
43 */
44public class TimelyCommunicationTracker extends CommunicationTracker {
45
46 protected final TimelyConfiguration configuration;
47
48 public TimelyCommunicationTracker(final TimelyConfiguration configuration) {
49 this.configuration = configuration;
50 }
51
52 @Override
53 protected CommunicationGroup createGroup(final Node representative, final int index) {
54 final boolean isSingleton = this.sccInformationProvider.sccs.getPartition(representative).size() == 1;
55 return new TimelyCommunicationGroup(this, representative, index, isSingleton);
56 }
57
58 @Override
59 protected void reconstructQueueContents(final Set<CommunicationGroup> oldActiveGroups) {
60 for (final CommunicationGroup oldGroup : oldActiveGroups) {
61 for (final Entry<MessageSelector, Collection<Mailbox>> entry : oldGroup.getMailboxes().entrySet()) {
62 for (final Mailbox mailbox : entry.getValue()) {
63 final CommunicationGroup newGroup = this.groupMap.get(mailbox.getReceiver());
64 newGroup.notifyHasMessage(mailbox, entry.getKey());
65 }
66 }
67 }
68 }
69
70 @Override
71 public Mailbox proxifyMailbox(final Node requester, final Mailbox original) {
72 final Mailbox mailboxToProxify = (original instanceof TimelyMailboxProxy)
73 ? ((TimelyMailboxProxy) original).getWrappedMailbox()
74 : original;
75 final TimestampTransformation preprocessor = getPreprocessor(requester, mailboxToProxify.getReceiver());
76 if (preprocessor == null) {
77 return mailboxToProxify;
78 } else {
79 return new TimelyMailboxProxy(mailboxToProxify, preprocessor);
80 }
81 }
82
83 @Override
84 public IndexerListener proxifyIndexerListener(final Node requester, final IndexerListener original) {
85 final IndexerListener listenerToProxify = (original instanceof TimelyIndexerListenerProxy)
86 ? ((TimelyIndexerListenerProxy) original).getWrappedIndexerListener()
87 : original;
88 final TimestampTransformation preprocessor = getPreprocessor(requester, listenerToProxify.getOwner());
89 if (preprocessor == null) {
90 return listenerToProxify;
91 } else {
92 return new TimelyIndexerListenerProxy(listenerToProxify, preprocessor);
93 }
94 }
95
96 protected TimestampTransformation getPreprocessor(final Node source, final Node target) {
97 final Node effectiveSource = source instanceof SpecializedProjectionIndexer
98 ? ((SpecializedProjectionIndexer) source).getActiveNode()
99 : source;
100 final CommunicationGroup sourceGroup = this.getGroup(effectiveSource);
101 final CommunicationGroup targetGroup = this.getGroup(target);
102
103 if (sourceGroup != null && targetGroup != null) {
104 // during RETE construction, the groups may be still null
105 if (sourceGroup != targetGroup && sourceGroup.isRecursive()) {
106 // targetGroup is a successor SCC of sourceGroup
107 // and sourceGroup is a recursive SCC
108 // then we need to zero out the timestamps
109 return TimestampTransformation.RESET;
110 }
111 if (sourceGroup == targetGroup && target instanceof ProductionNode) {
112 // if requester and receiver are in the same SCC
113 // and receiver is a production node
114 // then we need to increment the timestamps
115 return TimestampTransformation.INCREMENT;
116 }
117 }
118
119 return null;
120 }
121
122 @Override
123 protected void postProcessNode(final Node node) {
124 if (node instanceof NetworkStructureChangeSensitiveNode) {
125 ((NetworkStructureChangeSensitiveNode) node).networkStructureChanged();
126 }
127 }
128
129 @Override
130 protected void postProcessGroup(final CommunicationGroup group) {
131 if (this.configuration.getTimelineRepresentation() == TimelineRepresentation.FAITHFUL) {
132 final Node representative = group.getRepresentative();
133 final Set<Node> groupMembers = this.sccInformationProvider.sccs.getPartition(representative);
134 if (groupMembers.size() > 1) {
135 final Graph<Node> graph = new Graph<Node>();
136
137 for (final Node node : groupMembers) {
138 graph.insertNode(node);
139 }
140
141 for (final Node source : groupMembers) {
142 for (final Node target : this.dependencyGraph.getTargetNodes(source)) {
143 // (1) the edge is not a recursion cut point
144 // (2) the edge is within this group
145 if (!this.isRecursionCutPoint(source, target) && groupMembers.contains(target)) {
146 graph.insertEdge(source, target);
147 }
148 }
149 }
150
151 final List<Node> orderedNodes = TopologicalSorting.compute(graph);
152 final Map<Node, Integer> nodeMap = CollectionsFactory.createMap();
153 int identifier = 0;
154 for (final Node orderedNode : orderedNodes) {
155 nodeMap.put(orderedNode, identifier++);
156 }
157
158 ((TimelyCommunicationGroup) group).setComparatorAndReorderMailboxes(new NodeComparator(nodeMap));
159 }
160 }
161 }
162
163 /**
164 * This static field is used for debug purposes in the DotGenerator.
165 */
166 public static final Function<Node, Function<Node, String>> EDGE_LABEL_FUNCTION = new Function<Node, Function<Node, String>>() {
167
168 @Override
169 public Function<Node, String> apply(final Node source) {
170 return new Function<Node, String>() {
171 @Override
172 public String apply(final Node target) {
173 if (source instanceof SpecializedProjectionIndexer) {
174 final Collection<ListenerSubscription> subscriptions = ((SpecializedProjectionIndexer) source)
175 .getSubscriptions();
176 for (final ListenerSubscription subscription : subscriptions) {
177 if (subscription.getListener().getOwner() == target
178 && subscription.getListener() instanceof TimelyIndexerListenerProxy) {
179 return ((TimelyIndexerListenerProxy) subscription.getListener()).preprocessor
180 .toString();
181 }
182 }
183 }
184 if (source instanceof StandardIndexer) {
185 final Collection<IndexerListener> listeners = ((StandardIndexer) source).getListeners();
186 for (final IndexerListener listener : listeners) {
187 if (listener.getOwner() == target && listener instanceof TimelyIndexerListenerProxy) {
188 return ((TimelyIndexerListenerProxy) listener).preprocessor.toString();
189 }
190 }
191 }
192 if (source instanceof StandardNode) {
193 final Collection<Mailbox> mailboxes = ((StandardNode) source).getChildMailboxes();
194 for (final Mailbox mailbox : mailboxes) {
195 if (mailbox.getReceiver() == target && mailbox instanceof TimelyMailboxProxy) {
196 return ((TimelyMailboxProxy) mailbox).preprocessor.toString();
197 }
198 }
199 }
200 if (source instanceof DiscriminatorDispatcherNode) {
201 final Collection<Mailbox> mailboxes = ((DiscriminatorDispatcherNode) source)
202 .getBucketMailboxes().values();
203 for (final Mailbox mailbox : mailboxes) {
204 if (mailbox.getReceiver() == target && mailbox instanceof TimelyMailboxProxy) {
205 return ((TimelyMailboxProxy) mailbox).preprocessor.toString();
206 }
207 }
208 }
209 return null;
210 }
211 };
212 }
213
214 };
215
216}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyIndexerListenerProxy.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyIndexerListenerProxy.java
new file mode 100644
index 00000000..e8fbf84e
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyIndexerListenerProxy.java
@@ -0,0 +1,81 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network.communication.timely;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import tools.refinery.viatra.runtime.matchers.util.Direction;
13import tools.refinery.viatra.runtime.matchers.util.Preconditions;
14import tools.refinery.viatra.runtime.rete.index.IndexerListener;
15import tools.refinery.viatra.runtime.rete.network.Node;
16import tools.refinery.viatra.runtime.rete.network.ProductionNode;
17import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
18
19/**
20 * A timely proxy for another {@link IndexerListener}, which performs some preprocessing
21 * on the differential timestamps before passing it on to the real recipient.
22 * <p>
23 * These proxies are used on edges leading into {@link ProductionNode}s. Because {@link ProductionNode}s
24 * never ask back the indexer for its contents, there is no need to also apply the proxy on that direction.
25 *
26 * @author Tamas Szabo
27 * @since 2.3
28 */
29public class TimelyIndexerListenerProxy implements IndexerListener {
30
31 protected final TimestampTransformation preprocessor;
32 protected final IndexerListener wrapped;
33
34 public TimelyIndexerListenerProxy(final IndexerListener wrapped,
35 final TimestampTransformation preprocessor) {
36 Preconditions.checkArgument(!(wrapped instanceof TimelyIndexerListenerProxy), "Proxy in a proxy is not allowed!");
37 this.wrapped = wrapped;
38 this.preprocessor = preprocessor;
39 }
40
41 public IndexerListener getWrappedIndexerListener() {
42 return wrapped;
43 }
44
45 @Override
46 public Node getOwner() {
47 return this.wrapped.getOwner();
48 }
49
50 @Override
51 public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement, final Tuple signature,
52 final boolean change, final Timestamp timestamp) {
53 this.wrapped.notifyIndexerUpdate(direction, updateElement, signature, change, preprocessor.process(timestamp));
54 }
55
56 @Override
57 public String toString() {
58 return this.preprocessor.toString() + "_PROXY -> " + this.wrapped.toString();
59 }
60
61 @Override
62 public boolean equals(final Object obj) {
63 if (obj == null || obj.getClass() != this.getClass()) {
64 return false;
65 } else if (obj == this) {
66 return true;
67 } else {
68 final TimelyIndexerListenerProxy that = (TimelyIndexerListenerProxy) obj;
69 return this.wrapped.equals(that.wrapped) && this.preprocessor == that.preprocessor;
70 }
71 }
72
73 @Override
74 public int hashCode() {
75 int hash = 1;
76 hash = hash * 17 + this.wrapped.hashCode();
77 hash = hash * 31 + this.preprocessor.hashCode();
78 return hash;
79 }
80
81}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyMailboxProxy.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyMailboxProxy.java
new file mode 100644
index 00000000..550bfbeb
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyMailboxProxy.java
@@ -0,0 +1,102 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network.communication.timely;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import tools.refinery.viatra.runtime.matchers.util.Direction;
13import tools.refinery.viatra.runtime.matchers.util.Preconditions;
14import tools.refinery.viatra.runtime.rete.network.Receiver;
15import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
16import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
17import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
18import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
19
20/**
21 * A timely proxy for another {@link Mailbox}, which performs some preprocessing
22 * on the differential timestamps before passing it on to the real recipient.
23 *
24 * @author Tamas Szabo
25 * @since 2.3
26 */
27public class TimelyMailboxProxy implements Mailbox {
28
29 protected final TimestampTransformation preprocessor;
30 protected final Mailbox wrapped;
31
32 public TimelyMailboxProxy(final Mailbox wrapped, final TimestampTransformation preprocessor) {
33 Preconditions.checkArgument(!(wrapped instanceof TimelyMailboxProxy), "Proxy in a proxy is not allowed!");
34 this.wrapped = wrapped;
35 this.preprocessor = preprocessor;
36 }
37
38 public Mailbox getWrappedMailbox() {
39 return wrapped;
40 }
41
42 @Override
43 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) {
44 this.wrapped.postMessage(direction, update, preprocessor.process(timestamp));
45 }
46
47 @Override
48 public String toString() {
49 return this.preprocessor.toString() + "_PROXY -> " + this.wrapped.toString();
50 }
51
52 @Override
53 public void clear() {
54 this.wrapped.clear();
55 }
56
57 @Override
58 public void deliverAll(final MessageSelector selector) {
59 this.wrapped.deliverAll(selector);
60 }
61
62 @Override
63 public CommunicationGroup getCurrentGroup() {
64 return this.wrapped.getCurrentGroup();
65 }
66
67 @Override
68 public void setCurrentGroup(final CommunicationGroup group) {
69 this.wrapped.setCurrentGroup(group);
70 }
71
72 @Override
73 public Receiver getReceiver() {
74 return this.wrapped.getReceiver();
75 }
76
77 @Override
78 public boolean isEmpty() {
79 return this.wrapped.isEmpty();
80 }
81
82 @Override
83 public boolean equals(final Object obj) {
84 if (obj == null || obj.getClass() != this.getClass()) {
85 return false;
86 } else if (obj == this) {
87 return true;
88 } else {
89 final TimelyMailboxProxy that = (TimelyMailboxProxy) obj;
90 return this.wrapped.equals(that.wrapped) && this.preprocessor == that.preprocessor;
91 }
92 }
93
94 @Override
95 public int hashCode() {
96 int hash = 1;
97 hash = hash * 17 + this.wrapped.hashCode();
98 hash = hash * 31 + this.preprocessor.hashCode();
99 return hash;
100 }
101
102}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimestampTransformation.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimestampTransformation.java
new file mode 100644
index 00000000..8929eb5c
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimestampTransformation.java
@@ -0,0 +1,48 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network.communication.timely;
10
11import tools.refinery.viatra.runtime.rete.network.Node;
12import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
13
14/**
15 * Values of this enum perform different kind of preprocessing on {@link Timestamp}s.
16 * This is used on edges leading in and out from {@link Node}s in recursive {@link TimelyCommunicationGroup}s.
17 *
18 * @author Tamas Szabo
19 * @since 2.3
20 */
21public enum TimestampTransformation {
22
23 INCREMENT {
24 @Override
25 public Timestamp process(final Timestamp timestamp) {
26 return new Timestamp(timestamp.getValue() + 1);
27 }
28
29 @Override
30 public String toString() {
31 return "INCREMENT";
32 }
33 },
34 RESET {
35 @Override
36 public Timestamp process(final Timestamp timestamp) {
37 return Timestamp.ZERO;
38 }
39
40 @Override
41 public String toString() {
42 return "RESET";
43 }
44 };
45
46 public abstract Timestamp process(final Timestamp timestamp);
47
48}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedCommand.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedCommand.java
new file mode 100644
index 00000000..d6312671
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedCommand.java
@@ -0,0 +1,81 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network.delayed;
10
11import java.util.Collection;
12import java.util.Map;
13import java.util.Map.Entry;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.matchers.util.Signed;
18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import tools.refinery.viatra.runtime.rete.network.Network;
20import tools.refinery.viatra.runtime.rete.network.Node;
21import tools.refinery.viatra.runtime.rete.network.Receiver;
22import tools.refinery.viatra.runtime.rete.network.ReteContainer;
23import tools.refinery.viatra.runtime.rete.network.Supplier;
24import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
25import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
26import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
27
28/**
29 * Instances of this class are responsible for initializing a {@link Receiver} with the contents of a {@link Supplier}.
30 * However, due to the dynamic nature of the Rete {@link Network} and to the fact that certain {@link Node}s in the
31 * {@link Network} are sensitive to the shape of the {@link Network}, the commands must be delayed until the
32 * construction of the {@link Network} has stabilized.
33 *
34 * @author Tamas Szabo
35 * @since 2.3
36 */
37public abstract class DelayedCommand implements Runnable {
38
39 protected final Supplier supplier;
40 protected final Receiver receiver;
41 protected final Direction direction;
42 protected final ReteContainer container;
43
44 public DelayedCommand(final Supplier supplier, final Receiver receiver, final Direction direction,
45 final ReteContainer container) {
46 this.supplier = supplier;
47 this.receiver = receiver;
48 this.direction = direction;
49 this.container = container;
50 }
51
52 @Override
53 public void run() {
54 final CommunicationTracker tracker = this.container.getCommunicationTracker();
55 final Mailbox mailbox = tracker.proxifyMailbox(this.supplier, this.receiver.getMailbox());
56
57 if (this.isTimestampAware()) {
58 final Map<Tuple, Timeline<Timestamp>> contents = this.container.pullContentsWithTimeline(this.supplier,
59 false);
60 for (final Entry<Tuple, Timeline<Timestamp>> entry : contents.entrySet()) {
61 for (final Signed<Timestamp> change : entry.getValue().asChangeSequence()) {
62 mailbox.postMessage(change.getDirection().multiply(this.direction), entry.getKey(),
63 change.getPayload());
64 }
65 }
66 } else {
67 final Collection<Tuple> contents = this.container.pullContents(this.supplier, false);
68 for (final Tuple tuple : contents) {
69 mailbox.postMessage(this.direction, tuple, Timestamp.ZERO);
70 }
71 }
72 }
73
74 @Override
75 public String toString() {
76 return this.supplier + " -> " + this.receiver.toString();
77 }
78
79 protected abstract boolean isTimestampAware();
80
81}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedConnectCommand.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedConnectCommand.java
new file mode 100644
index 00000000..1bfdbec6
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedConnectCommand.java
@@ -0,0 +1,27 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network.delayed;
10
11import tools.refinery.viatra.runtime.matchers.util.Direction;
12import tools.refinery.viatra.runtime.rete.network.Receiver;
13import tools.refinery.viatra.runtime.rete.network.ReteContainer;
14import tools.refinery.viatra.runtime.rete.network.Supplier;
15
16public class DelayedConnectCommand extends DelayedCommand {
17
18 public DelayedConnectCommand(final Supplier supplier, final Receiver receiver, final ReteContainer container) {
19 super(supplier, receiver, Direction.INSERT, container);
20 }
21
22 @Override
23 protected boolean isTimestampAware() {
24 return this.container.isTimelyEvaluation() && this.container.getCommunicationTracker().areInSameGroup(this.supplier, this.receiver);
25 }
26
27}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedDisconnectCommand.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedDisconnectCommand.java
new file mode 100644
index 00000000..5825a971
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedDisconnectCommand.java
@@ -0,0 +1,30 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network.delayed;
10
11import tools.refinery.viatra.runtime.matchers.util.Direction;
12import tools.refinery.viatra.runtime.rete.network.Receiver;
13import tools.refinery.viatra.runtime.rete.network.ReteContainer;
14import tools.refinery.viatra.runtime.rete.network.Supplier;
15
16public class DelayedDisconnectCommand extends DelayedCommand {
17
18 protected final boolean wasInSameSCC;
19
20 public DelayedDisconnectCommand(final Supplier supplier, final Receiver receiver, final ReteContainer container, final boolean wasInSameSCC) {
21 super(supplier, receiver, Direction.DELETE, container);
22 this.wasInSameSCC = wasInSameSCC;
23 }
24
25 @Override
26 protected boolean isTimestampAware() {
27 return this.wasInSameSCC;
28 }
29
30}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/DefaultMessageIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/DefaultMessageIndexer.java
new file mode 100644
index 00000000..da9bc47e
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/DefaultMessageIndexer.java
@@ -0,0 +1,74 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, 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.rete.network.indexer;
10
11import java.util.Collections;
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
16
17/**
18 * @author Tamas Szabo
19 * @since 2.0
20 */
21public class DefaultMessageIndexer implements MessageIndexer {
22
23 protected final Map<Tuple, Integer> indexer;
24
25 public DefaultMessageIndexer() {
26 this.indexer = CollectionsFactory.createMap();
27 }
28
29 public Map<Tuple, Integer> getTuples() {
30 return Collections.unmodifiableMap(this.indexer);
31 }
32
33 @Override
34 public int getCount(final Tuple update) {
35 final Integer count = getTuples().get(update);
36 if (count == null) {
37 return 0;
38 } else {
39 return count;
40 }
41 }
42
43 @Override
44 public void insert(final Tuple update) {
45 update(update, 1);
46 }
47
48 @Override
49 public void delete(final Tuple update) {
50 update(update, -1);
51 }
52
53 @Override
54 public void update(final Tuple update, final int delta) {
55 final Integer oldCount = this.indexer.get(update);
56 final int newCount = (oldCount == null ? 0 : oldCount) + delta;
57 if (newCount == 0) {
58 this.indexer.remove(update);
59 } else {
60 this.indexer.put(update, newCount);
61 }
62 }
63
64 @Override
65 public boolean isEmpty() {
66 return this.indexer.isEmpty();
67 }
68
69 @Override
70 public void clear() {
71 this.indexer.clear();
72 }
73
74}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/GroupBasedMessageIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/GroupBasedMessageIndexer.java
new file mode 100644
index 00000000..80271252
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/GroupBasedMessageIndexer.java
@@ -0,0 +1,95 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, 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.rete.network.indexer;
10
11import java.util.Collections;
12import java.util.Map;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
18
19/**
20 * @author Tamas Szabo
21 * @since 2.0
22 */
23public class GroupBasedMessageIndexer implements MessageIndexer {
24
25 protected final Map<Tuple, DefaultMessageIndexer> indexer;
26 protected final TupleMask groupMask;
27
28 public GroupBasedMessageIndexer(final TupleMask groupMask) {
29 this.indexer = CollectionsFactory.createMap();
30 this.groupMask = groupMask;
31 }
32
33 public Map<Tuple, Integer> getTuplesByGroup(final Tuple group) {
34 final DefaultMessageIndexer values = this.indexer.get(group);
35 if (values == null) {
36 return Collections.emptyMap();
37 } else {
38 return Collections.unmodifiableMap(values.getTuples());
39 }
40 }
41
42 @Override
43 public int getCount(final Tuple update) {
44 final Tuple group = this.groupMask.transform(update);
45 final Integer count = getTuplesByGroup(group).get(update);
46 if (count == null) {
47 return 0;
48 } else {
49 return count;
50 }
51 }
52
53 public Set<Tuple> getGroups() {
54 return Collections.unmodifiableSet(this.indexer.keySet());
55 }
56
57 @Override
58 public void insert(final Tuple update) {
59 update(update, 1);
60 }
61
62 @Override
63 public void delete(final Tuple update) {
64 update(update, -1);
65 }
66
67 @Override
68 public void update(final Tuple update, final int delta) {
69 final Tuple group = this.groupMask.transform(update);
70 DefaultMessageIndexer valueIndexer = this.indexer.get(group);
71
72 if (valueIndexer == null) {
73 valueIndexer = new DefaultMessageIndexer();
74 this.indexer.put(group, valueIndexer);
75 }
76
77 valueIndexer.update(update, delta);
78
79 // it may happen that the indexer becomes empty as a result of the update
80 if (valueIndexer.isEmpty()) {
81 this.indexer.remove(group);
82 }
83 }
84
85 @Override
86 public boolean isEmpty() {
87 return this.indexer.isEmpty();
88 }
89
90 @Override
91 public void clear() {
92 this.indexer.clear();
93 }
94
95}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/MessageIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/MessageIndexer.java
new file mode 100644
index 00000000..271aaa44
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/MessageIndexer.java
@@ -0,0 +1,33 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, 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.rete.network.indexer;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import tools.refinery.viatra.runtime.matchers.util.Clearable;
13import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
14
15/**
16 * A message indexer is used by {@link Mailbox}es to index their contents.
17 *
18 * @author Tamas Szabo
19 * @since 2.0
20 */
21public interface MessageIndexer extends Clearable {
22
23 public void insert(final Tuple update);
24
25 public void delete(final Tuple update);
26
27 public void update(final Tuple update, final int delta);
28
29 public boolean isEmpty();
30
31 public int getCount(final Tuple update);
32
33}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/AdaptableMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/AdaptableMailbox.java
new file mode 100644
index 00000000..99097f56
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/AdaptableMailbox.java
@@ -0,0 +1,32 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, 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.rete.network.mailbox;
10
11import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
12import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyMailboxProxy;
13import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
14
15/**
16 * An adaptable mailbox can be wrapped by another mailbox to act in behalf of that. The significance of the adaptation
17 * is that the adaptee will notify the {@link CommunicationTracker} about updates by promoting the adapter itself.
18 * Adaptable mailboxes are used by the {@link BehaviorChangingMailbox}.
19 *
20 * Compare this with {@link TimelyMailboxProxy}. That one also wraps another mailbox in order to
21 * perform preprocessing on the messages sent to the original recipient.
22 *
23 * @author Tamas Szabo
24 * @since 2.0
25 */
26public interface AdaptableMailbox extends Mailbox {
27
28 public Mailbox getAdapter();
29
30 public void setAdapter(final Mailbox adapter);
31
32}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/FallThroughCapableMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/FallThroughCapableMailbox.java
new file mode 100644
index 00000000..8797e254
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/FallThroughCapableMailbox.java
@@ -0,0 +1,30 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.network.mailbox;
10
11import tools.refinery.viatra.runtime.rete.network.Receiver;
12import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
13
14/**
15 * A fall through capable mailbox can directly call the update method of its {@link Receiver} instead of using the
16 * standard post-deliver mailbox semantics. If the fall through flag is set to true, the mailbox uses direct delivery,
17 * otherwise it operates in the original behavior. The fall through operation is preferable whenever applicable because
18 * it improves performance. The fall through flag is controlled by the {@link CommunicationTracker} based on the
19 * receiver node type and network topology.
20 *
21 * @author Tamas Szabo
22 * @since 2.2
23 */
24public interface FallThroughCapableMailbox extends Mailbox {
25
26 public boolean isFallThrough();
27
28 public void setFallThrough(final boolean fallThrough);
29
30}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/Mailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/Mailbox.java
new file mode 100644
index 00000000..05005974
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/Mailbox.java
@@ -0,0 +1,78 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.rete.network.mailbox;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import tools.refinery.viatra.runtime.matchers.util.Clearable;
13import tools.refinery.viatra.runtime.matchers.util.Direction;
14import tools.refinery.viatra.runtime.rete.network.IGroupable;
15import tools.refinery.viatra.runtime.rete.network.Receiver;
16import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
17import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
18import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
19
20/**
21 * A mailbox is associated with every {@link Receiver}. Messages can be sent to a {@link Receiver} by posting them into
22 * the mailbox. Different mailbox implementations may differ in the way how they deliver the posted messages.
23 *
24 * @author Tamas Szabo
25 * @since 2.0
26 *
27 */
28public interface Mailbox extends Clearable, IGroupable {
29
30 /**
31 * Posts a new message to this mailbox.
32 *
33 * @param direction
34 * the direction of the update
35 * @param update
36 * the update element
37 * @since 2.4
38 */
39 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp);
40
41 /**
42 * Delivers all messages according to the given selector from this mailbox. The selector can also be null. In this case, no
43 * special separation is expected between the messages.
44 *
45 * @param selector the message selector
46 */
47 public void deliverAll(final MessageSelector selector);
48
49 /**
50 * Returns the {@link Receiver} of this mailbox.
51 *
52 * @return the receiver
53 */
54 public Receiver getReceiver();
55
56 /**
57 * Returns the {@link CommunicationGroup} of the receiver of this mailbox.
58 *
59 * @return the communication group
60 */
61 public CommunicationGroup getCurrentGroup();
62
63 /**
64 * Sets the {@link CommunicationGroup} that the receiver of this mailbox is associated with.
65 *
66 * @param group
67 * the communication group
68 */
69 public void setCurrentGroup(final CommunicationGroup group);
70
71 /**
72 * Returns true if this mailbox is empty.
73 *
74 * @return
75 */
76 public boolean isEmpty();
77
78}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/MessageIndexerFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/MessageIndexerFactory.java
new file mode 100644
index 00000000..2c5255fb
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/MessageIndexerFactory.java
@@ -0,0 +1,23 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, 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.rete.network.mailbox;
10
11import tools.refinery.viatra.runtime.rete.network.indexer.MessageIndexer;
12
13/**
14 * A factory used to create message indexers for {@link Mailbox}es.
15 *
16 * @author Tamas Szabo
17 * @since 2.0
18 */
19public interface MessageIndexerFactory<I extends MessageIndexer> {
20
21 public I create();
22
23}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/AbstractUpdateSplittingMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/AbstractUpdateSplittingMailbox.java
new file mode 100644
index 00000000..1e1ada71
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/AbstractUpdateSplittingMailbox.java
@@ -0,0 +1,109 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, 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.rete.network.mailbox.timeless;
10
11import tools.refinery.viatra.runtime.rete.network.Receiver;
12import tools.refinery.viatra.runtime.rete.network.ReteContainer;
13import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
14import tools.refinery.viatra.runtime.rete.network.indexer.MessageIndexer;
15import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
16import tools.refinery.viatra.runtime.rete.network.mailbox.MessageIndexerFactory;
17
18/**
19 * An abstract mailbox implementation that is capable of splitting update messages based on some form of monotonicity
20 * (anti-monotone and monotone). The monotonicity is either defined by the less or equal operator of a poset or, it can
21 * be the standard subset ordering among sets of tuples.
22 *
23 * @author Tamas Szabo
24 * @since 2.0
25 *
26 */
27public abstract class AbstractUpdateSplittingMailbox<IndexerType extends MessageIndexer, ReceiverType extends Receiver> implements Mailbox {
28
29 protected IndexerType monotoneQueue;
30 protected IndexerType antiMonotoneQueue;
31 protected IndexerType monotoneBuffer;
32 protected IndexerType antiMonotoneBuffer;
33 protected boolean deliveringMonotone;
34 protected boolean deliveringAntiMonotone;
35 protected final ReceiverType receiver;
36 protected final ReteContainer container;
37 protected CommunicationGroup group;
38
39 public AbstractUpdateSplittingMailbox(final ReceiverType receiver, final ReteContainer container,
40 final MessageIndexerFactory<IndexerType> factory) {
41 this.receiver = receiver;
42 this.container = container;
43 this.monotoneQueue = factory.create();
44 this.antiMonotoneQueue = factory.create();
45 this.monotoneBuffer = factory.create();
46 this.antiMonotoneBuffer = factory.create();
47 this.deliveringMonotone = false;
48 this.deliveringAntiMonotone = false;
49 }
50
51 protected void swapAndClearMonotone() {
52 final IndexerType tmp = this.monotoneQueue;
53 this.monotoneQueue = this.monotoneBuffer;
54 this.monotoneBuffer = tmp;
55 this.monotoneBuffer.clear();
56 }
57
58 protected void swapAndClearAntiMonotone() {
59 final IndexerType tmp = this.antiMonotoneQueue;
60 this.antiMonotoneQueue = this.antiMonotoneBuffer;
61 this.antiMonotoneBuffer = tmp;
62 this.antiMonotoneBuffer.clear();
63 }
64
65 protected IndexerType getActiveMonotoneQueue() {
66 if (this.deliveringMonotone) {
67 return this.monotoneBuffer;
68 } else {
69 return this.monotoneQueue;
70 }
71 }
72
73 protected IndexerType getActiveAntiMonotoneQueue() {
74 if (this.deliveringAntiMonotone) {
75 return this.antiMonotoneBuffer;
76 } else {
77 return this.antiMonotoneQueue;
78 }
79 }
80
81 @Override
82 public ReceiverType getReceiver() {
83 return this.receiver;
84 }
85
86 @Override
87 public void clear() {
88 this.monotoneQueue.clear();
89 this.antiMonotoneQueue.clear();
90 this.monotoneBuffer.clear();
91 this.antiMonotoneBuffer.clear();
92 }
93
94 @Override
95 public boolean isEmpty() {
96 return this.getActiveMonotoneQueue().isEmpty() && this.getActiveAntiMonotoneQueue().isEmpty();
97 }
98
99 @Override
100 public CommunicationGroup getCurrentGroup() {
101 return this.group;
102 }
103
104 @Override
105 public void setCurrentGroup(final CommunicationGroup group) {
106 this.group = group;
107 }
108
109}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/BehaviorChangingMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/BehaviorChangingMailbox.java
new file mode 100644
index 00000000..fe822d7c
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/BehaviorChangingMailbox.java
@@ -0,0 +1,117 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.rete.network.mailbox.timeless;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import tools.refinery.viatra.runtime.matchers.util.Direction;
13import tools.refinery.viatra.runtime.rete.network.Node;
14import tools.refinery.viatra.runtime.rete.network.Receiver;
15import tools.refinery.viatra.runtime.rete.network.ReteContainer;
16import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
17import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
18import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20import tools.refinery.viatra.runtime.rete.network.communication.timeless.TimelessCommunicationTracker;
21import tools.refinery.viatra.runtime.rete.network.mailbox.AdaptableMailbox;
22import tools.refinery.viatra.runtime.rete.network.mailbox.FallThroughCapableMailbox;
23
24/**
25 * This mailbox changes its behavior based on the position of its {@link Receiver} in the network topology.
26 * It either behaves as a {@link DefaultMailbox} or as an {@link UpdateSplittingMailbox}. The decision is made by the
27 * {@link CommunicationTracker}, see {@link TimelessCommunicationTracker#postProcessNode(Node)} for more details.
28 *
29 * @author Tamas Szabo
30 */
31public class BehaviorChangingMailbox implements FallThroughCapableMailbox {
32
33 protected boolean fallThrough;
34 protected boolean split;
35 protected AdaptableMailbox wrapped;
36 protected final Receiver receiver;
37 protected final ReteContainer container;
38 protected CommunicationGroup group;
39
40 public BehaviorChangingMailbox(final Receiver receiver, final ReteContainer container) {
41 this.fallThrough = false;
42 this.split = false;
43 this.receiver = receiver;
44 this.container = container;
45 this.wrapped = new DefaultMailbox(receiver, container);
46 this.wrapped.setAdapter(this);
47 }
48
49 @Override
50 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) {
51 if (this.fallThrough && !this.container.isExecutingDelayedCommands()) {
52 // disable fall through while we are in the middle of executing delayed construction commands
53 this.receiver.update(direction, update, timestamp);
54 } else {
55 this.wrapped.postMessage(direction, update, timestamp);
56 }
57 }
58
59 @Override
60 public void deliverAll(final MessageSelector kind) {
61 this.wrapped.deliverAll(kind);
62 }
63
64 @Override
65 public String toString() {
66 return "A_MBOX -> " + this.wrapped;
67 }
68
69 public void setSplitFlag(final boolean splitValue) {
70 if (this.split != splitValue) {
71 assert isEmpty();
72 if (splitValue) {
73 this.wrapped = new UpdateSplittingMailbox(this.receiver, this.container);
74 } else {
75 this.wrapped = new DefaultMailbox(this.receiver, this.container);
76 }
77 this.wrapped.setAdapter(this);
78 this.split = splitValue;
79 }
80 }
81
82 @Override
83 public boolean isEmpty() {
84 return this.wrapped.isEmpty();
85 }
86
87 @Override
88 public void clear() {
89 this.wrapped.clear();
90 }
91
92 @Override
93 public Receiver getReceiver() {
94 return this.receiver;
95 }
96
97 @Override
98 public CommunicationGroup getCurrentGroup() {
99 return this.group;
100 }
101
102 @Override
103 public void setCurrentGroup(final CommunicationGroup group) {
104 this.group = group;
105 }
106
107 @Override
108 public boolean isFallThrough() {
109 return this.fallThrough;
110 }
111
112 @Override
113 public void setFallThrough(final boolean fallThrough) {
114 this.fallThrough = fallThrough;
115 }
116
117} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/DefaultMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/DefaultMailbox.java
new file mode 100644
index 00000000..5c72ba39
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/DefaultMailbox.java
@@ -0,0 +1,164 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.rete.network.mailbox.timeless;
10
11import java.util.Map;
12import java.util.Map.Entry;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.rete.network.Receiver;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
20import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
21import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector;
22import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
23import tools.refinery.viatra.runtime.rete.network.mailbox.AdaptableMailbox;
24import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
25
26/**
27 * Default mailbox implementation.
28 * <p>
29 * Usually, the mailbox performs counting of messages so that they can cancel each other out. However, if marked as a
30 * fall-through mailbox, than update messages are delivered directly to the receiver node to reduce overhead.
31 *
32 * @author Tamas Szabo
33 * @since 2.0
34 */
35public class DefaultMailbox implements AdaptableMailbox {
36
37 private static int SIZE_TRESHOLD = 127;
38
39 protected Map<Tuple, Integer> queue;
40 protected Map<Tuple, Integer> buffer;
41 protected final Receiver receiver;
42 protected final ReteContainer container;
43 protected boolean delivering;
44 protected Mailbox adapter;
45 protected CommunicationGroup group;
46
47 public DefaultMailbox(final Receiver receiver, final ReteContainer container) {
48 this.receiver = receiver;
49 this.container = container;
50 this.queue = CollectionsFactory.createMap();
51 this.buffer = CollectionsFactory.createMap();
52 this.adapter = this;
53 }
54
55 protected Map<Tuple, Integer> getActiveQueue() {
56 if (this.delivering) {
57 return this.buffer;
58 } else {
59 return this.queue;
60 }
61 }
62
63 @Override
64 public Mailbox getAdapter() {
65 return this.adapter;
66 }
67
68 @Override
69 public void setAdapter(final Mailbox adapter) {
70 this.adapter = adapter;
71 }
72
73 @Override
74 public boolean isEmpty() {
75 return getActiveQueue().isEmpty();
76 }
77
78 @Override
79 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) {
80 final Map<Tuple, Integer> activeQueue = getActiveQueue();
81 final boolean wasEmpty = activeQueue.isEmpty();
82
83 boolean significantChange = false;
84 Integer count = activeQueue.get(update);
85 if (count == null) {
86 count = 0;
87 significantChange = true;
88 }
89
90 if (direction == Direction.DELETE) {
91 count--;
92 } else {
93 count++;
94 }
95
96 if (count == 0) {
97 activeQueue.remove(update);
98 significantChange = true;
99 } else {
100 activeQueue.put(update, count);
101 }
102
103 if (significantChange) {
104 final Mailbox targetMailbox = this.adapter;
105 final CommunicationGroup targetGroup = this.adapter.getCurrentGroup();
106
107 if (wasEmpty) {
108 targetGroup.notifyHasMessage(targetMailbox, PhasedSelector.DEFAULT);
109 } else if (activeQueue.isEmpty()) {
110 targetGroup.notifyLostAllMessages(targetMailbox, PhasedSelector.DEFAULT);
111 }
112 }
113 }
114
115 @Override
116 public void deliverAll(final MessageSelector kind) {
117 if (kind == PhasedSelector.DEFAULT) {
118 // use the buffer during delivering so that there is a clear
119 // separation between the stages
120 this.delivering = true;
121 this.receiver.batchUpdate(this.queue.entrySet(), Timestamp.ZERO);
122 this.delivering = false;
123
124 if (queue.size() > SIZE_TRESHOLD) {
125 this.queue = this.buffer;
126 this.buffer = CollectionsFactory.createMap();
127 } else {
128 this.queue.clear();
129 final Map<Tuple, Integer> tmpQueue = this.queue;
130 this.queue = this.buffer;
131 this.buffer = tmpQueue;
132 }
133 } else {
134 throw new IllegalArgumentException("Unsupported message kind " + kind);
135 }
136 }
137
138 @Override
139 public String toString() {
140 return "D_MBOX (" + this.receiver + ") " + this.getActiveQueue();
141 }
142
143 @Override
144 public Receiver getReceiver() {
145 return this.receiver;
146 }
147
148 @Override
149 public void clear() {
150 this.queue.clear();
151 this.buffer.clear();
152 }
153
154 @Override
155 public CommunicationGroup getCurrentGroup() {
156 return this.group;
157 }
158
159 @Override
160 public void setCurrentGroup(final CommunicationGroup group) {
161 this.group = group;
162 }
163
164}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/PosetAwareMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/PosetAwareMailbox.java
new file mode 100644
index 00000000..50d19882
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/PosetAwareMailbox.java
@@ -0,0 +1,218 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.rete.network.mailbox.timeless;
10
11import java.util.HashSet;
12import java.util.Map.Entry;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19import tools.refinery.viatra.runtime.matchers.util.Direction;
20import tools.refinery.viatra.runtime.rete.network.PosetAwareReceiver;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
23import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector;
24import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
25import tools.refinery.viatra.runtime.rete.network.indexer.GroupBasedMessageIndexer;
26
27/**
28 * A monotonicity aware mailbox implementation. The mailbox uses an {@link IPosetComparator} to identify if a pair of
29 * REVOKE - INSERT updates represent a monotone change pair. The mailbox is used by {@link PosetAwareReceiver}s.
30 *
31 * @author Tamas Szabo
32 * @since 2.0
33 */
34public class PosetAwareMailbox extends AbstractUpdateSplittingMailbox<GroupBasedMessageIndexer, PosetAwareReceiver> {
35
36 protected final TupleMask groupMask;
37
38 public PosetAwareMailbox(final PosetAwareReceiver receiver, final ReteContainer container) {
39 super(receiver, container, () -> new GroupBasedMessageIndexer(receiver.getCoreMask()));
40 this.groupMask = receiver.getCoreMask();
41 }
42
43 @Override
44 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) {
45 final GroupBasedMessageIndexer monotoneQueue = getActiveMonotoneQueue();
46 final GroupBasedMessageIndexer antiMonotoneQueue = getActiveAntiMonotoneQueue();
47 final boolean wasPresentAsMonotone = monotoneQueue.getCount(update) != 0;
48 final boolean wasPresentAsAntiMonotone = antiMonotoneQueue.getCount(update) != 0;
49 final TupleMask coreMask = this.receiver.getCoreMask();
50
51 // it cannot happen that it was present in both
52 assert !(wasPresentAsMonotone && wasPresentAsAntiMonotone);
53
54 if (direction == Direction.INSERT) {
55 if (wasPresentAsAntiMonotone) {
56 // it was an anti-monotone one before
57 antiMonotoneQueue.insert(update);
58 } else {
59 // it was a monotone one before or did not exist at all
60 monotoneQueue.insert(update);
61
62 // if it was not present in the monotone queue before, then
63 // we need to check whether it makes REVOKE updates monotone
64 if (!wasPresentAsMonotone) {
65 final Set<Tuple> counterParts = tryFindCounterPart(update, false, true);
66 for (final Tuple counterPart : counterParts) {
67 final int count = antiMonotoneQueue.getCount(counterPart);
68 assert count < 0;
69 antiMonotoneQueue.update(counterPart, -count);
70 monotoneQueue.update(counterPart, count);
71 }
72 }
73 }
74 } else {
75 if (wasPresentAsAntiMonotone) {
76 // it was an anti-monotone one before
77 antiMonotoneQueue.delete(update);
78 } else if (wasPresentAsMonotone) {
79 // it was a monotone one before
80 monotoneQueue.delete(update);
81
82 // and we need to check whether the monotone REVOKE updates
83 // still have a reinforcing counterpart
84 final Set<Tuple> candidates = new HashSet<Tuple>();
85 final Tuple key = coreMask.transform(update);
86 for (final Entry<Tuple, Integer> entry : monotoneQueue.getTuplesByGroup(key).entrySet()) {
87 if (entry.getValue() < 0) {
88 final Tuple candidate = entry.getKey();
89 final Set<Tuple> counterParts = tryFindCounterPart(candidate, true, false);
90 if (counterParts.isEmpty()) {
91 // all of them are gone
92 candidates.add(candidate);
93 }
94 }
95 }
96
97 // move the candidates from the monotone queue to the
98 // anti-monotone queue because they do not have a
99 // counterpart anymore
100 for (final Tuple candidate : candidates) {
101 final int count = monotoneQueue.getCount(candidate);
102 assert count < 0;
103 monotoneQueue.update(candidate, -count);
104 antiMonotoneQueue.update(candidate, count);
105 }
106 } else {
107 // it did not exist before
108 final Set<Tuple> counterParts = tryFindCounterPart(update, true, false);
109 if (counterParts.isEmpty()) {
110 // there is no tuple that would make this update monotone
111 antiMonotoneQueue.delete(update);
112 } else {
113 // there is a reinforcing counterpart
114 monotoneQueue.delete(update);
115 }
116 }
117 }
118
119 if (antiMonotoneQueue.isEmpty()) {
120 this.group.notifyLostAllMessages(this, PhasedSelector.ANTI_MONOTONE);
121 } else {
122 this.group.notifyHasMessage(this, PhasedSelector.ANTI_MONOTONE);
123 }
124
125 if (monotoneQueue.isEmpty()) {
126 this.group.notifyLostAllMessages(this, PhasedSelector.MONOTONE);
127 } else {
128 this.group.notifyHasMessage(this, PhasedSelector.MONOTONE);
129 }
130 }
131
132 protected Set<Tuple> tryFindCounterPart(final Tuple first, final boolean findPositiveCounterPart,
133 final boolean findAllCounterParts) {
134 final GroupBasedMessageIndexer monotoneQueue = getActiveMonotoneQueue();
135 final GroupBasedMessageIndexer antiMonotoneQueue = getActiveAntiMonotoneQueue();
136 final TupleMask coreMask = this.receiver.getCoreMask();
137 final TupleMask posetMask = this.receiver.getPosetMask();
138 final IPosetComparator posetComparator = this.receiver.getPosetComparator();
139 final Set<Tuple> result = CollectionsFactory.createSet();
140 final Tuple firstKey = coreMask.transform(first);
141 final Tuple firstValue = posetMask.transform(first);
142
143 if (findPositiveCounterPart) {
144 for (final Entry<Tuple, Integer> entry : monotoneQueue.getTuplesByGroup(firstKey).entrySet()) {
145 final Tuple secondValue = posetMask.transform(entry.getKey());
146 if (entry.getValue() > 0 && posetComparator.isLessOrEqual(firstValue, secondValue)) {
147 result.add(entry.getKey());
148 if (!findAllCounterParts) {
149 return result;
150 }
151 }
152 }
153 } else {
154 for (final Entry<Tuple, Integer> entry : antiMonotoneQueue.getTuplesByGroup(firstKey).entrySet()) {
155 final Tuple secondValue = posetMask.transform(entry.getKey());
156 if (posetComparator.isLessOrEqual(secondValue, firstValue)) {
157 result.add(entry.getKey());
158 if (!findAllCounterParts) {
159 return result;
160 }
161 }
162 }
163 }
164
165 return result;
166 }
167
168 @Override
169 public void deliverAll(final MessageSelector kind) {
170 if (kind == PhasedSelector.ANTI_MONOTONE) {
171 // use the buffer during delivering so that there is a clear
172 // separation between the stages
173 this.deliveringAntiMonotone = true;
174
175 for (final Tuple group : this.antiMonotoneQueue.getGroups()) {
176 for (final Entry<Tuple, Integer> entry : this.antiMonotoneQueue.getTuplesByGroup(group).entrySet()) {
177 final Tuple update = entry.getKey();
178 final int count = entry.getValue();
179 assert count < 0;
180 for (int i = 0; i < Math.abs(count); i++) {
181 this.receiver.updateWithPosetInfo(Direction.DELETE, update, false);
182 }
183 }
184 }
185
186 this.deliveringAntiMonotone = false;
187 swapAndClearAntiMonotone();
188 } else if (kind == PhasedSelector.MONOTONE) {
189 // use the buffer during delivering so that there is a clear
190 // separation between the stages
191 this.deliveringMonotone = true;
192
193 for (final Tuple group : this.monotoneQueue.getGroups()) {
194 for (final Entry<Tuple, Integer> entry : this.monotoneQueue.getTuplesByGroup(group).entrySet()) {
195 final Tuple update = entry.getKey();
196 final int count = entry.getValue();
197 assert count != 0;
198 final Direction direction = count < 0 ? Direction.DELETE : Direction.INSERT;
199 for (int i = 0; i < Math.abs(count); i++) {
200 this.receiver.updateWithPosetInfo(direction, update, true);
201 }
202 }
203 }
204
205 this.deliveringMonotone = false;
206 swapAndClearMonotone();
207 } else {
208 throw new IllegalArgumentException("Unsupported message kind " + kind);
209 }
210 }
211
212 @Override
213 public String toString() {
214 return "PA_MBOX (" + this.receiver + ") " + this.getActiveMonotoneQueue() + " "
215 + this.getActiveAntiMonotoneQueue();
216 }
217
218}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/UpdateSplittingMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/UpdateSplittingMailbox.java
new file mode 100644
index 00000000..afa155b2
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/UpdateSplittingMailbox.java
@@ -0,0 +1,135 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.rete.network.mailbox.timeless;
10
11import java.util.Map.Entry;
12
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import tools.refinery.viatra.runtime.matchers.util.Direction;
15import tools.refinery.viatra.runtime.rete.network.Receiver;
16import tools.refinery.viatra.runtime.rete.network.ReteContainer;
17import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
18import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
19import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21import tools.refinery.viatra.runtime.rete.network.indexer.DefaultMessageIndexer;
22import tools.refinery.viatra.runtime.rete.network.mailbox.AdaptableMailbox;
23import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
24
25/**
26 * A mailbox implementation that splits updates messages according to the standard subset ordering into anti-monotonic
27 * (deletions) and monotonic (insertions) updates.
28 *
29 * @author Tamas Szabo
30 * @since 2.0
31 */
32public class UpdateSplittingMailbox extends AbstractUpdateSplittingMailbox<DefaultMessageIndexer, Receiver>
33 implements AdaptableMailbox {
34
35 protected Mailbox adapter;
36
37 public UpdateSplittingMailbox(final Receiver receiver, final ReteContainer container) {
38 super(receiver, container, DefaultMessageIndexer::new);
39 this.adapter = this;
40 }
41
42 @Override
43 public Mailbox getAdapter() {
44 return this.adapter;
45 }
46
47 @Override
48 public void setAdapter(final Mailbox adapter) {
49 this.adapter = adapter;
50 }
51
52 @Override
53 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) {
54 final DefaultMessageIndexer monotoneQueue = getActiveMonotoneQueue();
55 final DefaultMessageIndexer antiMonotoneQueue = getActiveAntiMonotoneQueue();
56 final boolean wasPresentAsMonotone = monotoneQueue.getCount(update) != 0;
57 final boolean wasPresentAsAntiMonotone = antiMonotoneQueue.getCount(update) != 0;
58
59 // it cannot happen that it was present in both
60 assert !(wasPresentAsMonotone && wasPresentAsAntiMonotone);
61
62 if (direction == Direction.INSERT) {
63 if (wasPresentAsAntiMonotone) {
64 // it was an anti-monotone one before
65 antiMonotoneQueue.insert(update);
66 } else {
67 // it was a monotone one before or did not exist at all
68 monotoneQueue.insert(update);
69 }
70 } else {
71 if (wasPresentAsMonotone) {
72 // it was a monotone one before
73 monotoneQueue.delete(update);
74 } else {
75 // it was an anti-monotone one before or did not exist at all
76 antiMonotoneQueue.delete(update);
77 }
78 }
79
80 final Mailbox targetMailbox = this.adapter;
81 final CommunicationGroup targetGroup = this.adapter.getCurrentGroup();
82
83 if (antiMonotoneQueue.isEmpty()) {
84 targetGroup.notifyLostAllMessages(targetMailbox, PhasedSelector.ANTI_MONOTONE);
85 } else {
86 targetGroup.notifyHasMessage(targetMailbox, PhasedSelector.ANTI_MONOTONE);
87 }
88
89 if (monotoneQueue.isEmpty()) {
90 targetGroup.notifyLostAllMessages(targetMailbox, PhasedSelector.MONOTONE);
91 } else {
92 targetGroup.notifyHasMessage(targetMailbox, PhasedSelector.MONOTONE);
93 }
94 }
95
96 @Override
97 public void deliverAll(final MessageSelector kind) {
98 if (kind == PhasedSelector.ANTI_MONOTONE) {
99 // deliver anti-monotone
100 this.deliveringAntiMonotone = true;
101 for (final Entry<Tuple, Integer> entry : this.antiMonotoneQueue.getTuples().entrySet()) {
102 final Tuple update = entry.getKey();
103 final int count = entry.getValue();
104 assert count < 0;
105 for (int i = 0; i < Math.abs(count); i++) {
106 this.receiver.update(Direction.DELETE, update, Timestamp.ZERO);
107 }
108 }
109 this.deliveringAntiMonotone = false;
110 swapAndClearAntiMonotone();
111 } else if (kind == PhasedSelector.MONOTONE) {
112 // deliver monotone
113 this.deliveringMonotone = true;
114 for (final Entry<Tuple, Integer> entry : this.monotoneQueue.getTuples().entrySet()) {
115 final Tuple update = entry.getKey();
116 final int count = entry.getValue();
117 assert count > 0;
118 for (int i = 0; i < count; i++) {
119 this.receiver.update(Direction.INSERT, update, Timestamp.ZERO);
120 }
121 }
122 this.deliveringMonotone = false;
123 swapAndClearMonotone();
124 } else {
125 throw new IllegalArgumentException("Unsupported message kind " + kind);
126 }
127 }
128
129 @Override
130 public String toString() {
131 return "US_MBOX (" + this.receiver + ") " + this.getActiveMonotoneQueue() + " "
132 + this.getActiveAntiMonotoneQueue();
133 }
134
135}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timely/TimelyMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timely/TimelyMailbox.java
new file mode 100644
index 00000000..bf3b8e14
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timely/TimelyMailbox.java
@@ -0,0 +1,150 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, 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.rete.network.mailbox.timely;
10
11import java.util.Map;
12import java.util.TreeMap;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
18import tools.refinery.viatra.runtime.rete.network.Receiver;
19import tools.refinery.viatra.runtime.rete.network.ReteContainer;
20import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
21import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
22import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
23import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
24import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
25
26public class TimelyMailbox implements Mailbox {
27
28 protected TreeMap<Timestamp, Map<Tuple, Integer>> queue;
29 protected final Receiver receiver;
30 protected final ReteContainer container;
31 protected CommunicationGroup group;
32 protected boolean fallThrough;
33
34 public TimelyMailbox(final Receiver receiver, final ReteContainer container) {
35 this.receiver = receiver;
36 this.container = container;
37 this.queue = CollectionsFactory.createTreeMap();
38 }
39
40 protected TreeMap<Timestamp, Map<Tuple, Integer>> getActiveQueue() {
41 return this.queue;
42 }
43
44 @Override
45 public boolean isEmpty() {
46 return getActiveQueue().isEmpty();
47 }
48
49 @Override
50 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) {
51 final TreeMap<Timestamp, Map<Tuple, Integer>> activeQueue = getActiveQueue();
52
53 Map<Tuple, Integer> tupleMap = activeQueue.get(timestamp);
54 final boolean wasEmpty = tupleMap == null;
55 boolean significantChange = false;
56
57 if (tupleMap == null) {
58 tupleMap = CollectionsFactory.createMap();
59 activeQueue.put(timestamp, tupleMap);
60 significantChange = true;
61 }
62
63 Integer count = tupleMap.get(update);
64 if (count == null) {
65 count = 0;
66 significantChange = true;
67 }
68
69 if (direction == Direction.DELETE) {
70 count--;
71 } else {
72 count++;
73 }
74
75 if (count == 0) {
76 tupleMap.remove(update);
77 if (tupleMap.isEmpty()) {
78 activeQueue.remove(timestamp);
79 }
80 significantChange = true;
81 } else {
82 tupleMap.put(update, count);
83 }
84
85 if (significantChange) {
86 if (wasEmpty) {
87 this.group.notifyHasMessage(this, timestamp);
88 } else if (tupleMap.isEmpty()) {
89 final Timestamp resumableTimestamp = (this.receiver instanceof ResumableNode)
90 ? ((ResumableNode) this.receiver).getResumableTimestamp()
91 : null;
92 // check if there is folding left to do before unsubscribing just based on the message queue being empty
93 if (resumableTimestamp == null || resumableTimestamp.compareTo(timestamp) != 0) {
94 this.group.notifyLostAllMessages(this, timestamp);
95 }
96 }
97 }
98 }
99
100 @Override
101 public void deliverAll(final MessageSelector selector) {
102 if (selector instanceof Timestamp) {
103 final Timestamp timestamp = (Timestamp) selector;
104 // REMOVE the tuples associated with the selector, dont just query them
105 final Map<Tuple, Integer> tupleMap = this.queue.remove(timestamp);
106
107 // tupleMap may be empty if we only have lazy folding to do
108 if (tupleMap != null) {
109 this.receiver.batchUpdate(tupleMap.entrySet(), timestamp);
110 }
111
112 if (this.container.getTimelyConfiguration()
113 .getTimelineRepresentation() == TimelineRepresentation.FAITHFUL) {
114 // (1) either normal delivery, which ended up being a lazy folding state
115 // (2) and/or lazy folding needs to be resumed
116 if (this.receiver instanceof ResumableNode) {
117 ((ResumableNode) this.receiver).resumeAt(timestamp);
118 }
119 }
120 } else {
121 throw new IllegalArgumentException("Unsupported message selector " + selector);
122 }
123 }
124
125 @Override
126 public String toString() {
127 return "DDF_MBOX (" + this.receiver + ") " + this.getActiveQueue();
128 }
129
130 @Override
131 public Receiver getReceiver() {
132 return this.receiver;
133 }
134
135 @Override
136 public void clear() {
137 this.queue.clear();
138 }
139
140 @Override
141 public CommunicationGroup getCurrentGroup() {
142 return this.group;
143 }
144
145 @Override
146 public void setCurrentGroup(final CommunicationGroup group) {
147 this.group = group;
148 }
149
150}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/Address.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/Address.java
new file mode 100644
index 00000000..2fed3225
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/Address.java
@@ -0,0 +1,125 @@
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.rete.remote;
11
12import tools.refinery.viatra.runtime.rete.network.Node;
13import tools.refinery.viatra.runtime.rete.network.ReteContainer;
14
15/**
16 * Remote identifier of a node of type T.
17 *
18 * @author Gabor Bergmann
19 *
20 */
21public class Address<T extends Node> {
22 ReteContainer container;
23 Long nodeId;
24 /**
25 * Feel free to leave null e.g. if node is in a separate JVM.
26 */
27 T nodeCache;
28
29 /**
30 * Address of local node (use only for containers in the same VM!)
31 */
32 public static <N extends Node> Address<N> of(N node) {
33 return new Address<N>(node);
34 }
35
36 /**
37 * General constructor.
38 *
39 * @param container
40 * @param nodeId
41 */
42 public Address(ReteContainer container, Long nodeId) {
43 super();
44 this.container = container;
45 this.nodeId = nodeId;
46 }
47
48 /**
49 * Local-only constructor. (use only for containers in the same VM!)
50 *
51 * @param node
52 * the node to address
53 */
54 public Address(T node) {
55 super();
56 this.nodeCache = node;
57 this.container = node.getContainer();
58 this.nodeId = node.getNodeId();
59 }
60
61 @Override
62 public int hashCode() {
63 final int prime = 31;
64 int result = 1;
65 result = prime * result + ((container == null) ? 0 : container.hashCode());
66 result = prime * result + ((nodeId == null) ? 0 : nodeId.hashCode());
67 return result;
68 }
69
70 @Override
71 public boolean equals(Object obj) {
72 if (this == obj)
73 return true;
74 if (obj == null)
75 return false;
76 if (!(obj instanceof Address<?>))
77 return false;
78 final Address<?> other = (Address<?>) obj;
79 if (container == null) {
80 if (other.container != null)
81 return false;
82 } else if (!container.equals(other.container))
83 return false;
84 if (nodeId == null) {
85 if (other.nodeId != null)
86 return false;
87 } else if (!nodeId.equals(other.nodeId))
88 return false;
89 return true;
90 }
91
92 public ReteContainer getContainer() {
93 return container;
94 }
95
96 public void setContainer(ReteContainer container) {
97 this.container = container;
98 }
99
100 public Long getNodeId() {
101 return nodeId;
102 }
103
104 public void setNodeId(Long nodeId) {
105 this.nodeId = nodeId;
106 }
107
108 public T getNodeCache() {
109 return nodeCache;
110 }
111
112 public void setNodeCache(T nodeCache) {
113 this.nodeCache = nodeCache;
114 }
115
116 @Override
117 public String toString() {
118 if (nodeCache == null)
119 return "A(" + nodeId + " @ " + container + ")";
120 else
121 return "A(" + nodeCache + ")";
122
123 }
124
125}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteReceiver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteReceiver.java
new file mode 100644
index 00000000..f7d267af
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteReceiver.java
@@ -0,0 +1,63 @@
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.rete.remote;
11
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.List;
15import java.util.Map;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
20import tools.refinery.viatra.runtime.rete.network.Receiver;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
23import tools.refinery.viatra.runtime.rete.single.SingleInputNode;
24
25/**
26 * This node delivers updates to a remote recipient; no updates are propagated further in this network.
27 *
28 * @author Gabor Bergmann
29 *
30 */
31public class RemoteReceiver extends SingleInputNode {
32
33 List<Address<? extends Receiver>> targets;
34
35 public RemoteReceiver(ReteContainer reteContainer) {
36 super(reteContainer);
37 targets = new ArrayList<Address<? extends Receiver>>();
38 }
39
40 public void addTarget(Address<? extends Receiver> target) {
41 targets.add(target);
42 }
43
44 @Override
45 public void pullInto(Collection<Tuple> collector, boolean flush) {
46 propagatePullInto(collector, flush);
47 }
48
49 @Override
50 public void pullIntoWithTimeline(Map<Tuple, Timeline<Timestamp>> collector, boolean flush) {
51 throw new UnsupportedOperationException();
52 }
53
54 public Collection<Tuple> remotePull(boolean flush) {
55 return reteContainer.pullContents(this, flush);
56 }
57
58 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
59 for (Address<? extends Receiver> ad : targets)
60 reteContainer.sendUpdateToRemoteAddress(ad, direction, updateElement);
61 }
62
63}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteSupplier.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteSupplier.java
new file mode 100644
index 00000000..cbe4d177
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteSupplier.java
@@ -0,0 +1,54 @@
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.rete.remote;
11
12import java.util.Collection;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20import tools.refinery.viatra.runtime.rete.single.SingleInputNode;
21
22/**
23 * This node receives updates from a remote supplier; no local updates are expected.
24 *
25 * @author Gabor Bergmann
26 *
27 */
28public class RemoteSupplier extends SingleInputNode {
29
30 RemoteReceiver counterpart;
31
32 public RemoteSupplier(ReteContainer reteContainer, RemoteReceiver counterpart) {
33 super(reteContainer);
34 this.counterpart = counterpart;
35 counterpart.addTarget(reteContainer.makeAddress(this));
36 }
37
38 @Override
39 public void pullInto(Collection<Tuple> collector, boolean flush) {
40 Collection<Tuple> pulled = counterpart.remotePull(flush);
41 collector.addAll(pulled);
42 }
43
44 @Override
45 public void pullIntoWithTimeline(Map<Tuple, Timeline<Timestamp>> collector, boolean flush) {
46 throw new UnsupportedOperationException();
47 }
48
49 @Override
50 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
51 propagateUpdate(direction, updateElement, timestamp);
52 }
53
54}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/AbstractUniquenessEnforcerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/AbstractUniquenessEnforcerNode.java
new file mode 100644
index 00000000..e92ce63f
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/AbstractUniquenessEnforcerNode.java
@@ -0,0 +1,138 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.rete.single;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.List;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
20import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer.ListenerSubscription;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.StandardNode;
23import tools.refinery.viatra.runtime.rete.network.Supplier;
24import tools.refinery.viatra.runtime.rete.network.Tunnel;
25import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
26import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
27import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
28import tools.refinery.viatra.runtime.rete.util.Options;
29
30/**
31 * Ensures that no identical copies get to the output. Only one replica of each pattern substitution may traverse this
32 * node. There are both timeless and timely implementations.
33 *
34 * @author Gabor Bergmann
35 * @author Tamas Szabo
36 * @noinstantiate This class is not intended to be instantiated by clients.
37 * @noextend This class is not intended to be subclassed by clients.
38 * @since 2.2
39 */
40public abstract class AbstractUniquenessEnforcerNode extends StandardNode implements Tunnel {
41
42 protected final Collection<Supplier> parents;
43 protected ProjectionIndexer memoryNullIndexer;
44 protected ProjectionIndexer memoryIdentityIndexer;
45 protected final int tupleWidth;
46 // MUST BE INSTANTIATED IN THE CONCRETE SUBCLASSES AFTER ALL FIELDS ARE SET
47 protected Mailbox mailbox;
48 protected final TupleMask nullMask;
49 protected final TupleMask identityMask;
50 protected final List<ListenerSubscription> specializedListeners;
51
52 public AbstractUniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth) {
53 super(reteContainer);
54 this.parents = new ArrayList<Supplier>();
55 this.specializedListeners = new ArrayList<ListenerSubscription>();
56 this.tupleWidth = tupleWidth;
57 this.nullMask = TupleMask.linear(0, tupleWidth);
58 this.identityMask = TupleMask.identity(tupleWidth);
59 }
60
61 protected abstract Mailbox instantiateMailbox();
62
63 @Override
64 public Mailbox getMailbox() {
65 return this.mailbox;
66 }
67
68 /**
69 * @since 2.8
70 */
71 public abstract Set<Tuple> getTuples();
72
73 /**
74 * @since 2.4
75 */
76 protected void propagate(final Direction direction, final Tuple update, final Timestamp timestamp) {
77 // See Bug 518434
78 // trivial (non-active) indexers must be updated before other listeners
79 // so that if they are joined against each other, trivial indexers lookups
80 // will be consistent with their notifications;
81 // also, their subscriptions must share a single order
82 for (final ListenerSubscription subscription : specializedListeners) {
83 subscription.propagate(direction, update, timestamp);
84 }
85 propagateUpdate(direction, update, timestamp);
86 }
87
88 @Override
89 public ProjectionIndexer constructIndex(final TupleMask mask, final TraceInfo... traces) {
90 if (Options.employTrivialIndexers) {
91 if (nullMask.equals(mask)) {
92 final ProjectionIndexer indexer = getNullIndexer();
93 for (final TraceInfo traceInfo : traces) {
94 indexer.assignTraceInfo(traceInfo);
95 }
96 return indexer;
97 }
98 if (identityMask.equals(mask)) {
99 final ProjectionIndexer indexer = getIdentityIndexer();
100 for (final TraceInfo traceInfo : traces) {
101 indexer.assignTraceInfo(traceInfo);
102 }
103 return indexer;
104 }
105 }
106 return super.constructIndex(mask, traces);
107 }
108
109 public abstract ProjectionIndexer getNullIndexer();
110
111 public abstract ProjectionIndexer getIdentityIndexer();
112
113 @Override
114 public void appendParent(final Supplier supplier) {
115 parents.add(supplier);
116 }
117
118 @Override
119 public void removeParent(final Supplier supplier) {
120 parents.remove(supplier);
121 }
122
123 @Override
124 public Collection<Supplier> getParents() {
125 return parents;
126 }
127
128 @Override
129 public void assignTraceInfo(final TraceInfo traceInfo) {
130 super.assignTraceInfo(traceInfo);
131 if (traceInfo.propagateFromStandardNodeToSupplierParent()) {
132 for (final Supplier parent : parents) {
133 parent.acceptPropagatedTraceInfo(traceInfo);
134 }
135 }
136 }
137
138} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/CallbackNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/CallbackNode.java
new file mode 100644
index 00000000..c68036b5
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/CallbackNode.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.rete.single;
10
11import tools.refinery.viatra.runtime.matchers.backend.IUpdateable;
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.matchers.util.Direction;
14import tools.refinery.viatra.runtime.rete.misc.SimpleReceiver;
15import tools.refinery.viatra.runtime.rete.network.ReteContainer;
16import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
17
18/**
19 * @author Bergmann Gabor
20 *
21 */
22public class CallbackNode extends SimpleReceiver {
23
24 IUpdateable updateable;
25
26 public CallbackNode(ReteContainer reteContainer, IUpdateable updateable)
27 {
28 super(reteContainer);
29 this.updateable = updateable;
30 }
31
32 @Override
33 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
34 updateable.update(updateElement, direction == Direction.INSERT);
35 }
36
37}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DefaultProductionNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DefaultProductionNode.java
new file mode 100644
index 00000000..eca8bc17
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DefaultProductionNode.java
@@ -0,0 +1,79 @@
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.rete.single;
11
12import java.util.Iterator;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.rete.network.ProductionNode;
19import tools.refinery.viatra.runtime.rete.network.ReteContainer;
20import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery;
21import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
22
23/**
24 * Default implementation of the Production node, based on UniquenessEnforcerNode
25 *
26 * @author Gabor Bergmann
27 * @noinstantiate This class is not intended to be instantiated by clients.
28 */
29public class DefaultProductionNode extends UniquenessEnforcerNode implements ProductionNode {
30
31 protected final Map<String, Integer> posMapping;
32
33 /**
34 * @since 1.6
35 */
36 public DefaultProductionNode(final ReteContainer reteContainer, final Map<String, Integer> posMapping,
37 final boolean deleteRederiveEvaluation) {
38 this(reteContainer, posMapping, deleteRederiveEvaluation, null, null, null);
39 }
40
41 /**
42 * @since 1.6
43 */
44 public DefaultProductionNode(final ReteContainer reteContainer, final Map<String, Integer> posMapping,
45 final boolean deleteRederiveEvaluation, final TupleMask coreMask, final TupleMask posetMask,
46 final IPosetComparator posetComparator) {
47 super(reteContainer, posMapping.size(), deleteRederiveEvaluation, coreMask, posetMask, posetComparator);
48 this.posMapping = posMapping;
49 }
50
51 @Override
52 public Map<String, Integer> getPosMapping() {
53 return posMapping;
54 }
55
56 @Override
57 public Iterator<Tuple> iterator() {
58 return memory.iterator();
59 }
60
61 @Override
62 public void acceptPropagatedTraceInfo(final TraceInfo traceInfo) {
63 if (traceInfo.propagateToProductionNodeParentAlso()) {
64 super.acceptPropagatedTraceInfo(traceInfo);
65 }
66 }
67
68 @Override
69 public String toString() {
70 for (final TraceInfo traceInfo : this.traceInfos) {
71 if (traceInfo instanceof CompiledQuery) {
72 final String patternName = ((CompiledQuery) traceInfo).getPatternName();
73 return String.format(this.getClass().getName() + "<%s>=%s", patternName, super.toString());
74 }
75 }
76 return super.toString();
77 }
78
79}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorBucketNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorBucketNode.java
new file mode 100644
index 00000000..803bab20
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorBucketNode.java
@@ -0,0 +1,85 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.rete.single;
10
11import java.util.Collection;
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.Supplier;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21
22/**
23 * A bucket holds a filtered set of tuples of its parent {@link DiscriminatorDispatcherNode}.
24 * Exactly those that have the given bucket key at their discrimination column.
25 *
26 * <p> During operation, tuple contents and bucket keys have already been wrapped using {@link IQueryRuntimeContext#wrapElement(Object)}
27 *
28 * @author Gabor Bergmann
29 * @since 1.5
30 */
31public class DiscriminatorBucketNode extends SingleInputNode {
32
33 private Object bucketKey;
34
35 /**
36 * @param bucketKey will be wrapped using {@link IQueryRuntimeContext#wrapElement(Object)}
37
38 */
39 public DiscriminatorBucketNode(ReteContainer reteContainer, Object bucketKey) {
40 super(reteContainer);
41 this.bucketKey = reteContainer.getNetwork().getEngine().getRuntimeContext().wrapElement(bucketKey);
42 }
43
44 @Override
45 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
46 if (parent != null) {
47 getDispatcher().pullIntoFiltered(collector, bucketKey, flush);
48 }
49 }
50
51 @Override
52 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
53 if (parent != null) {
54 getDispatcher().pullIntoWithTimestampFiltered(collector, bucketKey, flush);
55 }
56 }
57
58 @Override
59 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
60 propagateUpdate(direction, updateElement, timestamp);
61 }
62
63 public Object getBucketKey() {
64 return bucketKey;
65 }
66
67 @Override
68 public void appendParent(Supplier supplier) {
69 if (! (supplier instanceof DiscriminatorDispatcherNode))
70 throw new IllegalArgumentException();
71 super.appendParent(supplier);
72 }
73
74 public DiscriminatorDispatcherNode getDispatcher() {
75 return (DiscriminatorDispatcherNode) parent;
76 }
77
78 @Override
79 protected String toStringCore() {
80 return String.format("%s<%s=='%s'>",
81 super.toStringCore(),
82 (getDispatcher() == null) ? "?" : getDispatcher().getDiscriminationColumnIndex(),
83 bucketKey);
84 }
85}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorDispatcherNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorDispatcherNode.java
new file mode 100644
index 00000000..a8e11fcd
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorDispatcherNode.java
@@ -0,0 +1,154 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.rete.single;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.HashMap;
14import java.util.Map;
15import java.util.Map.Entry;
16
17import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
20import tools.refinery.viatra.runtime.matchers.util.Direction;
21import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
22import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
23import tools.refinery.viatra.runtime.rete.network.Receiver;
24import tools.refinery.viatra.runtime.rete.network.ReteContainer;
25import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
26import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
27
28/**
29 * Node that sends tuples off to different buckets (attached as children of type {@link DiscriminatorBucketNode}), based
30 * on the value of a given column.
31 *
32 * <p>
33 * Tuple contents and bucket keys have already been wrapped using {@link IQueryRuntimeContext#wrapElement(Object)}
34 *
35 * @author Gabor Bergmann
36 * @since 1.5
37 */
38public class DiscriminatorDispatcherNode extends SingleInputNode implements NetworkStructureChangeSensitiveNode {
39
40 private int discriminationColumnIndex;
41 private Map<Object, DiscriminatorBucketNode> buckets = new HashMap<>();
42 private Map<Object, Mailbox> bucketMailboxes = new HashMap<>();
43
44 /**
45 * @param reteContainer
46 */
47 public DiscriminatorDispatcherNode(ReteContainer reteContainer, int discriminationColumnIndex) {
48 super(reteContainer);
49 this.discriminationColumnIndex = discriminationColumnIndex;
50 }
51
52 @Override
53 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
54 Object dispatchKey = updateElement.get(discriminationColumnIndex);
55 Mailbox bucketMailBox = bucketMailboxes.get(dispatchKey);
56 if (bucketMailBox != null) {
57 bucketMailBox.postMessage(direction, updateElement, timestamp);
58 }
59 }
60
61 public int getDiscriminationColumnIndex() {
62 return discriminationColumnIndex;
63 }
64
65 @Override
66 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
67 propagatePullInto(collector, flush);
68 }
69
70 @Override
71 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
72 propagatePullIntoWithTimestamp(collector, flush);
73 }
74
75 /**
76 * @since 2.3
77 */
78 public void pullIntoFiltered(final Collection<Tuple> collector, final Object bucketKey, final boolean flush) {
79 final ArrayList<Tuple> unfiltered = new ArrayList<Tuple>();
80 propagatePullInto(unfiltered, flush);
81 for (Tuple tuple : unfiltered) {
82 if (bucketKey.equals(tuple.get(discriminationColumnIndex))) {
83 collector.add(tuple);
84 }
85 }
86 }
87
88 /**
89 * @since 2.3
90 */
91 public void pullIntoWithTimestampFiltered(final Map<Tuple, Timeline<Timestamp>> collector, final Object bucketKey,
92 final boolean flush) {
93 final Map<Tuple, Timeline<Timestamp>> unfiltered = CollectionsFactory.createMap();
94 propagatePullIntoWithTimestamp(unfiltered, flush);
95 for (final Entry<Tuple, Timeline<Timestamp>> entry : unfiltered.entrySet()) {
96 if (bucketKey.equals(entry.getKey().get(discriminationColumnIndex))) {
97 collector.put(entry.getKey(), entry.getValue());
98 }
99 }
100 }
101
102 @Override
103 public void appendChild(Receiver receiver) {
104 super.appendChild(receiver);
105 if (receiver instanceof DiscriminatorBucketNode) {
106 DiscriminatorBucketNode bucket = (DiscriminatorBucketNode) receiver;
107 Object bucketKey = bucket.getBucketKey();
108 DiscriminatorBucketNode old = buckets.put(bucketKey, bucket);
109 if (old != null) {
110 throw new IllegalStateException();
111 }
112 bucketMailboxes.put(bucketKey, this.getCommunicationTracker().proxifyMailbox(this, bucket.getMailbox()));
113 }
114 }
115
116 /**
117 * @since 2.2
118 */
119 public Map<Object, Mailbox> getBucketMailboxes() {
120 return this.bucketMailboxes;
121 }
122
123 @Override
124 public void networkStructureChanged() {
125 bucketMailboxes.clear();
126 for (Receiver receiver : children) {
127 if (receiver instanceof DiscriminatorBucketNode) {
128 DiscriminatorBucketNode bucket = (DiscriminatorBucketNode) receiver;
129 Object bucketKey = bucket.getBucketKey();
130 bucketMailboxes.put(bucketKey,
131 this.getCommunicationTracker().proxifyMailbox(this, bucket.getMailbox()));
132 }
133 }
134 }
135
136 @Override
137 public void removeChild(Receiver receiver) {
138 super.removeChild(receiver);
139 if (receiver instanceof DiscriminatorBucketNode) {
140 DiscriminatorBucketNode bucket = (DiscriminatorBucketNode) receiver;
141 Object bucketKey = bucket.getBucketKey();
142 DiscriminatorBucketNode old = buckets.remove(bucketKey);
143 if (old != bucket)
144 throw new IllegalStateException();
145 bucketMailboxes.remove(bucketKey);
146 }
147 }
148
149 @Override
150 protected String toStringCore() {
151 return super.toStringCore() + '<' + discriminationColumnIndex + '>';
152 }
153
154}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/EqualityFilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/EqualityFilterNode.java
new file mode 100644
index 00000000..014c2016
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/EqualityFilterNode.java
@@ -0,0 +1,41 @@
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.rete.single;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.rete.network.ReteContainer;
14
15public class EqualityFilterNode extends FilterNode {
16
17 int[] indices;
18 int first;
19
20 /**
21 * @param reteContainer
22 * @param indices
23 * indices of the Tuple that should hold equal values
24 */
25 public EqualityFilterNode(ReteContainer reteContainer, int[] indices) {
26 super(reteContainer);
27 this.indices = indices;
28 first = indices[0];
29 }
30
31 @Override
32 public boolean check(Tuple ps) {
33 Object firstElement = ps.get(first);
34 for (int i = 1 /* first is omitted */; i < indices.length; i++) {
35 if (!ps.get(indices[i]).equals(firstElement))
36 return false;
37 }
38 return true;
39 }
40
41}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/FilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/FilterNode.java
new file mode 100644
index 00000000..f66f1715
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/FilterNode.java
@@ -0,0 +1,69 @@
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.rete.single;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Map.Entry;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import tools.refinery.viatra.runtime.rete.network.ReteContainer;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21
22/**
23 * This node implements a simple filter. A stateless abstract check() predicate determines whether a matching is allowed
24 * to pass.
25 *
26 * @author Gabor Bergmann
27 *
28 */
29public abstract class FilterNode extends SingleInputNode {
30
31 public FilterNode(final ReteContainer reteContainer) {
32 super(reteContainer);
33 }
34
35 /**
36 * Abstract filtering predicate. Expected to be stateless.
37 *
38 * @param ps
39 * the matching to be checked.
40 * @return true if and only if the parameter matching is allowed to pass through this node.
41 */
42 public abstract boolean check(final Tuple ps);
43
44 @Override
45 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
46 for (final Tuple ps : this.reteContainer.pullPropagatedContents(this, flush)) {
47 if (check(ps)) {
48 collector.add(ps);
49 }
50 }
51 }
52
53 @Override
54 public void pullIntoWithTimeline(Map<Tuple, Timeline<Timestamp>> collector, boolean flush) {
55 for (final Entry<Tuple, Timeline<Timestamp>> entry : this.reteContainer.pullPropagatedContentsWithTimestamp(this, flush).entrySet()) {
56 if (check(entry.getKey())) {
57 collector.put(entry.getKey(), entry.getValue());
58 }
59 }
60 }
61
62 @Override
63 public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) {
64 if (check(updateElement)) {
65 propagateUpdate(direction, updateElement, timestamp);
66 }
67 }
68
69}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/InequalityFilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/InequalityFilterNode.java
new file mode 100644
index 00000000..8dd3e949
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/InequalityFilterNode.java
@@ -0,0 +1,52 @@
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.rete.single;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
14import tools.refinery.viatra.runtime.rete.network.ReteContainer;
15
16/**
17 * This node filters patterns according to equalities and inequalities of elements. The 'subject' element is asserted to
18 * be different from the elements given by the inequalityMask.
19 *
20 *
21 * @author Gabor Bergmann
22 *
23 */
24public class InequalityFilterNode extends FilterNode {
25
26 int subjectIndex;
27 TupleMask inequalityMask;
28
29 /**
30 * @param reteContainer
31 * @param subject
32 * the index of the element that should be compared.
33 * @param inequalityMask
34 * the indices of elements that should be different from the subjectIndex.
35 */
36 public InequalityFilterNode(ReteContainer reteContainer, int subject, TupleMask inequalityMask) {
37 super(reteContainer);
38 this.subjectIndex = subject;
39 this.inequalityMask = inequalityMask;
40 }
41
42 @Override
43 public boolean check(Tuple ps) {
44 Object subject = ps.get(subjectIndex);
45 for (int ineq : inequalityMask.indices) {
46 if (subject.equals(ps.get(ineq)))
47 return false;
48 }
49 return true;
50 }
51
52}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RepresentativeElectionNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java
index 701f6ffe..6a1e305c 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RepresentativeElectionNode.java
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java
@@ -6,22 +6,25 @@
6 * http://www.eclipse.org/legal/epl-v20.html. 6 * http://www.eclipse.org/legal/epl-v20.html.
7 * SPDX-License-Identifier: EPL-2.0 7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/ 8 *******************************************************************************/
9package tools.refinery.store.query.viatra.internal.rete.network; 9package tools.refinery.viatra.runtime.rete.single;
10 10
11import org.eclipse.viatra.query.runtime.base.itc.graphimpl.Graph; 11import tools.refinery.viatra.runtime.base.itc.alg.representative.RepresentativeElectionAlgorithm;
12import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; 12import tools.refinery.viatra.runtime.base.itc.alg.representative.RepresentativeObserver;
13import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 13import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph;
14import org.eclipse.viatra.query.runtime.matchers.util.Clearable; 14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15import org.eclipse.viatra.query.runtime.matchers.util.Direction; 15import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
16import org.eclipse.viatra.query.runtime.matchers.util.timeline.Timeline; 16import tools.refinery.viatra.runtime.matchers.util.Clearable;
17import org.eclipse.viatra.query.runtime.rete.network.ReteContainer; 17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import org.eclipse.viatra.query.runtime.rete.network.communication.Timestamp; 18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import org.eclipse.viatra.query.runtime.rete.single.SingleInputNode; 19import tools.refinery.viatra.runtime.rete.network.ReinitializedNode;
20import tools.refinery.viatra.runtime.rete.network.ReteContainer;
21import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20 22
21import java.util.Collection; 23import java.util.Collection;
22import java.util.Map; 24import java.util.Map;
23 25
24public class RepresentativeElectionNode extends SingleInputNode implements Clearable { 26public class RepresentativeElectionNode extends SingleInputNode implements Clearable, RepresentativeObserver,
27 ReinitializedNode {
25 private final RepresentativeElectionAlgorithm.Factory algorithmFactory; 28 private final RepresentativeElectionAlgorithm.Factory algorithmFactory;
26 private Graph<Object> graph; 29 private Graph<Object> graph;
27 private RepresentativeElectionAlgorithm algorithm; 30 private RepresentativeElectionAlgorithm algorithm;
@@ -44,6 +47,7 @@ public class RepresentativeElectionNode extends SingleInputNode implements Clear
44 super.networkStructureChanged(); 47 super.networkStructureChanged();
45 } 48 }
46 49
50 @Override
47 public void reinitializeWith(Collection<Tuple> tuples) { 51 public void reinitializeWith(Collection<Tuple> tuples) {
48 algorithm.dispose(); 52 algorithm.dispose();
49 graph = new Graph<>(); 53 graph = new Graph<>();
@@ -54,6 +58,7 @@ public class RepresentativeElectionNode extends SingleInputNode implements Clear
54 algorithm.setObserver(this); 58 algorithm.setObserver(this);
55 } 59 }
56 60
61 @Override
57 public void tupleChanged(Object source, Object representative, Direction direction) { 62 public void tupleChanged(Object source, Object representative, Direction direction) {
58 var tuple = Tuples.staticArityFlatTupleOf(source, representative); 63 var tuple = Tuples.staticArityFlatTupleOf(source, representative);
59 propagateUpdate(direction, tuple, Timestamp.ZERO); 64 propagateUpdate(direction, tuple, Timestamp.ZERO);
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/SingleInputNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/SingleInputNode.java
new file mode 100644
index 00000000..99fc45b2
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/SingleInputNode.java
@@ -0,0 +1,126 @@
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.rete.single;
11
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Map;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.StandardNode;
20import tools.refinery.viatra.runtime.rete.network.Supplier;
21import tools.refinery.viatra.runtime.rete.network.Tunnel;
22import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
23import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
24import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
25import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
26import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
27import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
28
29/**
30 * @author Gabor Bergmann
31 *
32 */
33public abstract class SingleInputNode extends StandardNode implements Tunnel {
34
35 protected Supplier parent;
36 /**
37 * @since 1.6
38 */
39 protected Mailbox mailbox;
40
41 public SingleInputNode(ReteContainer reteContainer) {
42 super(reteContainer);
43 mailbox = instantiateMailbox();
44 reteContainer.registerClearable(mailbox);
45 parent = null;
46 }
47
48 /**
49 * Instantiates the {@link Mailbox} of this receiver.
50 * Subclasses may override this method to provide their own mailbox implementation.
51 *
52 * @return the mailbox
53 * @since 2.0
54 */
55 protected Mailbox instantiateMailbox() {
56 if (this.reteContainer.isTimelyEvaluation()) {
57 return new TimelyMailbox(this, this.reteContainer);
58 } else {
59 return new BehaviorChangingMailbox(this, this.reteContainer);
60 }
61 }
62
63 @Override
64 public CommunicationTracker getCommunicationTracker() {
65 return this.reteContainer.getCommunicationTracker();
66 }
67
68 @Override
69 public Mailbox getMailbox() {
70 return this.mailbox;
71 }
72
73 @Override
74 public void appendParent(Supplier supplier) {
75 if (parent == null)
76 parent = supplier;
77 else
78 throw new UnsupportedOperationException("Illegal RETE edge: " + this + " already has a parent (" + parent
79 + ") and cannot connect to additional parent (" + supplier
80 + ") as it is not a Uniqueness Enforcer Node. ");
81 }
82
83 @Override
84 public void removeParent(Supplier supplier) {
85 if (parent == supplier)
86 parent = null;
87 else
88 throw new IllegalArgumentException("Illegal RETE edge removal: the parent of " + this + " is not "
89 + supplier);
90 }
91
92 /**
93 * To be called by derived classes and ReteContainer.
94 */
95 public void propagatePullInto(final Collection<Tuple> collector, final boolean flush) {
96 if (parent != null) {
97 parent.pullInto(collector, flush);
98 }
99 }
100
101 /**
102 * To be called by derived classes and ReteContainer.
103 */
104 public void propagatePullIntoWithTimestamp(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
105 if (parent != null) {
106 parent.pullIntoWithTimeline(collector, flush);
107 }
108 }
109
110 @Override
111 public Collection<Supplier> getParents() {
112 if (parent == null)
113 return Collections.emptySet();
114 else
115 return Collections.singleton(parent);
116 }
117
118 @Override
119 public void assignTraceInfo(TraceInfo traceInfo) {
120 super.assignTraceInfo(traceInfo);
121 if (traceInfo.propagateFromStandardNodeToSupplierParent())
122 if (parent != null)
123 parent.acceptPropagatedTraceInfo(traceInfo);
124 }
125
126}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyProductionNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyProductionNode.java
new file mode 100644
index 00000000..82640948
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyProductionNode.java
@@ -0,0 +1,63 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.single;
10
11import java.util.Iterator;
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15import tools.refinery.viatra.runtime.rete.network.ProductionNode;
16import tools.refinery.viatra.runtime.rete.network.ReteContainer;
17import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery;
18import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
19/**
20 * Differential dataflow implementation of the Production node, based on {@link TimelyUniquenessEnforcerNode}.
21 *
22 * @author Tamas Szabo
23 * @noinstantiate This class is not intended to be instantiated by clients.
24 * @since 2.3
25 */
26public class TimelyProductionNode extends TimelyUniquenessEnforcerNode implements ProductionNode {
27
28 protected final Map<String, Integer> posMapping;
29
30 public TimelyProductionNode(final ReteContainer reteContainer, final Map<String, Integer> posMapping) {
31 super(reteContainer, posMapping.size());
32 this.posMapping = posMapping;
33 }
34
35 @Override
36 public Map<String, Integer> getPosMapping() {
37 return this.posMapping;
38 }
39
40 @Override
41 public Iterator<Tuple> iterator() {
42 return this.memory.keySet().iterator();
43 }
44
45 @Override
46 public void acceptPropagatedTraceInfo(final TraceInfo traceInfo) {
47 if (traceInfo.propagateToProductionNodeParentAlso()) {
48 super.acceptPropagatedTraceInfo(traceInfo);
49 }
50 }
51
52 @Override
53 public String toString() {
54 for (final TraceInfo traceInfo : this.traceInfos) {
55 if (traceInfo instanceof CompiledQuery) {
56 final String patternName = ((CompiledQuery) traceInfo).getPatternName();
57 return String.format(this.getClass().getName() + "<%s>=%s", patternName, super.toString());
58 }
59 }
60 return super.toString();
61 }
62
63}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyUniquenessEnforcerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyUniquenessEnforcerNode.java
new file mode 100644
index 00000000..4c4b4fc0
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyUniquenessEnforcerNode.java
@@ -0,0 +1,161 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, 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.rete.single;
10
11import java.util.Collection;
12import java.util.Map;
13import java.util.Map.Entry;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.matchers.util.Signed;
19import tools.refinery.viatra.runtime.matchers.util.TimelyMemory;
20import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
21import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
22import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
23import tools.refinery.viatra.runtime.rete.index.timely.TimelyMemoryIdentityIndexer;
24import tools.refinery.viatra.runtime.rete.index.timely.TimelyMemoryNullIndexer;
25import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
26import tools.refinery.viatra.runtime.rete.network.ReteContainer;
27import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
28import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
29import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
30import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
31import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
32
33/**
34 * Timely uniqueness enforcer node implementation.
35 *
36 * @author Tamas Szabo
37 * @noinstantiate This class is not intended to be instantiated by clients.
38 * @noextend This class is not intended to be subclassed by clients.
39 * @since 2.4
40 */
41public class TimelyUniquenessEnforcerNode extends AbstractUniquenessEnforcerNode implements ResumableNode {
42
43 protected final TimelyMemory<Timestamp> memory;
44 /**
45 * @since 2.4
46 */
47 protected CommunicationGroup group;
48
49 public TimelyUniquenessEnforcerNode(final ReteContainer container, final int tupleWidth) {
50 super(container, tupleWidth);
51 this.memory = new TimelyMemory<Timestamp>(
52 container.getTimelyConfiguration().getTimelineRepresentation() == TimelineRepresentation.FAITHFUL);
53 container.registerClearable(this.memory);
54 this.mailbox = instantiateMailbox();
55 container.registerClearable(this.mailbox);
56 }
57
58 protected Mailbox instantiateMailbox() {
59 return new TimelyMailbox(this, this.reteContainer);
60 }
61
62 @Override
63 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
64 for (final Tuple tuple : this.memory.getTuplesAtInfinity()) {
65 collector.add(tuple);
66 }
67 }
68
69 @Override
70 public CommunicationGroup getCurrentGroup() {
71 return this.group;
72 }
73
74 @Override
75 public void setCurrentGroup(final CommunicationGroup group) {
76 this.group = group;
77 }
78
79 @Override
80 public Set<Tuple> getTuples() {
81 return this.memory.getTuplesAtInfinity();
82 }
83
84 /**
85 * @since 2.4
86 */
87 @Override
88 public Timestamp getResumableTimestamp() {
89 return this.memory.getResumableTimestamp();
90 }
91
92 /**
93 * @since 2.4
94 */
95 @Override
96 public void resumeAt(final Timestamp timestamp) {
97 final Map<Tuple, Diff<Timestamp>> diffMap = this.memory.resumeAt(timestamp);
98 for (final Entry<Tuple, Diff<Timestamp>> entry : diffMap.entrySet()) {
99 for (final Signed<Timestamp> signed : entry.getValue()) {
100 propagate(signed.getDirection(), entry.getKey(), signed.getPayload());
101 }
102 }
103 final Timestamp nextTimestamp = this.memory.getResumableTimestamp();
104 if (nextTimestamp != null) {
105 this.group.notifyHasMessage(this.mailbox, nextTimestamp);
106 }
107 }
108
109 @Override
110 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
111 Diff<Timestamp> resultDiff = null;
112 if (direction == Direction.INSERT) {
113 resultDiff = this.memory.put(update, timestamp);
114 } else {
115 try {
116 resultDiff = this.memory.remove(update, timestamp);
117 } catch (final IllegalStateException e) {
118 issueError("[INTERNAL ERROR] Duplicate deletion of " + update + " was detected in "
119 + this.getClass().getName() + " " + this + " for pattern(s) "
120 + getTraceInfoPatternsEnumerated(), e);
121 // diff will remain unset in case of the exception, it is time to return
122 return;
123 }
124 }
125
126 for (final Signed<Timestamp> signed : resultDiff) {
127 propagate(signed.getDirection(), update, signed.getPayload());
128 }
129 }
130
131 @Override
132 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
133 collector.putAll(this.memory.asMap());
134 }
135
136 @Override
137 public ProjectionIndexer getNullIndexer() {
138 if (this.memoryNullIndexer == null) {
139 this.memoryNullIndexer = new TimelyMemoryNullIndexer(this.reteContainer, this.tupleWidth, this.memory, this,
140 this, this.specializedListeners);
141 this.getCommunicationTracker().registerDependency(this, this.memoryNullIndexer);
142 }
143 return this.memoryNullIndexer;
144 }
145
146 @Override
147 public ProjectionIndexer getIdentityIndexer() {
148 if (this.memoryIdentityIndexer == null) {
149 this.memoryIdentityIndexer = new TimelyMemoryIdentityIndexer(this.reteContainer, this.tupleWidth,
150 this.memory, this, this, this.specializedListeners);
151 this.getCommunicationTracker().registerDependency(this, this.memoryIdentityIndexer);
152 }
153 return this.memoryIdentityIndexer;
154 }
155
156 @Override
157 public void networkStructureChanged() {
158 super.networkStructureChanged();
159 }
160
161} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransformerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransformerNode.java
new file mode 100644
index 00000000..24750656
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransformerNode.java
@@ -0,0 +1,49 @@
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.rete.single;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Map.Entry;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import tools.refinery.viatra.runtime.rete.network.ReteContainer;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21
22public abstract class TransformerNode extends SingleInputNode {
23
24 public TransformerNode(final ReteContainer reteContainer) {
25 super(reteContainer);
26 }
27
28 protected abstract Tuple transform(final Tuple input);
29
30 @Override
31 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
32 for (Tuple ps : reteContainer.pullPropagatedContents(this, flush)) {
33 collector.add(transform(ps));
34 }
35 }
36
37 @Override
38 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
39 for (final Entry<Tuple, Timeline<Timestamp>> entry : reteContainer.pullPropagatedContentsWithTimestamp(this, flush).entrySet()) {
40 collector.put(transform(entry.getKey()), entry.getValue());
41 }
42 }
43
44 @Override
45 public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) {
46 propagateUpdate(direction, transform(updateElement), timestamp);
47 }
48
49}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java
new file mode 100644
index 00000000..eeead31b
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java
@@ -0,0 +1,147 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.single;
10
11import tools.refinery.viatra.runtime.base.itc.alg.incscc.IncSCCAlg;
12import tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple;
13import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph;
14import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource;
15import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
17import tools.refinery.viatra.runtime.matchers.util.Clearable;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
20import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
21import tools.refinery.viatra.runtime.rete.network.ReinitializedNode;
22import tools.refinery.viatra.runtime.rete.network.ReteContainer;
23import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
24import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
25
26import java.util.Collection;
27import java.util.Map;
28
29/**
30 * This class represents a transitive closure node in the Rete net.
31 * <p>
32 * This node must not be used in recursive {@link CommunicationGroup}s.
33 *
34 * @author Gabor Bergmann
35 *
36 */
37public class TransitiveClosureNode extends SingleInputNode
38 implements Clearable, ITcObserver<Object>, NetworkStructureChangeSensitiveNode, ReinitializedNode {
39
40 private Graph<Object> graphDataSource;
41 private ITcDataSource<Object> transitiveClosureAlgorithm;
42
43 /**
44 * Create a new transitive closure rete node.
45 *
46 * Client may optionally call {@link #reinitializeWith(Collection)} before using the node, instead of inserting the
47 * initial set of tuples one by one.
48 *
49 * @param reteContainer
50 * the rete container of the node
51 */
52 public TransitiveClosureNode(ReteContainer reteContainer) {
53 super(reteContainer);
54 graphDataSource = new Graph<Object>();
55 transitiveClosureAlgorithm = new IncSCCAlg<Object>(graphDataSource);
56 transitiveClosureAlgorithm.attachObserver(this);
57 reteContainer.registerClearable(this);
58 }
59
60 @Override
61 public void networkStructureChanged() {
62 if (this.reteContainer.isTimelyEvaluation() && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
63 throw new IllegalStateException(this.toString() + " cannot be used in recursive differential dataflow evaluation!");
64 }
65 super.networkStructureChanged();
66 }
67
68 /**
69 * Initializes the graph data source with the given collection of tuples.
70 *
71 * @param tuples
72 * the initial collection of tuples
73 */
74 @Override
75 public void reinitializeWith(Collection<tools.refinery.viatra.runtime.matchers.tuple.Tuple> tuples) {
76 clear();
77
78 for (tools.refinery.viatra.runtime.matchers.tuple.Tuple t : tuples) {
79 graphDataSource.insertNode(t.get(0));
80 graphDataSource.insertNode(t.get(1));
81 graphDataSource.insertEdge(t.get(0), t.get(1));
82 }
83 transitiveClosureAlgorithm.attachObserver(this);
84 }
85
86 @Override
87 public void pullInto(final Collection<tools.refinery.viatra.runtime.matchers.tuple.Tuple> collector, final boolean flush) {
88 for (final Tuple<Object> tuple : ((IncSCCAlg<Object>) transitiveClosureAlgorithm).getTcRelation()) {
89 collector.add(Tuples.staticArityFlatTupleOf(tuple.getSource(), tuple.getTarget()));
90 }
91 }
92
93 @Override
94 public void pullIntoWithTimeline(
95 final Map<tools.refinery.viatra.runtime.matchers.tuple.Tuple, Timeline<Timestamp>> collector,
96 final boolean flush) {
97 // use all zero timestamps because this node cannot be used in recursive groups anyway
98 for (final Tuple<Object> tuple : ((IncSCCAlg<Object>) transitiveClosureAlgorithm).getTcRelation()) {
99 collector.put(Tuples.staticArityFlatTupleOf(tuple.getSource(), tuple.getTarget()), Timestamp.INSERT_AT_ZERO_TIMELINE);
100 }
101 }
102
103 @Override
104 public void update(Direction direction, tools.refinery.viatra.runtime.matchers.tuple.Tuple updateElement,
105 Timestamp timestamp) {
106 if (updateElement.getSize() == 2) {
107 Object source = updateElement.get(0);
108 Object target = updateElement.get(1);
109
110 if (direction == Direction.INSERT) {
111 graphDataSource.insertNode(source);
112 graphDataSource.insertNode(target);
113 graphDataSource.insertEdge(source, target);
114 }
115 if (direction == Direction.DELETE) {
116 graphDataSource.deleteEdgeIfExists(source, target);
117
118 if (((IncSCCAlg<Object>) transitiveClosureAlgorithm).isIsolated(source)) {
119 graphDataSource.deleteNode(source);
120 }
121 if (!source.equals(target) && ((IncSCCAlg<Object>) transitiveClosureAlgorithm).isIsolated(target)) {
122 graphDataSource.deleteNode(target);
123 }
124 }
125 }
126 }
127
128 @Override
129 public void clear() {
130 transitiveClosureAlgorithm.dispose();
131 graphDataSource = new Graph<Object>();
132 transitiveClosureAlgorithm = new IncSCCAlg<Object>(graphDataSource);
133 }
134
135 @Override
136 public void tupleInserted(Object source, Object target) {
137 tools.refinery.viatra.runtime.matchers.tuple.Tuple tuple = Tuples.staticArityFlatTupleOf(source, target);
138 propagateUpdate(Direction.INSERT, tuple, Timestamp.ZERO);
139 }
140
141 @Override
142 public void tupleDeleted(Object source, Object target) {
143 tools.refinery.viatra.runtime.matchers.tuple.Tuple tuple = Tuples.staticArityFlatTupleOf(source, target);
144 propagateUpdate(Direction.DELETE, tuple, Timestamp.ZERO);
145 }
146
147}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransparentNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransparentNode.java
new file mode 100644
index 00000000..6c21a966
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransparentNode.java
@@ -0,0 +1,48 @@
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.rete.single;
11
12import java.util.Collection;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20
21/**
22 * Simply propagates everything. Might be used to join or fork.
23 *
24 * @author Gabor Bergmann
25 */
26public class TransparentNode extends SingleInputNode {
27
28 public TransparentNode(final ReteContainer reteContainer) {
29 super(reteContainer);
30 }
31
32 @Override
33 public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) {
34 propagateUpdate(direction, updateElement, timestamp);
35
36 }
37
38 @Override
39 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
40 propagatePullInto(collector, flush);
41 }
42
43 @Override
44 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
45 propagatePullIntoWithTimestamp(collector, flush);
46 }
47
48}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TrimmerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TrimmerNode.java
new file mode 100644
index 00000000..8a72138c
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TrimmerNode.java
@@ -0,0 +1,61 @@
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.rete.single;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
14import tools.refinery.viatra.runtime.rete.network.ReteContainer;
15
16/**
17 * Trims the matchings as specified by a mask.
18 *
19 * @author Gabor Bergmann
20 *
21 */
22public class TrimmerNode extends TransformerNode {
23
24 protected TupleMask mask;
25
26 /**
27 * @param reteContainer
28 * @param mask
29 * The mask used to trim substitutions.
30 */
31 public TrimmerNode(ReteContainer reteContainer, TupleMask mask) {
32 super(reteContainer);
33 this.mask = mask;
34 }
35
36 public TrimmerNode(ReteContainer reteContainer) {
37 super(reteContainer);
38 this.mask = null;
39 }
40
41 /**
42 * @return the mask
43 */
44 public TupleMask getMask() {
45 return mask;
46 }
47
48 /**
49 * @param mask
50 * the mask to set
51 */
52 public void setMask(TupleMask mask) {
53 this.mask = mask;
54 }
55
56 @Override
57 protected Tuple transform(Tuple input) {
58 return mask.transform(input);
59 }
60
61}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/UniquenessEnforcerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/UniquenessEnforcerNode.java
new file mode 100644
index 00000000..5bfde248
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/UniquenessEnforcerNode.java
@@ -0,0 +1,321 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann, Tamas Szabo 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.rete.single;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
20import tools.refinery.viatra.runtime.matchers.util.Direction;
21import tools.refinery.viatra.runtime.matchers.util.IMultiset;
22import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
23import tools.refinery.viatra.runtime.rete.index.MemoryIdentityIndexer;
24import tools.refinery.viatra.runtime.rete.index.MemoryNullIndexer;
25import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
26import tools.refinery.viatra.runtime.rete.network.PosetAwareReceiver;
27import tools.refinery.viatra.runtime.rete.network.RederivableNode;
28import tools.refinery.viatra.runtime.rete.network.ReteContainer;
29import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
30import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
31import tools.refinery.viatra.runtime.rete.network.communication.timeless.RecursiveCommunicationGroup;
32import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
33import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
34import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.PosetAwareMailbox;
35
36/**
37 * Timeless uniqueness enforcer node implementation.
38 * <p>
39 * The node is capable of operating in the delete and re-derive mode. In this mode, it is also possible to equip the
40 * node with an {@link IPosetComparator} to identify monotone changes; thus, ensuring that a fix-point can be reached
41 * during the evaluation.
42 *
43 * @author Gabor Bergmann
44 * @author Tamas Szabo
45 * @noinstantiate This class is not intended to be instantiated by clients.
46 * @noextend This class is not intended to be subclassed by clients.
47 */
48public class UniquenessEnforcerNode extends AbstractUniquenessEnforcerNode
49 implements RederivableNode, PosetAwareReceiver {
50
51 protected IMultiset<Tuple> memory;
52 /**
53 * @since 1.6
54 */
55 protected IMultiset<Tuple> rederivableMemory;
56 /**
57 * @since 1.6
58 */
59 protected boolean deleteRederiveEvaluation;
60
61 /**
62 * @since 1.7
63 */
64 protected CommunicationGroup currentGroup;
65
66 public UniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth) {
67 this(reteContainer, tupleWidth, false);
68 }
69
70 /**
71 * OPTIONAL ELEMENT - ONLY PRESENT IF MONOTONICITY INFO WAS AVAILABLE
72 *
73 * @since 1.6
74 */
75 protected final TupleMask coreMask;
76 /**
77 * OPTIONAL ELEMENTS - ONLY PRESENT IF MONOTONICITY INFO WAS AVAILABLE
78 *
79 * @since 1.6
80 */
81 protected final TupleMask posetMask;
82 /**
83 * OPTIONAL ELEMENTS - ONLY PRESENT IF MONOTONICITY INFO WAS AVAILABLE
84 *
85 * @since 1.6
86 */
87 protected final IPosetComparator posetComparator;
88
89 /**
90 * @since 1.6
91 */
92 public UniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth,
93 final boolean deleteRederiveEvaluation) {
94 this(reteContainer, tupleWidth, deleteRederiveEvaluation, null, null, null);
95 }
96
97 /**
98 * @since 1.6
99 */
100 public UniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth,
101 final boolean deleteRederiveEvaluation, final TupleMask coreMask, final TupleMask posetMask,
102 final IPosetComparator posetComparator) {
103 super(reteContainer, tupleWidth);
104 this.memory = CollectionsFactory.createMultiset();
105 this.rederivableMemory = CollectionsFactory.createMultiset();
106 reteContainer.registerClearable(this.memory);
107 reteContainer.registerClearable(this.rederivableMemory);
108 this.deleteRederiveEvaluation = deleteRederiveEvaluation;
109 this.coreMask = coreMask;
110 this.posetMask = posetMask;
111 this.posetComparator = posetComparator;
112 this.mailbox = instantiateMailbox();
113 reteContainer.registerClearable(this.mailbox);
114 }
115
116 @Override
117 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
118 for (final Tuple tuple : this.memory.distinctValues()) {
119 collector.add(tuple);
120 }
121 }
122
123 /**
124 * @since 2.8
125 */
126 @Override
127 public Set<Tuple> getTuples() {
128 return this.memory.distinctValues();
129 }
130
131 @Override
132 public boolean isInDRedMode() {
133 return this.deleteRederiveEvaluation;
134 }
135
136 @Override
137 public TupleMask getCoreMask() {
138 return coreMask;
139 }
140
141 @Override
142 public TupleMask getPosetMask() {
143 return posetMask;
144 }
145
146 @Override
147 public IPosetComparator getPosetComparator() {
148 return posetComparator;
149 }
150
151 @Override
152 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
153 throw new UnsupportedOperationException("Use the timely version of this node!");
154 }
155
156 /**
157 * @since 2.0
158 */
159 protected Mailbox instantiateMailbox() {
160 if (coreMask != null && posetMask != null && posetComparator != null) {
161 return new PosetAwareMailbox(this, this.reteContainer);
162 } else {
163 return new BehaviorChangingMailbox(this, this.reteContainer);
164 }
165 }
166
167 @Override
168 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
169 updateWithPosetInfo(direction, update, false);
170 }
171
172 @Override
173 public void updateWithPosetInfo(final Direction direction, final Tuple update, final boolean monotone) {
174 if (this.deleteRederiveEvaluation) {
175 if (updateWithDeleteAndRederive(direction, update, monotone)) {
176 propagate(direction, update, Timestamp.ZERO);
177 }
178 } else {
179 if (updateDefault(direction, update)) {
180 propagate(direction, update, Timestamp.ZERO);
181 }
182 }
183 }
184
185 /**
186 * @since 2.4
187 */
188 protected boolean updateWithDeleteAndRederive(final Direction direction, final Tuple update,
189 final boolean monotone) {
190 boolean propagate = false;
191
192 final int memoryCount = memory.getCount(update);
193 final int rederivableCount = rederivableMemory.getCount(update);
194
195 if (direction == Direction.INSERT) {
196 // INSERT
197 if (rederivableCount != 0) {
198 // the tuple is in the re-derivable memory
199 rederivableMemory.addOne(update);
200 if (rederivableMemory.isEmpty()) {
201 // there is nothing left to be re-derived
202 // this can happen if the INSERT cancelled out a DELETE
203 ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this);
204 }
205 } else {
206 // the tuple is in the main memory
207 propagate = memory.addOne(update);
208 }
209 } else {
210 // DELETE
211 if (rederivableCount != 0) {
212 // the tuple is in the re-derivable memory
213 if (memoryCount != 0) {
214 issueError("[INTERNAL ERROR] Inconsistent state for " + update
215 + " because it is present both in the main and re-derivable memory in the UniquenessEnforcerNode "
216 + this + " for pattern(s) " + getTraceInfoPatternsEnumerated(), null);
217 }
218
219 try {
220 rederivableMemory.removeOne(update);
221 } catch (final IllegalStateException ex) {
222 issueError(
223 "[INTERNAL ERROR] Duplicate deletion of " + update + " was detected in UniquenessEnforcer "
224 + this + " for pattern(s) " + getTraceInfoPatternsEnumerated(),
225 ex);
226 }
227 if (rederivableMemory.isEmpty()) {
228 // there is nothing left to be re-derived
229 ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this);
230 }
231 } else {
232 // the tuple is in the main memory
233 if (monotone) {
234 propagate = memory.removeOne(update);
235 } else {
236 final int count = memoryCount - 1;
237 if (count > 0) {
238 if (rederivableMemory.isEmpty()) {
239 // there is now something to be re-derived
240 ((RecursiveCommunicationGroup) currentGroup).addRederivable(this);
241 }
242 rederivableMemory.addPositive(update, count);
243 }
244 memory.clearAllOf(update);
245 propagate = true;
246 }
247 }
248 }
249
250 return propagate;
251 }
252
253 /**
254 * @since 2.4
255 */
256 protected boolean updateDefault(final Direction direction, final Tuple update) {
257 boolean propagate = false;
258 if (direction == Direction.INSERT) {
259 // INSERT
260 propagate = memory.addOne(update);
261 } else {
262 // DELETE
263 try {
264 propagate = memory.removeOne(update);
265 } catch (final IllegalStateException ex) {
266 propagate = false;
267 issueError("[INTERNAL ERROR] Duplicate deletion of " + update + " was detected in "
268 + this.getClass().getName() + " " + this + " for pattern(s) "
269 + getTraceInfoPatternsEnumerated(), ex);
270 }
271 }
272 return propagate;
273 }
274
275 /**
276 * @since 1.6
277 */
278 @Override
279 public void rederiveOne() {
280 final Tuple update = rederivableMemory.iterator().next();
281 final int count = rederivableMemory.getCount(update);
282 rederivableMemory.clearAllOf(update);
283 memory.addPositive(update, count);
284 // if there is no other re-derivable tuple, then unregister the node itself
285 if (this.rederivableMemory.isEmpty()) {
286 ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this);
287 }
288 propagate(Direction.INSERT, update, Timestamp.ZERO);
289 }
290
291 @Override
292 public ProjectionIndexer getNullIndexer() {
293 if (this.memoryNullIndexer == null) {
294 this.memoryNullIndexer = new MemoryNullIndexer(this.reteContainer, this.tupleWidth,
295 this.memory.distinctValues(), this, this, this.specializedListeners);
296 this.getCommunicationTracker().registerDependency(this, this.memoryNullIndexer);
297 }
298 return this.memoryNullIndexer;
299 }
300
301 @Override
302 public ProjectionIndexer getIdentityIndexer() {
303 if (this.memoryIdentityIndexer == null) {
304 this.memoryIdentityIndexer = new MemoryIdentityIndexer(this.reteContainer, this.tupleWidth,
305 this.memory.distinctValues(), this, this, this.specializedListeners);
306 this.getCommunicationTracker().registerDependency(this, this.memoryIdentityIndexer);
307 }
308 return this.memoryIdentityIndexer;
309 }
310
311 @Override
312 public CommunicationGroup getCurrentGroup() {
313 return currentGroup;
314 }
315
316 @Override
317 public void setCurrentGroup(final CommunicationGroup currentGroup) {
318 this.currentGroup = currentGroup;
319 }
320
321}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/ValueBinderFilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/ValueBinderFilterNode.java
new file mode 100644
index 00000000..c641bf6e
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/ValueBinderFilterNode.java
@@ -0,0 +1,44 @@
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.rete.single;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.rete.network.ReteContainer;
14
15/**
16 * A filter node that keeps only those tuples that contain a certain value at a certain position.
17 *
18 * @author Bergmann Gabor
19 *
20 */
21public class ValueBinderFilterNode extends FilterNode {
22
23 int bindingIndex;
24 Object bindingValue;
25
26 /**
27 * @param reteContainer
28 * @param bindingIndex
29 * the position in the tuple that should be bound
30 * @param bindingValue
31 * the value to which the tuple has to be bound
32 */
33 public ValueBinderFilterNode(ReteContainer reteContainer, int bindingIndex, Object bindingValue) {
34 super(reteContainer);
35 this.bindingIndex = bindingIndex;
36 this.bindingValue = bindingValue;
37 }
38
39 @Override
40 public boolean check(Tuple ps) {
41 return bindingValue.equals(ps.get(bindingIndex));
42 }
43
44}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ActiveNodeConflictTrace.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ActiveNodeConflictTrace.java
new file mode 100644
index 00000000..2055dfe8
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ActiveNodeConflictTrace.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.rete.traceability;
10
11import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
12
13public class ActiveNodeConflictTrace extends RecipeTraceInfo { // TODO implement PatternTraceInfo
14 RecipeTraceInfo inactiveRecipeTrace;
15 public ActiveNodeConflictTrace(ReteNodeRecipe recipe,
16 RecipeTraceInfo parentRecipeTrace,
17 RecipeTraceInfo inactiveRecipeTrace) {
18 super(recipe, parentRecipeTrace);
19 this.inactiveRecipeTrace = inactiveRecipeTrace;
20 }
21 public RecipeTraceInfo getInactiveRecipeTrace() {
22 return inactiveRecipeTrace;
23 }
24} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledQuery.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledQuery.java
new file mode 100644
index 00000000..b8c793c5
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledQuery.java
@@ -0,0 +1,54 @@
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.rete.traceability;
10
11import java.util.Map;
12
13import tools.refinery.viatra.runtime.matchers.psystem.PBody;
14import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
15import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
16
17/**
18 * Indicates that recipe expresses the finished match set of a query.
19 * @author Bergmann Gabor
20 * @noinstantiate This class is not intended to be instantiated by clients.
21 */
22public class CompiledQuery extends RecipeTraceInfo implements
23 PatternTraceInfo {
24
25 private PQuery query;
26 private final Map<PBody, ? extends RecipeTraceInfo> parentRecipeTracesPerBody;
27
28 /**
29 * @since 1.6
30 */
31 public CompiledQuery(ReteNodeRecipe recipe,
32 Map<PBody, ? extends RecipeTraceInfo> parentRecipeTraces,
33 PQuery query) {
34 super(recipe, parentRecipeTraces.values());
35 parentRecipeTracesPerBody = parentRecipeTraces;
36 this.query = query;
37 }
38 public PQuery getQuery() {
39 return query;
40 }
41
42 @Override
43 public String getPatternName() {
44 return query.getFullyQualifiedName();
45 }
46
47 /**
48 * @since 1.6
49 */
50 public Map<PBody, ? extends RecipeTraceInfo> getParentRecipeTracesPerBody() {
51 return parentRecipeTracesPerBody;
52 }
53
54}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledSubPlan.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledSubPlan.java
new file mode 100644
index 00000000..572e943c
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledSubPlan.java
@@ -0,0 +1,51 @@
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.rete.traceability;
10
11import java.util.Arrays;
12import java.util.Collection;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Set;
16import java.util.stream.Collectors;
17
18import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
19import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
20import tools.refinery.viatra.runtime.matchers.util.Preconditions;
21import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
22
23/**
24 * A trace marker associating a Rete recipe with a query SubPlan.
25 *
26 * <p> The Rete node represented by the recipe is equivalent to the SubPlan.
27 * <p> Invariant: each variable has at most one index associated with it in the tuple, i.e. no duplicates.
28 */
29public class CompiledSubPlan extends PlanningTrace {
30
31 public CompiledSubPlan(SubPlan subPlan, List<PVariable> variablesTuple,
32 ReteNodeRecipe recipe,
33 Collection<? extends RecipeTraceInfo> parentRecipeTraces) {
34 super(subPlan, variablesTuple, recipe, parentRecipeTraces);
35
36 // Make sure that each variable occurs only once
37 Set<PVariable> variablesSet = new HashSet<PVariable>(variablesTuple);
38 Preconditions.checkState(variablesSet.size() == variablesTuple.size(),
39 () -> String.format(
40 "Illegal column duplication (%s) while the query plan %s was compiled into a Rete Recipe %s",
41 variablesTuple.stream().map(PVariable::getName).collect(Collectors.joining(",")),
42 subPlan.toShortString(), recipe));
43 }
44
45 public CompiledSubPlan(SubPlan subPlan, List<PVariable> variablesTuple,
46 ReteNodeRecipe recipe,
47 RecipeTraceInfo... parentRecipeTraces) {
48 this(subPlan, variablesTuple, recipe, Arrays.asList(parentRecipeTraces));
49 }
50
51}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ParameterProjectionTrace.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ParameterProjectionTrace.java
new file mode 100644
index 00000000..8da1e314
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ParameterProjectionTrace.java
@@ -0,0 +1,42 @@
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.rete.traceability;
10
11import java.util.Arrays;
12import java.util.Collection;
13
14import tools.refinery.viatra.runtime.matchers.psystem.PBody;
15import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
16
17/**
18 * The recipe projects the finished results of a {@link PBody} onto the list of parameters.
19 * @author Bergmann Gabor
20 *
21 */
22public class ParameterProjectionTrace extends RecipeTraceInfo implements PatternTraceInfo {
23
24 public ParameterProjectionTrace(PBody body, ReteNodeRecipe recipe,
25 RecipeTraceInfo... parentRecipeTraces) {
26 this(body, recipe, Arrays.asList(parentRecipeTraces));
27 }
28
29 public ParameterProjectionTrace(PBody body, ReteNodeRecipe recipe,
30 Collection<? extends RecipeTraceInfo> parentRecipeTraces) {
31 super(recipe, parentRecipeTraces);
32 this.body = body;
33 }
34
35 PBody body;
36
37 @Override
38 public String getPatternName() {
39 return body.getPattern().getFullyQualifiedName();
40 }
41
42}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PatternTraceInfo.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PatternTraceInfo.java
new file mode 100644
index 00000000..fb7ef062
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PatternTraceInfo.java
@@ -0,0 +1,17 @@
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.rete.traceability;
10
11/**
12 * One kind of trace marker that merely establishes the pattern for which the node was built.
13 * @author Bergmann Gabor
14 */
15public interface PatternTraceInfo extends TraceInfo {
16 String getPatternName();
17} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PlanningTrace.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PlanningTrace.java
new file mode 100644
index 00000000..c1cc3a69
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PlanningTrace.java
@@ -0,0 +1,80 @@
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.rete.traceability;
10
11import java.util.Arrays;
12import java.util.Collection;
13import java.util.HashMap;
14import java.util.List;
15import java.util.Map;
16
17import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
18import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
19import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
20
21/**
22 * A trace marker associating a Rete recipe with a query SubPlan.
23 *
24 * <p> The recipe may be an auxiliary node;
25 * see {@link CompiledSubPlan} if it represents the entire SubPlan instead.
26 */
27public class PlanningTrace extends RecipeTraceInfo implements PatternTraceInfo {
28
29 protected SubPlan subPlan;
30 protected List<PVariable> variablesTuple;
31 protected Map<PVariable, Integer> posMapping;
32
33 public PlanningTrace(SubPlan subPlan, List<PVariable> variablesTuple,
34 ReteNodeRecipe recipe,
35 Collection<? extends RecipeTraceInfo> parentRecipeTraces) {
36 super(recipe, parentRecipeTraces);
37 this.subPlan = subPlan;
38 this.variablesTuple = variablesTuple;
39
40 this.posMapping = new HashMap<PVariable, Integer>();
41 for (int i = 0; i < variablesTuple.size(); ++i)
42 posMapping.put(variablesTuple.get(i), i);
43 }
44
45 public PlanningTrace(SubPlan subPlan, List<PVariable> variablesTuple,
46 ReteNodeRecipe recipe,
47 RecipeTraceInfo... parentRecipeTraces) {
48 this(subPlan, variablesTuple, recipe, Arrays.asList(parentRecipeTraces));
49 }
50
51 public SubPlan getSubPlan() {
52 return subPlan;
53 }
54
55 @Override
56 public String getPatternName() {
57 return subPlan.getBody().getPattern().getFullyQualifiedName();
58 }
59
60 public List<PVariable> getVariablesTuple() {
61 return variablesTuple;
62 }
63
64 public Map<PVariable, Integer> getPosMapping() {
65 return posMapping;
66 }
67
68 /**
69 * Returns a new clone that reinterprets the same compiled form
70 * as the compiled form of a (potentially different) subPlan.
71 * Useful e.g. if child plan turns out to be a no-op, or when promoting a {@link PlanningTrace} to {@link CompiledSubPlan}.
72 */
73 public CompiledSubPlan cloneFor(SubPlan newSubPlan) {
74 return new CompiledSubPlan(newSubPlan,
75 getVariablesTuple(),
76 getRecipe(),
77 getParentRecipeTracesForCloning());
78 }
79
80} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/RecipeTraceInfo.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/RecipeTraceInfo.java
new file mode 100644
index 00000000..8f610550
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/RecipeTraceInfo.java
@@ -0,0 +1,81 @@
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.rete.traceability;
10
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.List;
16
17import tools.refinery.viatra.runtime.rete.network.Node;
18import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
19
20/**
21 * A trace marker that indicates the recipe for which the node was built.
22 * @author Bergmann Gabor
23 */
24public class RecipeTraceInfo implements TraceInfo {
25 public ReteNodeRecipe getRecipe() {return recipe;}
26 /**
27 * For cloning in case of recursion cut-off points, use {@link #getParentRecipeTracesForCloning()} instead.
28 * @return an unmodifiable view on parent traces, to be constructed before this node (or alongside, in case of recursion)
29 */
30 public List<RecipeTraceInfo> getParentRecipeTraces() {return Collections.unmodifiableList(new ArrayList<>(parentRecipeTraces));}
31 /**
32 * Directly return the underlying collection so that changes to it will be transparent. Use only for recursion-tolerant cloning.
33 * @noreference This method is not intended to be referenced by clients.
34 */
35 public Collection<? extends RecipeTraceInfo> getParentRecipeTracesForCloning() {return parentRecipeTraces;}
36 @Override
37 public Node getNode() {return node;}
38
39 private Node node;
40 ReteNodeRecipe recipe;
41 ReteNodeRecipe shadowedRecipe;
42 Collection<? extends RecipeTraceInfo> parentRecipeTraces;
43
44
45 public RecipeTraceInfo(ReteNodeRecipe recipe, Collection<? extends RecipeTraceInfo> parentRecipeTraces) {
46 super();
47 this.recipe = recipe;
48 this.parentRecipeTraces = parentRecipeTraces; //ParentTraceList.from(parentRecipeTraces);
49 }
50 public RecipeTraceInfo(ReteNodeRecipe recipe, RecipeTraceInfo... parentRecipeTraces) {
51 this(recipe, Arrays.asList(parentRecipeTraces));
52 }
53
54 @Override
55 public boolean propagateToIndexerParent() {return false;}
56 @Override
57 public boolean propagateFromIndexerToSupplierParent() {return false;}
58 @Override
59 public boolean propagateFromStandardNodeToSupplierParent() {return false;}
60 @Override
61 public boolean propagateToProductionNodeParentAlso() {return false;}
62 @Override
63 public void assignNode(Node node) {this.node = node;}
64
65 /**
66 * @param knownRecipe a known recipe that is equivalent to the current recipe
67 */
68 public void shadowWithEquivalentRecipe(ReteNodeRecipe knownRecipe) {
69 this.shadowedRecipe = this.recipe;
70 this.recipe = knownRecipe;
71 }
72
73 /**
74 * Get original recipe shadowed by an equivalent
75 */
76 public ReteNodeRecipe getShadowedRecipe() {
77 return shadowedRecipe;
78 }
79
80
81} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/TraceInfo.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/TraceInfo.java
new file mode 100644
index 00000000..e1d440db
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/TraceInfo.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.rete.traceability;
10
11import tools.refinery.viatra.runtime.rete.network.Node;
12
13
14/**
15 * Traces the node back to a purpose for which the node was built,
16 * to explain why the node is there and what it means.
17 * @author Bergmann Gabor
18 */
19public interface TraceInfo {
20 boolean propagateToIndexerParent();
21 boolean propagateFromIndexerToSupplierParent();
22 boolean propagateFromStandardNodeToSupplierParent();
23 boolean propagateToProductionNodeParentAlso();
24
25 void assignNode(Node node);
26 Node getNode();
27}
28// /**
29// * The semantics of the tuples contained in this node.
30// * @return a tuple of correct size representing the semantics of each position.
31// * @post not null
32// */
33// Tuple getSemantics(); \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/UserRequestTrace.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/UserRequestTrace.java
new file mode 100644
index 00000000..11e4db32
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/UserRequestTrace.java
@@ -0,0 +1,36 @@
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.rete.traceability;
10
11import java.util.Collection;
12
13import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
14
15// private class AggregatorReferenceIndexTraceInfo extends RecipeTraceInfo {
16// RecipeTraceInfo aggregatorNodeRecipeTrace;
17// public AggregatorReferenceIndexTraceInfo(ProjectionIndexerRecipe recipe,
18// RecipeTraceInfo parentRecipeTrace,
19// RecipeTraceInfo aggregatorNodeRecipeTrace) {
20// super(recipe, parentRecipeTrace);
21// this.aggregatorNodeRecipeTrace = aggregatorNodeRecipeTrace;
22// }
23// public RecipeTraceInfo getAggregatorNodeRecipeTrace() {
24// return aggregatorNodeRecipeTrace;
25// }
26// }
27public class UserRequestTrace extends RecipeTraceInfo {
28 public UserRequestTrace(ReteNodeRecipe recipe,
29 Collection<RecipeTraceInfo> parentRecipeTraces) {
30 super(recipe, parentRecipeTraces);
31 }
32 public UserRequestTrace(ReteNodeRecipe recipe,
33 RecipeTraceInfo... parentRecipeTraces) {
34 super(recipe, parentRecipeTraces);
35 }
36} \ No newline at end of file
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/LexicographicComparator.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/LexicographicComparator.java
new file mode 100644
index 00000000..0efc50af
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/LexicographicComparator.java
@@ -0,0 +1,58 @@
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.rete.util;
10
11import java.util.Comparator;
12import java.util.Iterator;
13
14/**
15 * A comparator that compares two iterables based on the lexicographic sorting induced by a comparator on elements.
16 * @author Bergmann Gabor
17 *
18 */
19public class LexicographicComparator<T> implements Comparator<Iterable<? extends T>> {
20
21 final Comparator<T> elementComparator;
22
23 public LexicographicComparator(Comparator<T> elementComparator) {
24 super();
25 this.elementComparator = elementComparator;
26 }
27
28 @Override
29 public int compare(Iterable<? extends T> o1, Iterable<? extends T> o2) {
30 Iterator<? extends T> it1 = o1.iterator();
31 Iterator<? extends T> it2 = o2.iterator();
32
33 boolean has1, has2, bothHaveNext;
34 do {
35 has1 = it1.hasNext();
36 has2 = it2.hasNext();
37 bothHaveNext = has1 && has2;
38 if (bothHaveNext) {
39 T element1 = it1.next();
40 T element2 = it2.next();
41 int elementComparison = elementComparator.compare(element1, element2);
42 if (elementComparison != 0)
43 return elementComparison;
44 }
45 } while (bothHaveNext);
46 if (has1 && !has2) {
47 return +1;
48 } else if (!has1 && has2) {
49 return -1;
50 } else /*if (!has1 && !has2)*/ {
51 return 0;
52 }
53 }
54
55
56
57
58}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/Options.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/Options.java
new file mode 100644
index 00000000..96cc445f
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/Options.java
@@ -0,0 +1,111 @@
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.rete.util;
11
12import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
13import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
14import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy;
15import tools.refinery.viatra.runtime.rete.construction.basiclinear.BasicLinearLayout;
16import tools.refinery.viatra.runtime.rete.construction.quasitree.QuasiTreeLayout;
17import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyCommunicationGroup;
18
19/**
20 * Feature switches.
21 * @author Gabor Bergmann
22 * @noreference
23 */
24public class Options {
25
26 public enum NodeSharingOption {
27 NEVER, // not recommended, patternmatcher leaks possible
28 INDEXER_AND_REMOTEPROXY, ALL
29 }
30
31 public static final NodeSharingOption nodeSharingOption = NodeSharingOption.ALL;
32 public static final boolean releaseOnetimeIndexers = true; // effective only
33 // with
34 // nodesharing
35 // ==NEVER
36
37 public enum InjectivityStrategy {
38 EAGER, LAZY
39 }
40
41 public static final InjectivityStrategy injectivityStrategy = InjectivityStrategy.EAGER;
42
43 public static final boolean enableInheritance = true;
44
45 // public final static boolean useComplementerMask = true;
46
47 public static final boolean employTrivialIndexers = true;
48
49 // public final static boolean synchronous = false;
50
51 public static final int numberOfLocalContainers = 1;
52 public static final int firstFreeContainer = 0; // 0 if head container is
53 // free to contain pattern
54 // bodies, 1 otherwise
55
56 /**
57 * Enable for internal debugging of Rete communication scheme;
58 * catches cases where the topological sort is violated by a message sent "backwards"
59 * @since 1.6
60 */
61 public static final boolean MONITOR_VIOLATION_OF_RETE_NODEGROUP_TOPOLOGICAL_SORTING = false;
62
63 /**
64 * Enable for internal debugging of message delivery in {@link TimelyCommunicationGroup}s;
65 * catches cases when there is a violation of increasing timestamps during message delivery within a group.
66 * @since 2.3
67 */
68 public static final boolean MONITOR_VIOLATION_OF_DIFFERENTIAL_DATAFLOW_TIMESTAMPS = false;
69
70 /**
71 *
72 * @author Gabor Bergmann
73 * @noreference
74 */
75 public enum BuilderMethod {
76 LEGACY, // ONLY with GTASM
77 PSYSTEM_BASIC_LINEAR, PSYSTEM_QUASITREE;
78 /**
79 * @since 1.5
80 */
81 public IQueryPlannerStrategy layoutStrategy(IQueryBackendContext bContext, IQueryBackendHintProvider hintProvider) {
82 switch (this) {
83 case PSYSTEM_BASIC_LINEAR:
84 return new BasicLinearLayout(bContext);
85 case PSYSTEM_QUASITREE:
86 return new QuasiTreeLayout(bContext, hintProvider);
87 default:
88 throw new UnsupportedOperationException();
89 }
90 }
91 }
92
93 public static final BuilderMethod builderMethod =
94 // BuilderMethod.PSYSTEM_BASIC_LINEAR;
95 BuilderMethod.PSYSTEM_QUASITREE;
96
97 public enum FunctionalDependencyOption {
98 OFF,
99 OPPORTUNISTIC
100 }
101 public static final FunctionalDependencyOption functionalDependencyOption =
102 FunctionalDependencyOption.OPPORTUNISTIC;
103
104 public enum PlanTrimOption {
105 OFF,
106 OPPORTUNISTIC
107 }
108 public static final PlanTrimOption planTrimOption =
109 PlanTrimOption.OPPORTUNISTIC;
110
111}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/OrderingCompareAgent.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/OrderingCompareAgent.java
new file mode 100644
index 00000000..8b147cf6
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/OrderingCompareAgent.java
@@ -0,0 +1,92 @@
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.rete.util;
11
12import java.util.Comparator;
13
14/**
15 * Comparing agent for an ordering. Terminology: the "preferred" item will register as LESS.
16 *
17 * @author Gabor Bergmann
18 *
19 */
20public abstract class OrderingCompareAgent<T> {
21 protected T a;
22 protected T b;
23
24 /**
25 * @param a
26 * @param b
27 */
28 public OrderingCompareAgent(T a, T b) {
29 super();
30 this.a = a;
31 this.b = b;
32 }
33
34 int result = 0;
35
36 protected abstract void doCompare();
37
38 /**
39 * @return the result
40 */
41 public int compare() {
42 doCompare();
43 return result;
44 }
45
46 // COMPARISON HELPERS
47 protected boolean isUnknown() {
48 return result == 0;
49 }
50
51 /**
52 * @pre result == 0
53 */
54 protected boolean consider(int partial) {
55 if (isUnknown())
56 result = partial;
57 return isUnknown();
58 }
59
60 protected boolean swallowBoolean(boolean x) {
61 return x;
62 }
63
64 // PREFERENCE FUNCTIONS
65 protected static int dontCare() {
66 return 0;
67 }
68
69 protected static int preferTrue(boolean b1, boolean b2) {
70 return (b1 ^ b2) ? (b1 ? -1 : +1) : 0;
71 }
72
73 protected static int preferFalse(boolean b1, boolean b2) {
74 return (b1 ^ b2) ? (b2 ? -1 : +1) : 0;
75 }
76
77 protected static <U> int preferLess(Comparable<U> c1, U c2) {
78 return c1.compareTo(c2);
79 }
80
81 protected static <U> int preferLess(U c1, U c2, Comparator<U> comp) {
82 return comp.compare(c1, c2);
83 }
84
85 protected static <U> int preferMore(Comparable<U> c1, U c2) {
86 return -c1.compareTo(c2);
87 }
88 protected static <U> int preferMore(U c1, U c2, Comparator<U> comp) {
89 return -comp.compare(c1, c2);
90 }
91
92}
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/ReteHintOptions.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/ReteHintOptions.java
new file mode 100644
index 00000000..6e685253
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/ReteHintOptions.java
@@ -0,0 +1,60 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, 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.rete.util;
10
11import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
12import tools.refinery.viatra.runtime.matchers.backend.QueryHintOption;
13import tools.refinery.viatra.runtime.rete.matcher.DRedReteBackendFactory;
14
15/**
16 * Provides key objects (of type {@link QueryHintOption}) for {@link QueryEvaluationHint}s.
17 * @author Gabor Bergmann
18 * @since 1.5
19 */
20public final class ReteHintOptions {
21
22 private ReteHintOptions() {/*Utility class constructor*/}
23
24 public static final QueryHintOption<Boolean> useDiscriminatorDispatchersForConstantFiltering =
25 hintOption("useDiscriminatorDispatchersForConstantFiltering", true);
26
27 public static final QueryHintOption<Boolean> prioritizeConstantFiltering =
28 hintOption("prioritizeConstantFiltering", true);
29
30 public static final QueryHintOption<Boolean> cacheOutputOfEvaluatorsByDefault =
31 hintOption("cacheOutputOfEvaluatorsByDefault", true);
32
33 /**
34 * The incremental query evaluator backend can evaluate recursive patterns.
35 * However, by default, instance models that contain cycles are not supported with recursive queries
36 * and can lead to incorrect query results.
37 * Enabling Delete And Rederive (DRED) mode guarantees that recursive query evaluation leads to correct results in these cases as well.
38 *
39 * <p> As DRED may diminish the performance of incremental maintenance, it is not enabled by default.
40 * @since 1.6
41 * @deprecated Use {@link DRedReteBackendFactory} instead of setting this option to true.
42 */
43 @Deprecated
44 public static final QueryHintOption<Boolean> deleteRederiveEvaluation =
45 hintOption("deleteRederiveEvaluation", false);
46
47 /**
48 * This hint allows the query planner to take advantage of "weakened alternative" suggestions of the meta context.
49 * For instance, enumerable unary type constraints may be substituted with a simple type filtering where sufficient.
50 *
51 * @since 1.6
52 */
53 public static final QueryHintOption<Boolean> expandWeakenedAlternativeConstraints =
54 hintOption("expandWeakenedAlternativeConstraints", true);
55
56 // internal helper for conciseness
57 private static <T> QueryHintOption<T> hintOption(String hintKeyLocalName, T defaultValue) {
58 return new QueryHintOption<>(ReteHintOptions.class, hintKeyLocalName, defaultValue);
59 }
60}
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}