diff options
Diffstat (limited to 'subprojects/store-query-viatra/src/main/java/tools/refinery/store/query')
21 files changed, 1509 insertions, 13 deletions
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 5ee8bd74..bf0708bf 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 | |||
@@ -10,7 +10,6 @@ import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; | |||
10 | import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHintOptions; | 10 | import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHintOptions; |
11 | import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; | 11 | import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; |
12 | import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; | 12 | import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; |
13 | import org.eclipse.viatra.query.runtime.rete.matcher.ReteBackendFactory; | ||
14 | import tools.refinery.store.adapter.AbstractModelAdapterBuilder; | 13 | import tools.refinery.store.adapter.AbstractModelAdapterBuilder; |
15 | import tools.refinery.store.model.ModelStore; | 14 | import tools.refinery.store.model.ModelStore; |
16 | import tools.refinery.store.query.dnf.AnyQuery; | 15 | import tools.refinery.store.query.dnf.AnyQuery; |
@@ -24,6 +23,7 @@ import tools.refinery.store.query.viatra.internal.localsearch.FlatCostFunction; | |||
24 | import tools.refinery.store.query.viatra.internal.localsearch.RelationalLocalSearchBackendFactory; | 23 | import tools.refinery.store.query.viatra.internal.localsearch.RelationalLocalSearchBackendFactory; |
25 | import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; | 24 | import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; |
26 | import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery; | 25 | import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery; |
26 | import tools.refinery.store.query.viatra.internal.rete.RefineryReteBackendFactory; | ||
27 | 27 | ||
28 | import java.util.*; | 28 | import java.util.*; |
29 | import java.util.function.Function; | 29 | import 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(ReteBackendFactory.INSTANCE) | 44 | .withDefaultBackend(RefineryReteBackendFactory.INSTANCE) |
45 | .withDefaultCachingBackend(ReteBackendFactory.INSTANCE) | 45 | .withDefaultCachingBackend(RefineryReteBackendFactory.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/pquery/Dnf2PQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java index 15c4e0e1..af3bf32e 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 | |||
@@ -23,6 +23,7 @@ import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameterDirec | |||
23 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery; | 23 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery; |
24 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; | 24 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; |
25 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; | 25 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; |
26 | import tools.refinery.store.query.Constraint; | ||
26 | import tools.refinery.store.query.dnf.Dnf; | 27 | import tools.refinery.store.query.dnf.Dnf; |
27 | import tools.refinery.store.query.dnf.DnfClause; | 28 | import tools.refinery.store.query.dnf.DnfClause; |
28 | import tools.refinery.store.query.dnf.SymbolicParameter; | 29 | import tools.refinery.store.query.dnf.SymbolicParameter; |
@@ -34,7 +35,10 @@ import tools.refinery.store.query.term.Variable; | |||
34 | import tools.refinery.store.query.view.AnySymbolView; | 35 | import tools.refinery.store.query.view.AnySymbolView; |
35 | import tools.refinery.store.util.CycleDetectingMapper; | 36 | import tools.refinery.store.util.CycleDetectingMapper; |
36 | 37 | ||
37 | import java.util.*; | 38 | import java.util.ArrayList; |
39 | import java.util.HashMap; | ||
40 | import java.util.List; | ||
41 | import java.util.Map; | ||
38 | import java.util.function.Function; | 42 | import java.util.function.Function; |
39 | 43 | ||
40 | public class Dnf2PQuery { | 44 | public class Dnf2PQuery { |
@@ -124,6 +128,8 @@ public class Dnf2PQuery { | |||
124 | translateCountLiteral(countLiteral, body); | 128 | translateCountLiteral(countLiteral, body); |
125 | } else if (literal instanceof AggregationLiteral<?, ?> aggregationLiteral) { | 129 | } else if (literal instanceof AggregationLiteral<?, ?> aggregationLiteral) { |
126 | translateAggregationLiteral(aggregationLiteral, body); | 130 | translateAggregationLiteral(aggregationLiteral, body); |
131 | } else if (literal instanceof RepresentativeElectionLiteral representativeElectionLiteral) { | ||
132 | translateRepresentativeElectionLiteral(representativeElectionLiteral, body); | ||
127 | } else { | 133 | } else { |
128 | throw new IllegalArgumentException("Unknown literal: " + literal.toString()); | 134 | throw new IllegalArgumentException("Unknown literal: " + literal.toString()); |
129 | } | 135 | } |
@@ -157,15 +163,7 @@ public class Dnf2PQuery { | |||
157 | } | 163 | } |
158 | case TRANSITIVE -> { | 164 | case TRANSITIVE -> { |
159 | var substitution = translateSubstitution(callLiteral.getArguments(), body); | 165 | var substitution = translateSubstitution(callLiteral.getArguments(), body); |
160 | var constraint = callLiteral.getTarget(); | 166 | var pattern = wrapConstraintWithIdentityArguments(callLiteral.getTarget()); |
161 | PQuery pattern; | ||
162 | if (constraint instanceof Dnf dnf) { | ||
163 | pattern = translate(dnf); | ||
164 | } else if (constraint instanceof AnySymbolView symbolView) { | ||
165 | pattern = wrapperFactory.wrapSymbolViewIdentityArguments(symbolView); | ||
166 | } else { | ||
167 | throw new IllegalArgumentException("Unknown Constraint: " + constraint); | ||
168 | } | ||
169 | new BinaryTransitiveClosure(body, substitution, pattern); | 167 | new BinaryTransitiveClosure(body, substitution, pattern); |
170 | } | 168 | } |
171 | case NEGATIVE -> { | 169 | case NEGATIVE -> { |
@@ -178,6 +176,16 @@ public class Dnf2PQuery { | |||
178 | } | 176 | } |
179 | } | 177 | } |
180 | 178 | ||
179 | private PQuery wrapConstraintWithIdentityArguments(Constraint constraint) { | ||
180 | if (constraint instanceof Dnf dnf) { | ||
181 | return translate(dnf); | ||
182 | } else if (constraint instanceof AnySymbolView symbolView) { | ||
183 | return wrapperFactory.wrapSymbolViewIdentityArguments(symbolView); | ||
184 | } else { | ||
185 | throw new IllegalArgumentException("Unknown Constraint: " + constraint); | ||
186 | } | ||
187 | } | ||
188 | |||
181 | private static Tuple translateSubstitution(List<Variable> substitution, PBody body) { | 189 | private static Tuple translateSubstitution(List<Variable> substitution, PBody body) { |
182 | int arity = substitution.size(); | 190 | int arity = substitution.size(); |
183 | Object[] variables = new Object[arity]; | 191 | Object[] variables = new Object[arity]; |
@@ -240,4 +248,10 @@ public class Dnf2PQuery { | |||
240 | new AggregatorConstraint(boundAggregator, body, substitution, wrappedCall.pattern(), resultVariable, | 248 | new AggregatorConstraint(boundAggregator, body, substitution, wrappedCall.pattern(), resultVariable, |
241 | aggregatedColumn); | 249 | aggregatedColumn); |
242 | } | 250 | } |
251 | |||
252 | private void translateRepresentativeElectionLiteral(RepresentativeElectionLiteral literal, PBody body) { | ||
253 | var substitution = translateSubstitution(literal.getArguments(), body); | ||
254 | var pattern = wrapConstraintWithIdentityArguments(literal.getTarget()); | ||
255 | new RepresentativeElectionConstraint(body, substitution, pattern, literal.getConnectivity()); | ||
256 | } | ||
243 | } | 257 | } |
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RepresentativeElectionConstraint.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RepresentativeElectionConstraint.java new file mode 100644 index 00000000..e146213e --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RepresentativeElectionConstraint.java | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.pquery; | ||
7 | |||
8 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryMetaContext; | ||
9 | import org.eclipse.viatra.query.runtime.matchers.psystem.*; | ||
10 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
11 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery; | ||
12 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; | ||
13 | import tools.refinery.store.query.literal.Connectivity; | ||
14 | |||
15 | import java.util.Set; | ||
16 | |||
17 | public class RepresentativeElectionConstraint extends KeyedEnumerablePConstraint<PQuery> | ||
18 | implements IQueryReference, ITypeInfoProviderConstraint { | ||
19 | private final Connectivity connectivity; | ||
20 | |||
21 | public RepresentativeElectionConstraint(PBody pBody, Tuple variablesTuple, PQuery supplierKey, | ||
22 | Connectivity connectivity) { | ||
23 | super(pBody, variablesTuple, supplierKey); | ||
24 | this.connectivity = connectivity; | ||
25 | } | ||
26 | |||
27 | public Connectivity getConnectivity() { | ||
28 | return connectivity; | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public PQuery getReferredQuery() { | ||
33 | return supplierKey; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
38 | return PositivePatternCall.getTypesImpliedByCall(supplierKey, variablesTuple); | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | protected String keyToString() { | ||
43 | return supplierKey.getFullyQualifiedName() + "#representative"; | ||
44 | } | ||
45 | } | ||
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 new file mode 100644 index 00000000..a833a37b --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefineryPBodyCopier.java | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.pquery.rewriter; | ||
7 | |||
8 | import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; | ||
9 | import org.eclipse.viatra.query.runtime.matchers.psystem.PConstraint; | ||
10 | import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.IRewriterTraceCollector; | ||
11 | import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PBodyCopier; | ||
12 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; | ||
13 | import tools.refinery.store.query.viatra.internal.pquery.RepresentativeElectionConstraint; | ||
14 | |||
15 | public 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 new file mode 100644 index 00000000..ed85a843 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefineryPBodyNormalizer.java | |||
@@ -0,0 +1,38 @@ | |||
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 | *******************************************************************************/ | ||
9 | package tools.refinery.store.query.viatra.internal.pquery.rewriter; | ||
10 | |||
11 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryMetaContext; | ||
12 | import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; | ||
13 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PDisjunction; | ||
14 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery; | ||
15 | import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PBodyCopier; | ||
16 | import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PBodyNormalizer; | ||
17 | |||
18 | import java.util.LinkedHashSet; | ||
19 | import java.util.Set; | ||
20 | |||
21 | public 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 new file mode 100644 index 00000000..dc288ba0 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/rewriter/RefinerySurrogateQueryRewriter.java | |||
@@ -0,0 +1,58 @@ | |||
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 | *******************************************************************************/ | ||
9 | package tools.refinery.store.query.viatra.internal.pquery.rewriter; | ||
10 | |||
11 | import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; | ||
12 | import org.eclipse.viatra.query.runtime.matchers.context.surrogate.SurrogateQueryRegistry; | ||
13 | import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; | ||
14 | import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; | ||
15 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
16 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
17 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PDisjunction; | ||
18 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery; | ||
19 | import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PBodyCopier; | ||
20 | import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PDisjunctionRewriter; | ||
21 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; | ||
22 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; | ||
23 | |||
24 | import java.util.LinkedHashSet; | ||
25 | import java.util.Set; | ||
26 | |||
27 | public 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 new file mode 100644 index 00000000..517e511a --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteBackendFactory.java | |||
@@ -0,0 +1,90 @@ | |||
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 | *******************************************************************************/ | ||
9 | package tools.refinery.store.query.viatra.internal.rete; | ||
10 | |||
11 | import org.eclipse.viatra.query.runtime.matchers.backend.*; | ||
12 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryBackendContext; | ||
13 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery; | ||
14 | import org.eclipse.viatra.query.runtime.rete.construction.plancompiler.ReteRecipeCompiler; | ||
15 | import org.eclipse.viatra.query.runtime.rete.matcher.IncrementalMatcherCapability; | ||
16 | import org.eclipse.viatra.query.runtime.rete.matcher.ReteEngine; | ||
17 | import org.eclipse.viatra.query.runtime.rete.matcher.TimelyConfiguration; | ||
18 | import org.eclipse.viatra.query.runtime.rete.util.Options; | ||
19 | |||
20 | // Singleton implementations follows the VIATRA implementation. | ||
21 | @SuppressWarnings("squid:S6548") | ||
22 | public 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 new file mode 100644 index 00000000..c088219b --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteEngine.java | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.rete; | ||
7 | |||
8 | import org.apache.log4j.Logger; | ||
9 | import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; | ||
10 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryBackendContext; | ||
11 | import org.eclipse.viatra.query.runtime.rete.matcher.ReteEngine; | ||
12 | import org.eclipse.viatra.query.runtime.rete.matcher.TimelyConfiguration; | ||
13 | import org.eclipse.viatra.query.runtime.rete.network.Network; | ||
14 | import org.eclipse.viatra.query.runtime.rete.network.NodeProvisioner; | ||
15 | import org.eclipse.viatra.query.runtime.rete.network.ReteContainer; | ||
16 | |||
17 | import java.io.IOException; | ||
18 | import java.lang.invoke.MethodHandle; | ||
19 | import java.lang.invoke.MethodHandles; | ||
20 | import java.lang.invoke.MethodType; | ||
21 | |||
22 | public 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 new file mode 100644 index 00000000..fd1b48d8 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/RefineryReteRecipeCompiler.java | |||
@@ -0,0 +1,228 @@ | |||
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 | *******************************************************************************/ | ||
9 | package tools.refinery.store.query.viatra.internal.rete; | ||
10 | |||
11 | import org.apache.log4j.Logger; | ||
12 | import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendHintProvider; | ||
13 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryCacheContext; | ||
14 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryMetaContext; | ||
15 | import org.eclipse.viatra.query.runtime.matchers.planning.IQueryPlannerStrategy; | ||
16 | import org.eclipse.viatra.query.runtime.matchers.planning.SubPlan; | ||
17 | import org.eclipse.viatra.query.runtime.matchers.planning.operations.PApply; | ||
18 | import org.eclipse.viatra.query.runtime.matchers.planning.operations.PEnumerate; | ||
19 | import org.eclipse.viatra.query.runtime.matchers.psystem.EnumerablePConstraint; | ||
20 | import org.eclipse.viatra.query.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
21 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery; | ||
22 | import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PDisjunctionRewriterCacher; | ||
23 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; | ||
24 | import org.eclipse.viatra.query.runtime.rete.construction.plancompiler.CompilerHelper; | ||
25 | import org.eclipse.viatra.query.runtime.rete.construction.plancompiler.ReteRecipeCompiler; | ||
26 | import org.eclipse.viatra.query.runtime.rete.matcher.TimelyConfiguration; | ||
27 | import org.eclipse.viatra.query.runtime.rete.recipes.ReteNodeRecipe; | ||
28 | import org.eclipse.viatra.query.runtime.rete.traceability.CompiledSubPlan; | ||
29 | import org.eclipse.viatra.query.runtime.rete.traceability.PlanningTrace; | ||
30 | import org.eclipse.viatra.query.runtime.rete.util.ReteHintOptions; | ||
31 | import org.jetbrains.annotations.Nullable; | ||
32 | import tools.refinery.store.query.viatra.internal.pquery.RepresentativeElectionConstraint; | ||
33 | import tools.refinery.store.query.viatra.internal.rete.recipe.RefineryRecipesFactory; | ||
34 | import tools.refinery.store.query.viatra.internal.pquery.rewriter.RefineryPBodyNormalizer; | ||
35 | import tools.refinery.store.query.viatra.internal.pquery.rewriter.RefinerySurrogateQueryRewriter; | ||
36 | |||
37 | import java.lang.invoke.MethodHandle; | ||
38 | import java.lang.invoke.MethodHandles; | ||
39 | import java.lang.invoke.MethodType; | ||
40 | import java.lang.reflect.Field; | ||
41 | import java.util.Map; | ||
42 | |||
43 | // Since we don't modify VIATRA code, this is our last resort. | ||
44 | @SuppressWarnings("squid:S3011") | ||
45 | public 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 new file mode 100644 index 00000000..0fe5ee27 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RefineryConnectionFactoryExtensions.java | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.rete.network; | ||
7 | |||
8 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; | ||
9 | import org.eclipse.viatra.query.runtime.rete.network.Node; | ||
10 | import org.eclipse.viatra.query.runtime.rete.network.ReteContainer; | ||
11 | import org.eclipse.viatra.query.runtime.rete.network.Supplier; | ||
12 | import org.eclipse.viatra.query.runtime.rete.remote.Address; | ||
13 | import org.eclipse.viatra.query.runtime.rete.traceability.RecipeTraceInfo; | ||
14 | import tools.refinery.store.query.viatra.internal.rete.recipe.RepresentativeElectionRecipe; | ||
15 | |||
16 | import java.util.ArrayList; | ||
17 | |||
18 | public 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 new file mode 100644 index 00000000..82b63a55 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RefineryNodeFactoryExtensions.java | |||
@@ -0,0 +1,44 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.rete.network; | ||
7 | |||
8 | import org.eclipse.viatra.query.runtime.rete.network.ReteContainer; | ||
9 | import org.eclipse.viatra.query.runtime.rete.network.Supplier; | ||
10 | import org.eclipse.viatra.query.runtime.rete.recipes.ReteNodeRecipe; | ||
11 | import org.eclipse.viatra.query.runtime.rete.traceability.TraceInfo; | ||
12 | import org.jetbrains.annotations.Nullable; | ||
13 | import tools.refinery.store.query.viatra.internal.rete.recipe.RepresentativeElectionRecipe; | ||
14 | |||
15 | public 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/network/RepresentativeElectionAlgorithm.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RepresentativeElectionAlgorithm.java new file mode 100644 index 00000000..ff5c7158 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RepresentativeElectionAlgorithm.java | |||
@@ -0,0 +1,137 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.rete.network; | ||
7 | |||
8 | import org.eclipse.viatra.query.runtime.base.itc.graphimpl.Graph; | ||
9 | import org.eclipse.viatra.query.runtime.base.itc.igraph.IGraphObserver; | ||
10 | import org.eclipse.viatra.query.runtime.matchers.util.Direction; | ||
11 | |||
12 | import java.util.*; | ||
13 | |||
14 | public abstract class RepresentativeElectionAlgorithm implements IGraphObserver<Object> { | ||
15 | protected final Graph<Object> graph; | ||
16 | protected final Map<Object, Object> representatives = new HashMap<>(); | ||
17 | protected final Map<Object, Set<Object>> components = new HashMap<>(); | ||
18 | private RepresentativeElectionNode observer; | ||
19 | |||
20 | protected RepresentativeElectionAlgorithm(Graph<Object> graph) { | ||
21 | this.graph = graph; | ||
22 | initializeComponents(); | ||
23 | graph.attachObserver(this); | ||
24 | } | ||
25 | |||
26 | protected abstract void initializeComponents(); | ||
27 | |||
28 | protected void initializeSet(Set<Object> set) { | ||
29 | var iterator = set.iterator(); | ||
30 | if (!iterator.hasNext()) { | ||
31 | // Set is empty. | ||
32 | return; | ||
33 | } | ||
34 | var representative = iterator.next(); | ||
35 | for (var node : set) { | ||
36 | var oldRepresentative = representatives.put(node, representative); | ||
37 | if (oldRepresentative != null && !representative.equals(oldRepresentative)) { | ||
38 | throw new IllegalStateException("Node %s is already in a set represented by %s, cannot add it to %s" | ||
39 | .formatted(node, oldRepresentative, set)); | ||
40 | } | ||
41 | } | ||
42 | components.put(representative, set); | ||
43 | } | ||
44 | |||
45 | protected void merge(Object leftRepresentative, Object rightRepresentative) { | ||
46 | if (leftRepresentative.equals(rightRepresentative)) { | ||
47 | return; | ||
48 | } | ||
49 | var leftSet = getComponent(leftRepresentative); | ||
50 | var rightSet = getComponent(rightRepresentative); | ||
51 | if (leftSet.size() < rightSet.size()) { | ||
52 | merge(rightRepresentative, rightSet, leftRepresentative, leftSet); | ||
53 | } else { | ||
54 | merge(leftRepresentative, leftSet, rightRepresentative, rightSet); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | private void merge(Object preservedRepresentative, Set<Object> preservedSet, Object removedRepresentative, | ||
59 | Set<Object> removedSet) { | ||
60 | components.remove(removedRepresentative); | ||
61 | for (var node : removedSet) { | ||
62 | representatives.put(node, preservedRepresentative); | ||
63 | preservedSet.add(node); | ||
64 | notifyToObservers(node, removedRepresentative, preservedRepresentative); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | protected void assignNewRepresentative(Object oldRepresentative, Set<Object> set) { | ||
69 | var iterator = set.iterator(); | ||
70 | if (!iterator.hasNext()) { | ||
71 | return; | ||
72 | } | ||
73 | var newRepresentative = iterator.next(); | ||
74 | components.put(newRepresentative, set); | ||
75 | for (var node : set) { | ||
76 | var oldRepresentativeOfNode = representatives.put(node, newRepresentative); | ||
77 | if (!oldRepresentative.equals(oldRepresentativeOfNode)) { | ||
78 | throw new IllegalArgumentException("Node %s was not represented by %s but by %s" | ||
79 | .formatted(node, oldRepresentative, oldRepresentativeOfNode)); | ||
80 | } | ||
81 | notifyToObservers(node, oldRepresentative, newRepresentative); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | public void setObserver(RepresentativeElectionNode observer) { | ||
86 | this.observer = observer; | ||
87 | } | ||
88 | |||
89 | public Map<Object, Set<Object>> getComponents() { | ||
90 | return components; | ||
91 | } | ||
92 | |||
93 | public Object getRepresentative(Object node) { | ||
94 | return representatives.get(node); | ||
95 | } | ||
96 | |||
97 | public Set<Object> getComponent(Object representative) { | ||
98 | return components.get(representative); | ||
99 | } | ||
100 | |||
101 | public void dispose() { | ||
102 | graph.detachObserver(this); | ||
103 | } | ||
104 | |||
105 | @Override | ||
106 | public void nodeInserted(Object n) { | ||
107 | var component = new HashSet<>(1); | ||
108 | component.add(n); | ||
109 | initializeSet(component); | ||
110 | notifyToObservers(n, n, Direction.INSERT); | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public void nodeDeleted(Object n) { | ||
115 | var representative = representatives.remove(n); | ||
116 | if (!representative.equals(n)) { | ||
117 | throw new IllegalStateException("Trying to delete node with dangling edges"); | ||
118 | } | ||
119 | components.remove(representative); | ||
120 | notifyToObservers(n, representative, Direction.DELETE); | ||
121 | } | ||
122 | |||
123 | protected void notifyToObservers(Object node, Object oldRepresentative, Object newRepresentative) { | ||
124 | notifyToObservers(node, oldRepresentative, Direction.DELETE); | ||
125 | notifyToObservers(node, newRepresentative, Direction.INSERT); | ||
126 | } | ||
127 | |||
128 | protected void notifyToObservers(Object node, Object representative, Direction direction) { | ||
129 | if (observer != null) { | ||
130 | observer.tupleChanged(node, representative, direction); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | interface Factory { | ||
135 | RepresentativeElectionAlgorithm create(Graph<Object> graph); | ||
136 | } | ||
137 | } | ||
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RepresentativeElectionNode.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RepresentativeElectionNode.java new file mode 100644 index 00000000..701f6ffe --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/RepresentativeElectionNode.java | |||
@@ -0,0 +1,120 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, Gabor Bergmann, 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 | *******************************************************************************/ | ||
9 | package tools.refinery.store.query.viatra.internal.rete.network; | ||
10 | |||
11 | import org.eclipse.viatra.query.runtime.base.itc.graphimpl.Graph; | ||
12 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; | ||
13 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; | ||
14 | import org.eclipse.viatra.query.runtime.matchers.util.Clearable; | ||
15 | import org.eclipse.viatra.query.runtime.matchers.util.Direction; | ||
16 | import org.eclipse.viatra.query.runtime.matchers.util.timeline.Timeline; | ||
17 | import org.eclipse.viatra.query.runtime.rete.network.ReteContainer; | ||
18 | import org.eclipse.viatra.query.runtime.rete.network.communication.Timestamp; | ||
19 | import org.eclipse.viatra.query.runtime.rete.single.SingleInputNode; | ||
20 | |||
21 | import java.util.Collection; | ||
22 | import java.util.Map; | ||
23 | |||
24 | public class RepresentativeElectionNode extends SingleInputNode implements Clearable { | ||
25 | private final RepresentativeElectionAlgorithm.Factory algorithmFactory; | ||
26 | private Graph<Object> graph; | ||
27 | private RepresentativeElectionAlgorithm algorithm; | ||
28 | |||
29 | public RepresentativeElectionNode(ReteContainer reteContainer, | ||
30 | RepresentativeElectionAlgorithm.Factory algorithmFactory) { | ||
31 | super(reteContainer); | ||
32 | this.algorithmFactory = algorithmFactory; | ||
33 | graph = new Graph<>(); | ||
34 | algorithm = algorithmFactory.create(graph); | ||
35 | algorithm.setObserver(this); | ||
36 | reteContainer.registerClearable(this); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public void networkStructureChanged() { | ||
41 | if (reteContainer.isTimelyEvaluation() && reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { | ||
42 | throw new IllegalStateException(this + " cannot be used in recursive differential dataflow evaluation!"); | ||
43 | } | ||
44 | super.networkStructureChanged(); | ||
45 | } | ||
46 | |||
47 | public void reinitializeWith(Collection<Tuple> tuples) { | ||
48 | algorithm.dispose(); | ||
49 | graph = new Graph<>(); | ||
50 | for (var tuple : tuples) { | ||
51 | insertEdge(tuple.get(0), tuple.get(1)); | ||
52 | } | ||
53 | algorithm = algorithmFactory.create(graph); | ||
54 | algorithm.setObserver(this); | ||
55 | } | ||
56 | |||
57 | public void tupleChanged(Object source, Object representative, Direction direction) { | ||
58 | var tuple = Tuples.staticArityFlatTupleOf(source, representative); | ||
59 | propagateUpdate(direction, tuple, Timestamp.ZERO); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public void clear() { | ||
64 | algorithm.dispose(); | ||
65 | graph = new Graph<>(); | ||
66 | algorithm = algorithmFactory.create(graph); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { | ||
71 | var source = updateElement.get(0); | ||
72 | var target = updateElement.get(1); | ||
73 | switch (direction) { | ||
74 | case INSERT -> insertEdge(source, target); | ||
75 | case DELETE -> deleteEdge(source, target); | ||
76 | default -> throw new IllegalArgumentException("Unknown direction: " + direction); | ||
77 | } | ||
78 | } | ||
79 | |||
80 | private void insertEdge(Object source, Object target) { | ||
81 | graph.insertNode(source); | ||
82 | graph.insertNode(target); | ||
83 | graph.insertEdge(source, target); | ||
84 | } | ||
85 | |||
86 | private void deleteEdge(Object source, Object target) { | ||
87 | graph.deleteEdgeIfExists(source, target); | ||
88 | if (isIsolated(source)) { | ||
89 | graph.deleteNode(source); | ||
90 | } | ||
91 | if (!source.equals(target) && isIsolated(target)) { | ||
92 | graph.deleteNode(target); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | private boolean isIsolated(Object node) { | ||
97 | return graph.getTargetNodes(node).isEmpty() && graph.getSourceNodes(node).isEmpty(); | ||
98 | } | ||
99 | |||
100 | @Override | ||
101 | public void pullInto(Collection<Tuple> collector, boolean flush) { | ||
102 | for (var entry : algorithm.getComponents().entrySet()) { | ||
103 | var representative = entry.getKey(); | ||
104 | for (var node : entry.getValue()) { | ||
105 | collector.add(Tuples.staticArityFlatTupleOf(node, representative)); | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | |||
110 | @Override | ||
111 | public void pullIntoWithTimeline(Map<Tuple, Timeline<Timestamp>> collector, boolean flush) { | ||
112 | // Use all zero timestamps because this node cannot be used in recursive groups anyway. | ||
113 | for (var entry : algorithm.getComponents().entrySet()) { | ||
114 | var representative = entry.getKey(); | ||
115 | for (var node : entry.getValue()) { | ||
116 | collector.put(Tuples.staticArityFlatTupleOf(node, representative), Timestamp.INSERT_AT_ZERO_TIMELINE); | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | } | ||
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/StronglyConnectedComponentAlgorithm.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/StronglyConnectedComponentAlgorithm.java new file mode 100644 index 00000000..11155f3e --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/StronglyConnectedComponentAlgorithm.java | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.rete.network; | ||
7 | |||
8 | import org.eclipse.viatra.query.runtime.base.itc.alg.misc.GraphHelper; | ||
9 | import org.eclipse.viatra.query.runtime.base.itc.alg.misc.bfs.BFS; | ||
10 | import org.eclipse.viatra.query.runtime.base.itc.alg.misc.scc.SCC; | ||
11 | import org.eclipse.viatra.query.runtime.base.itc.graphimpl.Graph; | ||
12 | |||
13 | import java.util.Collection; | ||
14 | import java.util.Set; | ||
15 | |||
16 | public class StronglyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm { | ||
17 | public StronglyConnectedComponentAlgorithm(Graph<Object> graph) { | ||
18 | super(graph); | ||
19 | } | ||
20 | |||
21 | @Override | ||
22 | protected void initializeComponents() { | ||
23 | var computedSCCs = SCC.computeSCC(graph).getSccs(); | ||
24 | for (var computedSCC : computedSCCs) { | ||
25 | initializeSet(computedSCC); | ||
26 | } | ||
27 | } | ||
28 | |||
29 | @Override | ||
30 | public void edgeInserted(Object source, Object target) { | ||
31 | var sourceRoot = getRepresentative(source); | ||
32 | var targetRoot = getRepresentative(target); | ||
33 | if (sourceRoot.equals(targetRoot)) { | ||
34 | // New edge does not change strongly connected components. | ||
35 | return; | ||
36 | } | ||
37 | if (BFS.isReachable(target, source, graph)) { | ||
38 | merge(sourceRoot, targetRoot); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public void edgeDeleted(Object source, Object target) { | ||
44 | var sourceRoot = getRepresentative(source); | ||
45 | var targetRoot = getRepresentative(target); | ||
46 | if (!sourceRoot.equals(targetRoot)) { | ||
47 | // New edge does not change strongly connected components. | ||
48 | return; | ||
49 | } | ||
50 | var component = GraphHelper.getSubGraph(getComponent(sourceRoot), graph); | ||
51 | if (!BFS.isReachable(source, target, component)) { | ||
52 | var newSCCs = SCC.computeSCC(component).getSccs(); | ||
53 | split(sourceRoot, newSCCs); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | private void split(Object preservedRepresentative, Collection<? extends Set<Object>> sets) { | ||
58 | for (var set : sets) { | ||
59 | if (set.contains(preservedRepresentative)) { | ||
60 | components.put(preservedRepresentative, set); | ||
61 | } else { | ||
62 | assignNewRepresentative(preservedRepresentative, set); | ||
63 | } | ||
64 | } | ||
65 | } | ||
66 | } | ||
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/WeaklyConnectedComponentAlgorithm.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/WeaklyConnectedComponentAlgorithm.java new file mode 100644 index 00000000..118004dd --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/network/WeaklyConnectedComponentAlgorithm.java | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.rete.network; | ||
7 | |||
8 | import org.eclipse.viatra.query.runtime.base.itc.graphimpl.Graph; | ||
9 | |||
10 | import java.util.ArrayDeque; | ||
11 | import java.util.HashSet; | ||
12 | import java.util.Set; | ||
13 | |||
14 | public class WeaklyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm { | ||
15 | public WeaklyConnectedComponentAlgorithm(Graph<Object> graph) { | ||
16 | super(graph); | ||
17 | } | ||
18 | |||
19 | @Override | ||
20 | protected void initializeComponents() { | ||
21 | for (var node : graph.getAllNodes()) { | ||
22 | if (representatives.containsKey(node)) { | ||
23 | continue; | ||
24 | } | ||
25 | var reachable = getReachableNodes(node); | ||
26 | initializeSet(reachable); | ||
27 | } | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public void edgeInserted(Object source, Object target) { | ||
32 | var sourceRoot = getRepresentative(source); | ||
33 | var targetRoot = getRepresentative(target); | ||
34 | merge(sourceRoot, targetRoot); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public void edgeDeleted(Object source, Object target) { | ||
39 | var sourceRoot = getRepresentative(source); | ||
40 | var targetRoot = getRepresentative(target); | ||
41 | if (!sourceRoot.equals(targetRoot)) { | ||
42 | throw new IllegalArgumentException("Trying to remove edge not in graph"); | ||
43 | } | ||
44 | var targetReachable = getReachableNodes(target); | ||
45 | if (!targetReachable.contains(source)) { | ||
46 | split(sourceRoot, targetReachable); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | private void split(Object sourceRepresentative, Set<Object> targetReachable) { | ||
51 | var sourceComponent = getComponent(sourceRepresentative); | ||
52 | sourceComponent.removeAll(targetReachable); | ||
53 | if (targetReachable.contains(sourceRepresentative)) { | ||
54 | components.put(sourceRepresentative, targetReachable); | ||
55 | assignNewRepresentative(sourceRepresentative, sourceComponent); | ||
56 | } else { | ||
57 | assignNewRepresentative(sourceRepresentative, targetReachable); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | private Set<Object> getReachableNodes(Object source) { | ||
62 | var retSet = new HashSet<>(); | ||
63 | retSet.add(source); | ||
64 | var nodeQueue = new ArrayDeque<>(); | ||
65 | nodeQueue.addLast(source); | ||
66 | |||
67 | while (!nodeQueue.isEmpty()) { | ||
68 | var node = nodeQueue.removeFirst(); | ||
69 | for (var neighbor : graph.getTargetNodes(node).distinctValues()) { | ||
70 | if (!retSet.contains(neighbor)) { | ||
71 | retSet.add(neighbor); | ||
72 | nodeQueue.addLast(neighbor); | ||
73 | } | ||
74 | } | ||
75 | for (var neighbor : graph.getSourceNodes(node).distinctValues()) { | ||
76 | if (!retSet.contains(neighbor)) { | ||
77 | retSet.add(neighbor); | ||
78 | nodeQueue.addLast(neighbor); | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | return retSet; | ||
84 | } | ||
85 | } | ||
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 new file mode 100644 index 00000000..1f8b3034 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesFactory.java | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.rete.recipe; | ||
7 | |||
8 | import org.eclipse.emf.ecore.EDataType; | ||
9 | import org.eclipse.emf.ecore.EFactory; | ||
10 | import tools.refinery.store.query.literal.Connectivity; | ||
11 | |||
12 | // Naming and index computation follows EMF conventions. | ||
13 | @SuppressWarnings("squid:S115") | ||
14 | public 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 new file mode 100644 index 00000000..4e2a695c --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesFactoryImpl.java | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.rete.recipe; | ||
7 | |||
8 | import org.eclipse.emf.ecore.EClass; | ||
9 | import org.eclipse.emf.ecore.EDataType; | ||
10 | import org.eclipse.emf.ecore.EObject; | ||
11 | import org.eclipse.emf.ecore.EPackage; | ||
12 | import org.eclipse.emf.ecore.impl.EFactoryImpl; | ||
13 | import org.eclipse.emf.ecore.plugin.EcorePlugin; | ||
14 | import tools.refinery.store.query.literal.Connectivity; | ||
15 | |||
16 | public 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 new file mode 100644 index 00000000..6c933c45 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesPackage.java | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.rete.recipe; | ||
7 | |||
8 | import org.eclipse.emf.ecore.*; | ||
9 | import org.eclipse.viatra.query.runtime.rete.recipes.RecipesPackage; | ||
10 | |||
11 | // Naming and index computation follows EMF conventions. | ||
12 | @SuppressWarnings({"squid:S100", "squid:S115", "PointlessArithmeticExpression"}) | ||
13 | public 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 new file mode 100644 index 00000000..d5073402 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RefineryRecipesPackageImpl.java | |||
@@ -0,0 +1,96 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.rete.recipe; | ||
7 | |||
8 | import org.eclipse.emf.ecore.*; | ||
9 | import org.eclipse.emf.ecore.impl.EPackageImpl; | ||
10 | import org.eclipse.viatra.query.runtime.rete.recipes.RecipesPackage; | ||
11 | import tools.refinery.store.query.literal.Connectivity; | ||
12 | |||
13 | public 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 new file mode 100644 index 00000000..def825c2 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RepresentativeElectionRecipe.java | |||
@@ -0,0 +1,17 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.rete.recipe; | ||
7 | |||
8 | import org.eclipse.viatra.query.runtime.rete.recipes.AlphaRecipe; | ||
9 | import tools.refinery.store.query.literal.Connectivity; | ||
10 | |||
11 | public 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 new file mode 100644 index 00000000..959836d2 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/rete/recipe/RepresentativeElectionRecipeImpl.java | |||
@@ -0,0 +1,105 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.viatra.internal.rete.recipe; | ||
7 | |||
8 | import org.eclipse.emf.common.notify.Notification; | ||
9 | import org.eclipse.emf.common.util.EList; | ||
10 | import org.eclipse.emf.ecore.EClass; | ||
11 | import org.eclipse.emf.ecore.impl.ENotificationImpl; | ||
12 | import org.eclipse.viatra.query.runtime.rete.recipes.RecipesPackage; | ||
13 | import org.eclipse.viatra.query.runtime.rete.recipes.ReteNodeRecipe; | ||
14 | import org.eclipse.viatra.query.runtime.rete.recipes.impl.AlphaRecipeImpl; | ||
15 | import tools.refinery.store.query.literal.Connectivity; | ||
16 | |||
17 | import java.lang.reflect.InvocationTargetException; | ||
18 | |||
19 | public 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 | } | ||