aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/interpreter-rete-recipes/src/main/java
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-09-16 13:19:31 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-09-16 16:53:01 +0200
commit97b0c4c1192fe5580a7957c844acc8092b56c604 (patch)
treebea3cdf9aaeb5da2864fcf87780d356661af8f63 /subprojects/interpreter-rete-recipes/src/main/java
parentbuild: fix Sonar quality gate issues (diff)
downloadrefinery-97b0c4c1192fe5580a7957c844acc8092b56c604.tar.gz
refinery-97b0c4c1192fe5580a7957c844acc8092b56c604.tar.zst
refinery-97b0c4c1192fe5580a7957c844acc8092b56c604.zip
chore: remove VIATRA branding
Rename VIATRA subprojects to Refinery Interpreter to avoid interfering with Eclipse Foundation trademarks. Uses refering to a specific (historical) version of VIATRA were kept to avoid ambiguity.
Diffstat (limited to 'subprojects/interpreter-rete-recipes/src/main/java')
-rw-r--r--subprojects/interpreter-rete-recipes/src/main/java/tools/refinery/interpreter/rete/recipes/GenerateReteRecipes.mwe225
-rw-r--r--subprojects/interpreter-rete-recipes/src/main/java/tools/refinery/interpreter/rete/recipes/helper/RecipeRecognizer.java204
-rw-r--r--subprojects/interpreter-rete-recipes/src/main/java/tools/refinery/interpreter/rete/recipes/helper/RecipesHelper.java81
3 files changed, 310 insertions, 0 deletions
diff --git a/subprojects/interpreter-rete-recipes/src/main/java/tools/refinery/interpreter/rete/recipes/GenerateReteRecipes.mwe2 b/subprojects/interpreter-rete-recipes/src/main/java/tools/refinery/interpreter/rete/recipes/GenerateReteRecipes.mwe2
new file mode 100644
index 00000000..324d93a5
--- /dev/null
+++ b/subprojects/interpreter-rete-recipes/src/main/java/tools/refinery/interpreter/rete/recipes/GenerateReteRecipes.mwe2
@@ -0,0 +1,25 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6module tools.refinery.viatra.runtime.rete.recipes.GenerateReteRecipes
7
8Workflow {
9 bean = org.eclipse.emf.mwe.utils.StandaloneSetup {
10 projectMapping = {
11 projectName = "tools.refinery.refinery-interpreter-rete-recipes"
12 path = "."
13 }
14 }
15
16 component = org.eclipse.emf.mwe.utils.DirectoryCleaner {
17 directory = "src/main/emf-gen"
18 }
19
20 component = org.eclipse.emf.mwe2.ecore.EcoreGenerator {
21 generateCustomClasses = false
22 genModel = "platform:/resource/tools.refinery.refinery-interpreter-rete-recipes/src/main/resources/model/rete-recipes.genmodel"
23 srcPath = "platform:/resource/tools.refinery.refinery-interpreter-rete-recipes/src/main/emf-gen"
24 }
25}
diff --git a/subprojects/interpreter-rete-recipes/src/main/java/tools/refinery/interpreter/rete/recipes/helper/RecipeRecognizer.java b/subprojects/interpreter-rete-recipes/src/main/java/tools/refinery/interpreter/rete/recipes/helper/RecipeRecognizer.java
new file mode 100644
index 00000000..ed551b6e
--- /dev/null
+++ b/subprojects/interpreter-rete-recipes/src/main/java/tools/refinery/interpreter/rete/recipes/helper/RecipeRecognizer.java
@@ -0,0 +1,204 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Gabor Bergmann, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.interpreter.rete.recipes.helper;
10
11import org.eclipse.emf.common.util.EList;
12import org.eclipse.emf.ecore.EAttribute;
13import org.eclipse.emf.ecore.EClass;
14import org.eclipse.emf.ecore.EObject;
15import org.eclipse.emf.ecore.EStructuralFeature;
16import org.eclipse.emf.ecore.util.EcoreUtil;
17import tools.refinery.interpreter.rete.recipes.RecipesPackage;
18import tools.refinery.interpreter.rete.recipes.ReteNodeRecipe;
19import tools.refinery.interpreter.matchers.context.IQueryRuntimeContext;
20
21import java.util.*;
22
23/**
24 * Stores a set of known <em>canonical</em> recipes, each representing a disjoint equivalence class of recipes, modulo
25 * {@link #isEquivalentRecipe(ReteNodeRecipe, ReteNodeRecipe)}.
26 *
27 * @author Gabor Bergmann
28 * @since 1.3
29 *
30 */
31public class RecipeRecognizer {
32 private static long nextRecipeEquivalenceClassID = 0;
33
34 /**
35 * if EcoreUtil.equals(recipe1, recipe2), only one of them will be included here
36 */
37 Map<EClass, Set<ReteNodeRecipe>> canonicalRecipesByClass = new HashMap<>();
38 Map<Long, ReteNodeRecipe> canonicalRecipeByEquivalenceClassID = new HashMap<>();
39
40 private IQueryRuntimeContext runtimeContext;
41
42 /**
43 * @param can be null; if provided, further equivalences can be detected based on {@link IQueryRuntimeContext#wrapElement(Object)}
44 * @since 1.6
45 */
46 public RecipeRecognizer(IQueryRuntimeContext runtimeContext) {
47 this.runtimeContext = runtimeContext;
48 }
49 public RecipeRecognizer() {
50 this(null);
51 }
52
53 /**
54 * Recognizes when an equivalent canonical recipe is already known.
55 *
56 * @return an equivalent canonical recipe, or the null if no known equivalent found
57 */
58 public ReteNodeRecipe peekCanonicalRecipe(final ReteNodeRecipe recipe) {
59 // equivalence class already known
60 for (Long classID : recipe.getEquivalenceClassIDs()) {
61 ReteNodeRecipe knownRecipe = canonicalRecipeByEquivalenceClassID.get(classID);
62 if (knownRecipe != null)
63 return knownRecipe;
64 }
65
66 // equivalence class not known, but maybe equivalent recipe still
67 // available
68 Collection<ReteNodeRecipe> sameClassRecipes = getSameClassCanonicalRecipes(recipe);
69 for (ReteNodeRecipe knownRecipe : sameClassRecipes) {
70 if (isEquivalentRecipe(recipe, knownRecipe)) {
71 // FOUND EQUIVALENT RECIPE
72 recipe.getEquivalenceClassIDs().add(knownRecipe.getEquivalenceClassIDs().get(0));
73 return knownRecipe;
74 }
75 }
76
77 return null;
78 }
79
80 /**
81 * This recipe will be remembered as a canonical recipe. Method maintains both internal data structures and the
82 * equivalence class attribute of the recipe. PRECONDITION: {@link #peekCanonicalRecipe(ReteNodeRecipe)} must return
83 * null or the recipe itself
84 */
85 public void makeCanonical(final ReteNodeRecipe recipe) {
86 // this is a canonical recipe, chosen representative of its new
87 // equivalence class
88 if (recipe.getEquivalenceClassIDs().isEmpty()) {
89 recipe.getEquivalenceClassIDs().add(nextRecipeEquivalenceClassID++);
90 }
91 for (Long classID : recipe.getEquivalenceClassIDs()) {
92 canonicalRecipeByEquivalenceClassID.put(classID, recipe);
93 }
94 getSameClassCanonicalRecipes(recipe).add(recipe);
95 }
96
97 /**
98 * Ensures that there is an equivalent canonical recipe; if none is known yet, this recipe will be remembered as
99 * canonical.
100 *
101 * @return an equivalent canonical recipe; the argument recipe itself (which is made canonical) if no known
102 * equivalent found
103 */
104 public ReteNodeRecipe canonicalizeRecipe(final ReteNodeRecipe recipe) {
105 ReteNodeRecipe knownRecipe = peekCanonicalRecipe(recipe);
106 if (knownRecipe == null) {
107 knownRecipe = recipe;
108 makeCanonical(recipe);
109 }
110 return knownRecipe;
111 }
112
113 /**
114 * @return true iff recipe is a canonical recipe
115 */
116 public boolean isKnownCanonicalRecipe(final ReteNodeRecipe recipe) {
117 if (recipe.getEquivalenceClassIDs().isEmpty()) {
118 return false;
119 }
120 ReteNodeRecipe knownRecipe = canonicalRecipeByEquivalenceClassID.get(recipe.getEquivalenceClassIDs().get(0));
121 return recipe == knownRecipe;
122 }
123
124 private Set<ReteNodeRecipe> getSameClassCanonicalRecipes(final ReteNodeRecipe recipe) {
125 Set<ReteNodeRecipe> sameClassRecipes = canonicalRecipesByClass.get(recipe.eClass());
126 if (sameClassRecipes == null) {
127 sameClassRecipes = new HashSet<>();
128 canonicalRecipesByClass.put(recipe.eClass(), sameClassRecipes);
129 }
130 return sameClassRecipes;
131 }
132
133 private boolean isEquivalentRecipe(ReteNodeRecipe recipe, ReteNodeRecipe knownRecipe) {
134 return new EqualityHelper(runtimeContext).equals(recipe, knownRecipe);
135 }
136
137 // TODO reuse in more cases later, e.g. switching join node parents, etc.
138 private static class EqualityHelper extends EcoreUtil.EqualityHelper {
139
140
141
142
143 private static final long serialVersionUID = -8841971394686015188L;
144
145 private static final EAttribute RETE_NODE_RECIPE_EQUIVALENCE_CLASS_IDS =
146 RecipesPackage.eINSTANCE.getReteNodeRecipe_EquivalenceClassIDs();
147 private static final EAttribute CONSTANT_RECIPE_CONSTANT_VALUES =
148 RecipesPackage.eINSTANCE.getConstantRecipe_ConstantValues();
149 private static final EAttribute DISCRIMINATOR_BUCKET_RECIPE_BUCKET_KEY =
150 RecipesPackage.eINSTANCE.getDiscriminatorBucketRecipe_BucketKey();
151
152 private IQueryRuntimeContext runtimeContext;
153
154 public EqualityHelper(IQueryRuntimeContext runtimeContext) {
155 this.runtimeContext = runtimeContext;
156 }
157
158 @Override
159 protected boolean haveEqualFeature(EObject eObject1, EObject eObject2, EStructuralFeature feature) {
160 // ignore differences in this attribute, as it may only be assigned
161 // after the equivalence check
162 if (RETE_NODE_RECIPE_EQUIVALENCE_CLASS_IDS.equals(feature))
163 return true;
164
165 if (runtimeContext != null) {
166 // constant values
167 if (/*CONSTANT_RECIPE_CONSTANT_VALUES.equals(feature) ||*/ DISCRIMINATOR_BUCKET_RECIPE_BUCKET_KEY.equals(feature)) {
168 // use runtime context to map to canonical wrapped form
169 // this is costly for constant recipes (TODO improve this), but essential for discriminator buckets
170
171 Object val1 = eObject1.eGet(feature);
172 Object val2 = eObject2.eGet(feature);
173
174 if (val1 != null && val2 != null) {
175 return runtimeContext.wrapElement(val1).equals(runtimeContext.wrapElement(val2));
176 } else {
177 return val1 == null && val2 == null;
178 }
179
180 }
181 }
182
183 // fallback to general comparison
184 return super.haveEqualFeature(eObject1, eObject2, feature);
185 }
186
187 @Override
188 public boolean equals(EObject eObject1, EObject eObject2) {
189 // short-circuit if already known to be equivalent
190 if (eObject1 instanceof ReteNodeRecipe) {
191 if (eObject2 instanceof ReteNodeRecipe) {
192 EList<Long> eqClassIDs1 = ((ReteNodeRecipe) eObject1).getEquivalenceClassIDs();
193 EList<Long> eqClassIDs2 = ((ReteNodeRecipe) eObject2).getEquivalenceClassIDs();
194
195 if (!Collections.disjoint(eqClassIDs1, eqClassIDs2))
196 return true;
197 }
198 }
199
200 // fallback to general comparison
201 return super.equals(eObject1, eObject2);
202 }
203 }
204}
diff --git a/subprojects/interpreter-rete-recipes/src/main/java/tools/refinery/interpreter/rete/recipes/helper/RecipesHelper.java b/subprojects/interpreter-rete-recipes/src/main/java/tools/refinery/interpreter/rete/recipes/helper/RecipesHelper.java
new file mode 100644
index 00000000..763989ea
--- /dev/null
+++ b/subprojects/interpreter-rete-recipes/src/main/java/tools/refinery/interpreter/rete/recipes/helper/RecipesHelper.java
@@ -0,0 +1,81 @@
1/**
2 * Copyright (c) 2004-2014 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 */
9package tools.refinery.interpreter.rete.recipes.helper;
10
11import org.eclipse.emf.common.util.EList;
12import tools.refinery.interpreter.rete.recipes.*;
13
14import java.util.Collection;
15
16/**
17 * Static helper class for easy construction of recipes.
18 *
19 * @author Bergmann Gabor
20 */
21public class RecipesHelper {
22 private static final RecipesFactory FACTORY = RecipesFactory.eINSTANCE;
23
24 private RecipesHelper() {/* Utility class constructor */}
25
26 /**
27 * @since 2.0
28 */
29 public static Mask mask(final int sourceArity, final Collection<Integer> sourceIndices) {
30 Mask mask = RecipesHelper.FACTORY.createMask();
31 mask.setSourceArity(sourceArity);
32 mask.getSourceIndices().addAll(sourceIndices);
33 return mask;
34 }
35
36 public static Mask mask(final int sourceArity, final int... sourceIndices) {
37 Mask mask = RecipesHelper.FACTORY.createMask();
38 mask.setSourceArity(sourceArity);
39 final EList<Integer> maskIndeces = mask.getSourceIndices();
40 for (int index : sourceIndices) {
41 maskIndeces.add(index);
42 }
43 return mask;
44 }
45
46 public static ProjectionIndexerRecipe projectionIndexerRecipe(final ReteNodeRecipe parent, final Mask mask) {
47 ProjectionIndexerRecipe recipe = RecipesHelper.FACTORY.createProjectionIndexerRecipe();
48 recipe.setParent(parent);
49 recipe.setMask(mask);
50 return recipe;
51 }
52
53 public static ExpressionDefinition expressionDefinition(final Object evaluator) {
54 ExpressionDefinition definition = RecipesHelper.FACTORY.createExpressionDefinition();
55 definition.setEvaluator(evaluator);
56 return definition;
57 }
58
59 public static InputRecipe inputRecipe(final Object inputKey, final String inputKeyID, final int arity) {
60 InputRecipe recipe = RecipesHelper.FACTORY.createInputRecipe();
61 recipe.setInputKey(inputKey);
62 recipe.setKeyArity(arity);
63 recipe.setKeyID(inputKeyID);
64 recipe.setTraceInfo(inputKeyID);
65 return recipe;
66 }
67
68 /**
69 * Mask can be null in case no tuple reordering or trimming is needed
70 */
71 public static InputFilterRecipe inputFilterRecipe(final ReteNodeRecipe parent, final Object inputKey,
72 final String inputKeyID, final Mask mask) {
73 InputFilterRecipe it = RecipesHelper.FACTORY.createInputFilterRecipe();
74 it.setParent(parent);
75 it.setInputKey(inputKey);
76 it.setKeyID(inputKeyID);
77 it.setTraceInfo(inputKeyID);
78 it.setMask(mask);
79 return it;
80 }
81}