diff options
author | 2023-08-19 02:31:57 +0200 | |
---|---|---|
committer | 2023-08-19 02:31:57 +0200 | |
commit | 9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b (patch) | |
tree | fad77963c1dc9028b0a767ac3ad69a174c8eb8c6 /subprojects/viatra-runtime-matchers | |
parent | feat: predicate semantics (diff) | |
download | refinery-9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b.tar.gz refinery-9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b.tar.zst refinery-9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b.zip |
chore: import VIATRA source
Make our modifications more maintainable by editing the source code directly
instead of using reflection.
Diffstat (limited to 'subprojects/viatra-runtime-matchers')
229 files changed, 21378 insertions, 0 deletions
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 ("Content"). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the | ||
19 | Eclipse Public License Version 2.0 ("EPL"). A copy of the EPL is available at <a href="http://www.eclipse.org/org/documents/epl-v20.php">http://www.eclipse.org/legal/epl-v20.html</a>. | ||
20 | For purposes of the EPL, "Program" will mean the Content.</p> | ||
21 | |||
22 | <p>If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party ("Redistributor") and different terms and conditions may | ||
23 | apply to your use of any object code in the Content. Check the Redistributor's license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise | ||
24 | indicated below, the terms and conditions of the EPL still apply to any source code in the Content and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> | ||
25 | </body> | ||
26 | </html> | ||
diff --git a/subprojects/viatra-runtime-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 | |||
7 | plugins { | ||
8 | id("tools.refinery.gradle.java-library") | ||
9 | } | ||
10 | |||
11 | dependencies { | ||
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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
17 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.aggregators; | ||
10 | |||
11 | /** | ||
12 | * @since 2.0 | ||
13 | */ | ||
14 | class 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.aggregators; | ||
10 | |||
11 | import java.util.OptionalDouble; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
15 | |||
16 | /** | ||
17 | * @author Zoltan Ujhelyi | ||
18 | * @since 2.0 | ||
19 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.aggregators; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.aggregators; | ||
10 | |||
11 | import java.util.Comparator; | ||
12 | import java.util.SortedMap; | ||
13 | import java.util.TreeMap; | ||
14 | import java.util.stream.Stream; | ||
15 | |||
16 | import 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 | */ | ||
24 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.aggregators; | ||
10 | |||
11 | import java.util.OptionalDouble; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
15 | |||
16 | /** | ||
17 | * @author Zoltan Ujhelyi | ||
18 | * @since 2.0 | ||
19 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.aggregators; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.aggregators; | ||
10 | |||
11 | import java.util.OptionalDouble; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
15 | |||
16 | /** | ||
17 | * @author Zoltan Ujhelyi | ||
18 | * @since 2.0 | ||
19 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.aggregators; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.aggregators; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
13 | import 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}) | ||
27 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.aggregators; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
13 | import 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}) | ||
23 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.aggregators; | ||
10 | |||
11 | import java.math.BigDecimal; | ||
12 | import java.math.BigInteger; | ||
13 | import java.util.Calendar; | ||
14 | import java.util.Date; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
18 | import 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}) | ||
35 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.aggregators; | ||
10 | |||
11 | import java.math.BigDecimal; | ||
12 | import java.math.BigInteger; | ||
13 | import java.util.Calendar; | ||
14 | import java.util.Date; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
18 | import 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}) | ||
35 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.aggregators; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
13 | import 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}) | ||
27 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.algorithms; | ||
10 | |||
11 | import java.util.Comparator; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.NoSuchElementException; | ||
14 | |||
15 | /** | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 2.1 | ||
18 | * | ||
19 | */ | ||
20 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.algorithms; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Iterator; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import 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 | */ | ||
29 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.algorithms; | ||
11 | |||
12 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IRewriterTraceCollector; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.NopTraceCollector; | ||
13 | |||
14 | /** | ||
15 | * Query evaluation hints applicable to any engine | ||
16 | * @since 1.6 | ||
17 | * | ||
18 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
13 | import 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 | ||
26 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
12 | import 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
12 | import 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 | */ | ||
23 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.backend; | ||
10 | |||
11 | /** | ||
12 | * A provider interface for {@link IQueryBackendFactory} instances. | ||
13 | * @since 2.0 | ||
14 | */ | ||
15 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.backend; | ||
10 | |||
11 | import 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 | */ | ||
21 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.backend; | ||
10 | |||
11 | import java.util.Optional; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
18 | import 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 | */ | ||
25 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.backend; | ||
10 | |||
11 | import 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 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.backend; | ||
10 | |||
11 | import java.util.AbstractMap; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.Map; | ||
15 | import java.util.Objects; | ||
16 | import java.util.stream.Collectors; | ||
17 | |||
18 | import 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 | */ | ||
31 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.backend; | ||
10 | |||
11 | import java.util.Map; | ||
12 | import 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
13 | import 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.context; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Comparator; | ||
14 | import java.util.HashMap; | ||
15 | import java.util.Map; | ||
16 | import 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 | */ | ||
25 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.context; | ||
10 | |||
11 | import 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 | */ | ||
21 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.context; | ||
10 | |||
11 | import org.apache.log4j.Logger; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability; | ||
13 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; | ||
14 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
16 | import 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 | */ | ||
25 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.context; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; | ||
13 | import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; | ||
14 | import 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.context; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Comparator; | ||
13 | import java.util.Map; | ||
14 | import 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.context; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
13 | import 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.context; | ||
10 | |||
11 | import java.lang.reflect.InvocationTargetException; | ||
12 | import java.util.Optional; | ||
13 | import java.util.concurrent.Callable; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
19 | import 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 | */ | ||
28 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.context; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | |||
13 | /** | ||
14 | * Listens for changes in the runtime context. | ||
15 | * @author Bergmann Gabor | ||
16 | * | ||
17 | */ | ||
18 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
18 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.context; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import 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 | */ | ||
26 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.context.common; | ||
10 | |||
11 | import 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 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
23 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.context.surrogate; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.NoSuchElementException; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.IProvider; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.SingletonInstanceProvider; | ||
23 | |||
24 | /** | ||
25 | * @author Abel Hegedus | ||
26 | * | ||
27 | */ | ||
28 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.memories; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Map; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
20 | import 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 | */ | ||
29 | abstract 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.memories; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.Map; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; | ||
24 | import 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 | */ | ||
33 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.memories; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
17 | import 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 | */ | ||
25 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.memories; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.Map; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyDefaultMaskedTupleMemory; | ||
17 | import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyIdentityMaskedTupleMemory; | ||
18 | import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyNullaryMaskedTupleMemory; | ||
19 | import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyUnaryMaskedTupleMemory; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
22 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.Clearable; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.resumable.MaskedResumable; | ||
26 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
27 | import 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 | */ | ||
41 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.memories; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
19 | import 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 | */ | ||
27 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.memories; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; | ||
24 | import 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 | */ | ||
32 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Objects; | ||
16 | import java.util.Set; | ||
17 | import java.util.TreeMap; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
26 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
27 | import 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 | */ | ||
36 | abstract 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
23 | import 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 | */ | ||
32 | abstract 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.memories.timely; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
23 | import 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 | */ | ||
31 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
22 | import 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 | */ | ||
30 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
21 | import 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 | */ | ||
29 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
24 | import 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 | */ | ||
32 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.planning; | ||
11 | |||
12 | import java.util.Map; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import 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 | */ | ||
33 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.planning; | ||
11 | |||
12 | import org.apache.log4j.Logger; | ||
13 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
15 | import 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 | */ | ||
23 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.planning; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
12 | |||
13 | /** | ||
14 | * @author Zoltan Ujhelyi | ||
15 | * @since 0.9 | ||
16 | */ | ||
17 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.planning; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Set; | ||
16 | import java.util.WeakHashMap; | ||
17 | import java.util.stream.Collectors; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
20 | import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; | ||
21 | import tools.refinery.viatra.runtime.matchers.planning.operations.POperation; | ||
22 | import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; | ||
23 | import tools.refinery.viatra.runtime.matchers.planning.operations.PStart; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
27 | import 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 | */ | ||
46 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.planning; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.planning.operations.POperation; | ||
12 | import 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 | */ | ||
20 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.planning.helpers; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
20 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
21 | import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory; | ||
22 | import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
28 | |||
29 | /** | ||
30 | * @author Gabor Bergmann | ||
31 | * | ||
32 | */ | ||
33 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.planning.helpers; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.Map; | ||
16 | import java.util.Map.Entry; | ||
17 | import java.util.Set; | ||
18 | |||
19 | import 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 | */ | ||
30 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.planning.helpers; | ||
10 | |||
11 | import java.util.Optional; | ||
12 | import java.util.function.BiFunction; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
15 | import 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 | */ | ||
24 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.planning.helpers; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.LinkedList; | ||
16 | import java.util.Map; | ||
17 | import java.util.Map.Entry; | ||
18 | import java.util.Queue; | ||
19 | import java.util.Set; | ||
20 | import java.util.stream.Collectors; | ||
21 | |||
22 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
23 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
28 | |||
29 | /** | ||
30 | * @author Gabor Bergmann | ||
31 | * @author Tamas Szabo | ||
32 | */ | ||
33 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.planning.operations; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
16 | import 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 | */ | ||
29 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.planning.operations; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; | ||
15 | import 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 | */ | ||
25 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.planning.operations; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
15 | |||
16 | /** | ||
17 | * Represents a natural join of two parent SubPlans. | ||
18 | * @author Bergmann Gabor | ||
19 | * | ||
20 | */ | ||
21 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.planning.operations; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
15 | import 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 | */ | ||
25 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.planning.operations; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.List; | ||
14 | import java.util.Set; | ||
15 | import java.util.stream.Collectors; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
20 | import 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 | */ | ||
29 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.planning.operations; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Set; | ||
15 | import java.util.stream.Collectors; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
18 | import 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 | */ | ||
30 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
13 | |||
14 | import java.util.Collections; | ||
15 | import java.util.HashSet; | ||
16 | import java.util.Map; | ||
17 | import java.util.Set; | ||
18 | import java.util.concurrent.atomic.AtomicInteger; | ||
19 | |||
20 | /** | ||
21 | * @author Gabor Bergmann | ||
22 | * | ||
23 | */ | ||
24 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
15 | import 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 | */ | ||
23 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | |||
13 | import 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.List; | ||
13 | |||
14 | import 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import 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 | */ | ||
24 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | |||
20 | import java.util.Set; | ||
21 | |||
22 | /** | ||
23 | * Common superinterface of enumerable and deferred type constraints. | ||
24 | * @author Bergmann Gabor | ||
25 | * | ||
26 | */ | ||
27 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
15 | |||
16 | /** | ||
17 | * @author Gabor Bergmann | ||
18 | * | ||
19 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
18 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PProblem; | ||
16 | import 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 | */ | ||
24 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | |||
14 | /** | ||
15 | * @author Gabor Bergmann | ||
16 | * | ||
17 | */ | ||
18 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.LinkedHashSet; | ||
16 | import java.util.List; | ||
17 | import java.util.Map; | ||
18 | import java.util.Set; | ||
19 | import java.util.WeakHashMap; | ||
20 | import java.util.stream.Collectors; | ||
21 | |||
22 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
23 | import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
28 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
29 | import 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 | */ | ||
37 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.Comparator; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
18 | |||
19 | /** | ||
20 | * @author Gabor Bergmann | ||
21 | * | ||
22 | */ | ||
23 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
10 | |||
11 | /** | ||
12 | * Marker interface for PSystem elements that can be traced. | ||
13 | * @since 1.6 | ||
14 | */ | ||
15 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.HashSet; | ||
13 | import java.util.Set; | ||
14 | |||
15 | /** | ||
16 | * @author Gabor Bergmann | ||
17 | * | ||
18 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.List; | ||
14 | import java.util.Map; | ||
15 | import java.util.stream.Collectors; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
19 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
20 | import tools.refinery.viatra.runtime.matchers.context.InputKeyImplication; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | import 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 | */ | ||
31 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
15 | import 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 | */ | ||
23 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
17 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.aggregations; | ||
10 | |||
11 | import java.lang.annotation.ElementType; | ||
12 | import java.lang.annotation.Inherited; | ||
13 | import java.lang.annotation.Retention; | ||
14 | import java.lang.annotation.RetentionPolicy; | ||
15 | import java.lang.annotation.Target; | ||
16 | |||
17 | import 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 | ||
44 | public @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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.aggregations; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
12 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package 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 | |||
28 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.aggregations; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import 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 | */ | ||
38 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.analysis; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Set; | ||
16 | import java.util.stream.Collectors; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
20 | import tools.refinery.viatra.runtime.matchers.planning.helpers.FunctionalDependencyHelper; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.ParameterReference; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
28 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
29 | import 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 | */ | ||
42 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.annotations; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Optional; | ||
13 | import java.util.Set; | ||
14 | import java.util.function.BiConsumer; | ||
15 | |||
16 | import org.eclipse.collections.api.multimap.MutableMultimap; | ||
17 | import org.eclipse.collections.impl.multimap.list.FastListMultimap; | ||
18 | |||
19 | /** | ||
20 | * A container describing query annotations | ||
21 | * @author Zoltan Ujhelyi | ||
22 | * | ||
23 | */ | ||
24 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
17 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
25 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
26 | import 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 | */ | ||
34 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.Set; | ||
14 | import java.util.stream.Collectors; | ||
15 | import java.util.stream.Stream; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
23 | |||
24 | /** | ||
25 | * @author Gabor Bergmann | ||
26 | * | ||
27 | */ | ||
28 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
23 | |||
24 | /** | ||
25 | * @author Gabor Bergmann | ||
26 | * | ||
27 | */ | ||
28 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
21 | |||
22 | /** | ||
23 | * @author Gabor Bergmann | ||
24 | * | ||
25 | */ | ||
26 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.LinkedHashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
22 | |||
23 | /** | ||
24 | * @author Zoltan Ujhelyi | ||
25 | * | ||
26 | */ | ||
27 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | import java.util.Collections; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
20 | |||
21 | /** | ||
22 | * @author Gabor Bergmann | ||
23 | * | ||
24 | */ | ||
25 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | |||
20 | /** | ||
21 | * @author Gabor Bergmann | ||
22 | * | ||
23 | */ | ||
24 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | |||
25 | /** | ||
26 | * @author Gabor Bergmann | ||
27 | * | ||
28 | */ | ||
29 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
22 | |||
23 | /** | ||
24 | * @author Gabor Bergmann | ||
25 | */ | ||
26 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.IMultiQueryReference; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
18 | import 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 | */ | ||
36 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Map; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
23 | import 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 | */ | ||
34 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
21 | |||
22 | /** | ||
23 | * @since 2.0 | ||
24 | */ | ||
25 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
13 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
16 | import 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 | */ | ||
24 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
14 | import 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 | */ | ||
22 | public 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 | */ | ||
6 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
7 | |||
8 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
22 | |||
23 | /** | ||
24 | * @author Gabor Bergmann | ||
25 | * | ||
26 | */ | ||
27 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
11 | |||
12 | import java.util.HashSet; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
25 | |||
26 | /** | ||
27 | * @author Gabor Bergmann | ||
28 | * | ||
29 | */ | ||
30 | public 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/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java new file mode 100644 index 00000000..b97ff55f --- /dev/null +++ b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
7 | |||
8 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
9 | import tools.refinery.viatra.runtime.matchers.psystem.*; | ||
10 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | |||
13 | import java.util.Set; | ||
14 | |||
15 | public class RepresentativeElectionConstraint extends KeyedEnumerablePConstraint<PQuery> | ||
16 | implements IQueryReference, ITypeInfoProviderConstraint { | ||
17 | private final Connectivity connectivity; | ||
18 | |||
19 | public RepresentativeElectionConstraint(PBody pBody, Tuple variablesTuple, PQuery supplierKey, | ||
20 | Connectivity connectivity) { | ||
21 | super(pBody, variablesTuple, supplierKey); | ||
22 | this.connectivity = connectivity; | ||
23 | } | ||
24 | |||
25 | public Connectivity getConnectivity() { | ||
26 | return connectivity; | ||
27 | } | ||
28 | |||
29 | @Override | ||
30 | public PQuery getReferredQuery() { | ||
31 | return supplierKey; | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
36 | return PositivePatternCall.getTypesImpliedByCall(supplierKey, variablesTuple); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | protected String keyToString() { | ||
41 | return supplierKey.getFullyQualifiedName() + "#" + connectivity + "#representative"; | ||
42 | } | ||
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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Map; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
22 | import 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 | */ | ||
33 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.queries; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Objects; | ||
16 | import java.util.Optional; | ||
17 | import java.util.Set; | ||
18 | import java.util.stream.Collectors; | ||
19 | import java.util.stream.Stream; | ||
20 | |||
21 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
22 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; | ||
23 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
24 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; | ||
28 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
29 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
30 | |||
31 | /** | ||
32 | * Default implementation of PQuery. | ||
33 | * | ||
34 | * @author Bergmann Gabor | ||
35 | */ | ||
36 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.queries; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.LinkedHashSet; | ||
13 | import java.util.Set; | ||
14 | import java.util.stream.Collectors; | ||
15 | |||
16 | import 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 | */ | ||
26 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.queries; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | import 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 | */ | ||
21 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
18 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.queries; | ||
10 | |||
11 | import 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 | */ | ||
18 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.queries; | ||
10 | |||
11 | import java.util.HashSet; | ||
12 | import java.util.Set; | ||
13 | import java.util.function.Function; | ||
14 | import java.util.function.Predicate; | ||
15 | import java.util.stream.Stream; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.IMultiQueryReference; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
23 | import 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 | */ | ||
31 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.queries; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
15 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; | ||
16 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; | ||
17 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
20 | import 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 | */ | ||
38 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.queries; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Optional; | ||
13 | |||
14 | import 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 | */ | ||
25 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.queries; | ||
10 | |||
11 | /** | ||
12 | * @author Zoltan Ujhelyi | ||
13 | * @since 2.0 | ||
14 | * | ||
15 | */ | ||
16 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.queries; | ||
10 | |||
11 | import 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 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
15 | |||
16 | /** | ||
17 | * @since 1.6 | ||
18 | * | ||
19 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
16 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
11 | |||
12 | /** | ||
13 | * @author Marton Bur | ||
14 | * | ||
15 | */ | ||
16 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.List; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Objects; | ||
16 | import java.util.Set; | ||
17 | import java.util.stream.Collectors; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
27 | import 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 | */ | ||
35 | class 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
12 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
17 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
14 | import 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 | */ | ||
24 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
12 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.LinkedList; | ||
15 | import java.util.Map; | ||
16 | import java.util.Queue; | ||
17 | import java.util.Set; | ||
18 | import java.util.function.Predicate; | ||
19 | import java.util.stream.Stream; | ||
20 | |||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
26 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
27 | import 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 | */ | ||
35 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
12 | |||
13 | /** | ||
14 | * @author Grill Balázs | ||
15 | * @since 1.4 | ||
16 | * | ||
17 | */ | ||
18 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.*; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.*; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.AllowAllFilter; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.SameName; | ||
22 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
24 | |||
25 | import java.util.*; | ||
26 | import 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 | */ | ||
34 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.Collection; | ||
14 | import java.util.Collections; | ||
15 | import java.util.Comparator; | ||
16 | import java.util.HashMap; | ||
17 | import java.util.HashSet; | ||
18 | import java.util.Iterator; | ||
19 | import java.util.LinkedHashSet; | ||
20 | import java.util.LinkedList; | ||
21 | import java.util.List; | ||
22 | import java.util.Map; | ||
23 | import java.util.Queue; | ||
24 | import java.util.Set; | ||
25 | |||
26 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
27 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
28 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
29 | import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
33 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
34 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
35 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality; | ||
36 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality; | ||
37 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
38 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
39 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
40 | import 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 | */ | ||
50 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
12 | import 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 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Arrays; | ||
13 | import java.util.Collections; | ||
14 | import java.util.List; | ||
15 | import java.util.WeakHashMap; | ||
16 | |||
17 | import 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 | */ | ||
25 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import java.util.ArrayDeque; | ||
12 | import java.util.ArrayList; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Deque; | ||
15 | import java.util.HashMap; | ||
16 | import java.util.HashSet; | ||
17 | import java.util.LinkedHashSet; | ||
18 | import java.util.LinkedList; | ||
19 | import java.util.List; | ||
20 | import java.util.Map; | ||
21 | import java.util.Set; | ||
22 | |||
23 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
28 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
29 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.AllowAllFilter; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.ExportedParameterFilter; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.HierarchicalName; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.SameName; | ||
33 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
34 | import 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 | */ | ||
42 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import 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 | */ | ||
18 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import java.util.LinkedHashSet; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
15 | import tools.refinery.viatra.runtime.matchers.context.surrogate.SurrogateQueryRegistry; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
25 | |||
26 | /** | ||
27 | * @author Zoltan Ujhelyi | ||
28 | * | ||
29 | */ | ||
30 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.LinkedHashMap; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
18 | import 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 | */ | ||
27 | class 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
12 | import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableContext; | ||
13 | import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableWriterBinary; | ||
14 | import 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 | */ | ||
27 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
12 | import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableContext; | ||
13 | import tools.refinery.viatra.runtime.matchers.scopes.tables.SimpleBinaryTable; | ||
14 | import 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 | */ | ||
26 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.scopes; | ||
11 | |||
12 | import java.lang.reflect.InvocationTargetException; | ||
13 | import java.util.concurrent.Callable; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.context.IndexingService; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.common.JavaTransitiveInstancesKey; | ||
19 | import tools.refinery.viatra.runtime.matchers.scopes.tables.IIndexTable; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
21 | import 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 | */ | ||
38 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.scopes; | ||
11 | |||
12 | import java.util.Map; | ||
13 | import java.util.Optional; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.AbstractQueryRuntimeContext; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener; | ||
18 | import tools.refinery.viatra.runtime.matchers.scopes.tables.IIndexTable; | ||
19 | import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableContext; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
22 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
24 | import 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 | */ | ||
41 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
20 | import 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 | */ | ||
31 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
10 | |||
11 | import java.util.Map; | ||
12 | import java.util.Optional; | ||
13 | import java.util.stream.Stream; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
24 | import 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 | */ | ||
36 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.List; | ||
14 | import java.util.Objects; | ||
15 | import java.util.Optional; | ||
16 | import java.util.stream.Stream; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
19 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
22 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
25 | import 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 | */ | ||
39 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Optional; | ||
13 | import java.util.stream.Stream; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
22 | import 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 | */ | ||
50 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
21 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
11 | |||
12 | import 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 | */ | ||
24 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | import 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 | */ | ||
24 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
11 | |||
12 | import 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 | */ | ||
25 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Optional; | ||
13 | import java.util.Set; | ||
14 | import java.util.stream.Stream; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
26 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
27 | import 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 | */ | ||
46 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
11 | |||
12 | import java.util.Optional; | ||
13 | import java.util.stream.Stream; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
23 | import 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 | */ | ||
35 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Map; | ||
16 | import java.util.Objects; | ||
17 | import 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 | */ | ||
26 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
18 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
20 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | |||
14 | /** | ||
15 | * Default Tuple implementation, with statically unknown arity. | ||
16 | * @author Gabor Bergmann | ||
17 | */ | ||
18 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
18 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
17 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Map; | ||
13 | import java.util.Set; | ||
14 | |||
15 | /** | ||
16 | * Represents both mutable and immutable tuples | ||
17 | * | ||
18 | * @author Zoltan Ujhelyi | ||
19 | * @since 1.7 | ||
20 | * | ||
21 | */ | ||
22 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | import java.util.Objects; | ||
14 | |||
15 | /** | ||
16 | * | ||
17 | * Tuple that inherits another tuple on the left. | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * | ||
21 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public 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 | |||
10 | package 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 | */ | ||
19 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
11 | |||
12 | /** | ||
13 | * Immutable tuple. Obtain instances via utility class {@link Tuples}. | ||
14 | * @author Gabor Bergmann | ||
15 | * | ||
16 | */ | ||
17 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.Arrays; | ||
14 | import java.util.Collection; | ||
15 | import java.util.HashSet; | ||
16 | import java.util.LinkedList; | ||
17 | import java.util.List; | ||
18 | import java.util.OptionalInt; | ||
19 | import 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 | */ | ||
28 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.List; | ||
13 | |||
14 | /** | ||
15 | * @author Gabor Bergmann | ||
16 | * @since 1.7 | ||
17 | */ | ||
18 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import java.util.Map; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider; | ||
14 | |||
15 | /** | ||
16 | * @author Zoltan Ujhelyi | ||
17 | * @since 1.7 | ||
18 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
17 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import 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 | */ | ||
21 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
10 | |||
11 | import 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 | */ | ||
19 | public 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 | |||
10 | package 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
16 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.util; | ||
11 | |||
12 | /** | ||
13 | * @author Gabor Bergmann | ||
14 | * @since 1.7 | ||
15 | * An instance of clearable pattern memory. | ||
16 | */ | ||
17 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.List; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | import java.util.TreeMap; | ||
16 | import java.util.function.Function; | ||
17 | |||
18 | /** | ||
19 | * Factory class used as an accessor to Collections implementations. | ||
20 | * @author istvanrath | ||
21 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
16 | public 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.util; | ||
11 | |||
12 | import java.util.Iterator; | ||
13 | import java.util.Set; | ||
14 | import java.util.function.BiConsumer; | ||
15 | |||
16 | import 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 | */ | ||
28 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | /** | ||
12 | * @author Gabor Bergmann | ||
13 | * @since 1.7 | ||
14 | */ | ||
15 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.List; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | import java.util.TreeMap; | ||
17 | |||
18 | import org.eclipse.collections.api.map.MutableMap; | ||
19 | import org.eclipse.collections.impl.factory.Maps; | ||
20 | import org.eclipse.collections.impl.factory.Sets; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.ICollectionsFramework; | ||
22 | import 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 | */ | ||
29 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Set; | ||
13 | import java.util.function.BiConsumer; | ||
14 | |||
15 | import 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 | */ | ||
23 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import org.eclipse.collections.api.LongIterable; | ||
16 | import org.eclipse.collections.api.iterator.LongIterator; | ||
17 | import org.eclipse.collections.api.set.primitive.LongSet; | ||
18 | import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; | ||
19 | |||
20 | /** | ||
21 | * @author Gabor Bergmann | ||
22 | * @since 2.0 | ||
23 | */ | ||
24 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import org.eclipse.collections.impl.map.mutable.UnifiedMap; | ||
12 | import org.eclipse.collections.impl.map.mutable.primitive.LongObjectHashMap; | ||
13 | import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedMultiset; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedSet; | ||
15 | |||
16 | import java.util.Set; | ||
17 | import 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 | */ | ||
27 | class 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | /** | ||
12 | * @author Gabor Bergmann | ||
13 | * @since 1.7 | ||
14 | */ | ||
15 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import org.eclipse.collections.impl.set.mutable.UnifiedSet; | ||
14 | |||
15 | /** | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 2.0 | ||
18 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Set; | ||
14 | |||
15 | /** | ||
16 | * A singleton immutable empty memory. | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 2.0 | ||
19 | * | ||
20 | */ | ||
21 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import 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 | */ | ||
23 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
18 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
25 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Map; | ||
13 | import java.util.Map.Entry; | ||
14 | import java.util.Set; | ||
15 | import java.util.function.BiConsumer; | ||
16 | import java.util.stream.Stream; | ||
17 | import 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 | */ | ||
31 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import 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 | */ | ||
24 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.NoSuchElementException; | ||
14 | import java.util.Objects; | ||
15 | import java.util.stream.Stream; | ||
16 | import java.util.stream.StreamSupport; | ||
17 | |||
18 | import 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 | */ | ||
50 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.function.Function; | ||
12 | import java.util.function.Supplier; | ||
13 | |||
14 | import 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import 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 | */ | ||
21 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Map; | ||
13 | import java.util.Map.Entry; | ||
14 | import java.util.Set; | ||
15 | import 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 | */ | ||
23 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
16 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import 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 | */ | ||
27 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.Map; | ||
13 | import 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 | */ | ||
20 | public 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 | *******************************************************************************/ | ||
12 | package tools.refinery.viatra.runtime.matchers.util; | ||
13 | |||
14 | import java.util.ArrayList; | ||
15 | import java.util.List; | ||
16 | import java.util.Set; | ||
17 | import java.util.stream.Collectors; | ||
18 | import 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 | */ | ||
33 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * A piece of data associated with a direction. | ||
15 | * | ||
16 | * @author Tamas Szabo | ||
17 | * @since 2.4 | ||
18 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | /** | ||
12 | * A provider implementation that always returns the same object instance. | ||
13 | * @author Zoltan Ujhelyi | ||
14 | */ | ||
15 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.NoSuchElementException; | ||
14 | import 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 | */ | ||
21 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Map; | ||
13 | import java.util.Map.Entry; | ||
14 | import java.util.NavigableMap; | ||
15 | import java.util.Set; | ||
16 | import java.util.TreeMap; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.resumable.Resumable; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.resumable.UnmaskedResumable; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
24 | import 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 | */ | ||
33 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util.resumable; | ||
10 | |||
11 | import java.util.Map; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package 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 | */ | ||
19 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util.resumable; | ||
10 | |||
11 | import java.util.Map; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import 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 | */ | ||
22 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util.timeline; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.List; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
16 | import 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 | */ | ||
25 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util.timeline; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | |||
13 | import 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 | */ | ||
24 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util.timeline; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
14 | import 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 | */ | ||
23 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util.timeline; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.List; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
16 | import 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 | */ | ||
26 | public 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 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.util.timeline; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | /** | ||
14 | * Utility class for creating {@link Timeline}s. | ||
15 | * @author Tamas Szabo | ||
16 | * @since 2.4 | ||
17 | */ | ||
18 | public 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 | } | ||