diff options
author | Kristóf Marussy <kristof@marussy.com> | 2023-09-16 13:19:31 +0200 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2023-09-16 16:53:01 +0200 |
commit | 97b0c4c1192fe5580a7957c844acc8092b56c604 (patch) | |
tree | bea3cdf9aaeb5da2864fcf87780d356661af8f63 /subprojects/interpreter-rete-recipes/src/main/java | |
parent | build: fix Sonar quality gate issues (diff) | |
download | refinery-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')
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 | */ | ||
6 | module tools.refinery.viatra.runtime.rete.recipes.GenerateReteRecipes | ||
7 | |||
8 | Workflow { | ||
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 | *******************************************************************************/ | ||
9 | package tools.refinery.interpreter.rete.recipes.helper; | ||
10 | |||
11 | import org.eclipse.emf.common.util.EList; | ||
12 | import org.eclipse.emf.ecore.EAttribute; | ||
13 | import org.eclipse.emf.ecore.EClass; | ||
14 | import org.eclipse.emf.ecore.EObject; | ||
15 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
16 | import org.eclipse.emf.ecore.util.EcoreUtil; | ||
17 | import tools.refinery.interpreter.rete.recipes.RecipesPackage; | ||
18 | import tools.refinery.interpreter.rete.recipes.ReteNodeRecipe; | ||
19 | import tools.refinery.interpreter.matchers.context.IQueryRuntimeContext; | ||
20 | |||
21 | import 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 | */ | ||
31 | public 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 | */ | ||
9 | package tools.refinery.interpreter.rete.recipes.helper; | ||
10 | |||
11 | import org.eclipse.emf.common.util.EList; | ||
12 | import tools.refinery.interpreter.rete.recipes.*; | ||
13 | |||
14 | import java.util.Collection; | ||
15 | |||
16 | /** | ||
17 | * Static helper class for easy construction of recipes. | ||
18 | * | ||
19 | * @author Bergmann Gabor | ||
20 | */ | ||
21 | public 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 | } | ||