diff options
Diffstat (limited to 'subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java')
-rw-r--r-- | subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java new file mode 100644 index 00000000..e31d7b5c --- /dev/null +++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java | |||
@@ -0,0 +1,301 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, Marton Bur, Istvan Rath and Daniel Varro | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.localsearch.matcher; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.LinkedList; | ||
15 | import java.util.List; | ||
16 | import java.util.NoSuchElementException; | ||
17 | import java.util.Objects; | ||
18 | import java.util.Optional; | ||
19 | import java.util.Set; | ||
20 | import java.util.Spliterator; | ||
21 | import java.util.Spliterators; | ||
22 | import java.util.stream.Collectors; | ||
23 | import java.util.stream.Stream; | ||
24 | import java.util.stream.StreamSupport; | ||
25 | |||
26 | import tools.refinery.viatra.runtime.localsearch.MatchingFrame; | ||
27 | import tools.refinery.viatra.runtime.localsearch.plan.IPlanDescriptor; | ||
28 | import tools.refinery.viatra.runtime.localsearch.plan.SearchPlan; | ||
29 | import tools.refinery.viatra.runtime.localsearch.plan.SearchPlanExecutor; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
31 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
32 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
33 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
34 | import tools.refinery.viatra.runtime.matchers.tuple.VolatileModifiableMaskedTuple; | ||
35 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
36 | |||
37 | /** | ||
38 | * @author Zoltan Ujhelyi | ||
39 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
40 | */ | ||
41 | public final class LocalSearchMatcher implements ILocalSearchAdaptable { | ||
42 | |||
43 | private final List<SearchPlanExecutor> plan; | ||
44 | private final IPlanDescriptor planDescriptor; | ||
45 | private final List<ILocalSearchAdapter> adapters; | ||
46 | |||
47 | /** | ||
48 | * @since 2.0 | ||
49 | */ | ||
50 | public List<SearchPlanExecutor> getPlan() { | ||
51 | return plan; | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public List<ILocalSearchAdapter> getAdapters() { | ||
56 | return new ArrayList<>(adapters); | ||
57 | } | ||
58 | |||
59 | private abstract class PlanExecutionIterator implements Iterator<Tuple> { | ||
60 | |||
61 | protected final Iterator<SearchPlanExecutor> planIterator; | ||
62 | |||
63 | protected SearchPlanExecutor currentPlan; | ||
64 | protected MatchingFrame frame; | ||
65 | protected final Set<ITuple> matchSet; | ||
66 | protected VolatileModifiableMaskedTuple parametersOfFrameView; | ||
67 | private boolean isNextMatchCalculated; | ||
68 | |||
69 | public PlanExecutionIterator(final Iterator<SearchPlanExecutor> planIterator) { | ||
70 | this.planIterator = planIterator; | ||
71 | isNextMatchCalculated = false; | ||
72 | matchSet = new HashSet<>(); | ||
73 | } | ||
74 | |||
75 | protected boolean selectNextPlan() { | ||
76 | if(currentPlan != null) { | ||
77 | currentPlan.removeAdapters(adapters); | ||
78 | } | ||
79 | boolean validPlanSelected = false; | ||
80 | |||
81 | SearchPlanExecutor nextPlan = null; | ||
82 | |||
83 | while (!validPlanSelected && planIterator.hasNext()) { | ||
84 | nextPlan = planIterator.next(); | ||
85 | nextPlan.addAdapters(adapters); | ||
86 | nextPlan.resetPlan(); | ||
87 | |||
88 | validPlanSelected = initializeMatchingFrame(nextPlan); | ||
89 | } | ||
90 | |||
91 | if (validPlanSelected) { | ||
92 | for (ILocalSearchAdapter adapter : adapters) { | ||
93 | adapter.planChanged(Optional.ofNullable(currentPlan).map(SearchPlanExecutor::getSearchPlan), | ||
94 | Optional.ofNullable(nextPlan).map(SearchPlanExecutor::getSearchPlan)); | ||
95 | } | ||
96 | currentPlan = nextPlan; | ||
97 | return true; | ||
98 | } else { | ||
99 | currentPlan = null; | ||
100 | return false; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | protected abstract boolean initializeMatchingFrame(SearchPlanExecutor nextPlan); | ||
105 | |||
106 | private boolean findNextNewMatchInCurrentPlan() { | ||
107 | boolean foundMatch = currentPlan.execute(frame); | ||
108 | while (foundMatch && matchSet.contains(parametersOfFrameView)) { | ||
109 | for (ILocalSearchAdapter adapter : adapters) { | ||
110 | adapter.duplicateMatchFound(frame); | ||
111 | } | ||
112 | foundMatch = currentPlan.execute(frame); | ||
113 | } | ||
114 | return foundMatch; | ||
115 | } | ||
116 | |||
117 | @Override | ||
118 | public boolean hasNext() { | ||
119 | if (isNextMatchCalculated) { | ||
120 | return true; | ||
121 | } | ||
122 | if (currentPlan == null) { | ||
123 | return false; | ||
124 | } | ||
125 | boolean foundMatch = findNextNewMatchInCurrentPlan(); | ||
126 | |||
127 | while (!foundMatch && planIterator.hasNext()) { | ||
128 | foundMatch = selectNextPlan() && findNextNewMatchInCurrentPlan(); | ||
129 | } | ||
130 | if (!foundMatch) { | ||
131 | for (ILocalSearchAdapter adapter : adapters) { | ||
132 | adapter.noMoreMatchesAvailable(LocalSearchMatcher.this); | ||
133 | } | ||
134 | } | ||
135 | isNextMatchCalculated = foundMatch; | ||
136 | return foundMatch; | ||
137 | } | ||
138 | |||
139 | @Override | ||
140 | public Tuple next() { | ||
141 | if (!hasNext()) { | ||
142 | throw new NoSuchElementException("No more matches available."); | ||
143 | } | ||
144 | isNextMatchCalculated = false; | ||
145 | final Tuple match = parametersOfFrameView.toImmutable(); | ||
146 | matchSet.add(match); | ||
147 | return match; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | private class PlanExecutionIteratorWithArrayParameters extends PlanExecutionIterator { | ||
152 | |||
153 | private final Object[] parameterValues; | ||
154 | |||
155 | public PlanExecutionIteratorWithArrayParameters(Iterator<SearchPlanExecutor> planIterator, final Object[] parameterValues) { | ||
156 | super(planIterator); | ||
157 | this.parameterValues = parameterValues; | ||
158 | selectNextPlan(); | ||
159 | } | ||
160 | |||
161 | protected boolean initializeMatchingFrame(SearchPlanExecutor nextPlan) { | ||
162 | frame = new MatchingFrame(nextPlan.getVariableMapping().size()); | ||
163 | parametersOfFrameView = new VolatileModifiableMaskedTuple(frame, nextPlan.getParameterMask()); | ||
164 | for (int i = 0; i < parameterValues.length; i++) { | ||
165 | Object valueToSet = parameterValues[i]; | ||
166 | if (valueToSet != null) { | ||
167 | Object oldValue = parametersOfFrameView.get(i); | ||
168 | if (oldValue == null) { | ||
169 | parametersOfFrameView.set(i, valueToSet); | ||
170 | } else if (!Objects.equals(valueToSet, oldValue)) { | ||
171 | // Initial value setting resulted in contradictory values. This can happen because two parameter | ||
172 | // variables have been unified but the call provides different values for the parameters. | ||
173 | return false; | ||
174 | } | ||
175 | // If oldValue is not null but equal to newValue, the setting can be ignored | ||
176 | } | ||
177 | } | ||
178 | |||
179 | return true; | ||
180 | } | ||
181 | } | ||
182 | private class PlanExecutionIteratorWithTupleParameters extends PlanExecutionIterator { | ||
183 | |||
184 | private final ITuple parameterValues; | ||
185 | private final TupleMask parameterSeedMask; | ||
186 | |||
187 | public PlanExecutionIteratorWithTupleParameters(Iterator<SearchPlanExecutor> planIterator, final TupleMask parameterSeedMask, final ITuple parameterValues) { | ||
188 | super(planIterator); | ||
189 | this.parameterSeedMask = parameterSeedMask; | ||
190 | this.parameterValues = parameterValues; | ||
191 | selectNextPlan(); | ||
192 | } | ||
193 | |||
194 | protected boolean initializeMatchingFrame(SearchPlanExecutor nextPlan) { | ||
195 | frame = new MatchingFrame(nextPlan.getVariableMapping().size()); | ||
196 | parametersOfFrameView = new VolatileModifiableMaskedTuple(frame, nextPlan.getParameterMask()); | ||
197 | for (int i = 0; i < parameterSeedMask.getSize(); i++) { | ||
198 | int index = parameterSeedMask.indices[i]; | ||
199 | Object valueToSet = parameterValues.get(i); | ||
200 | if (valueToSet != null) { | ||
201 | Object oldValue = parametersOfFrameView.get(index); | ||
202 | if (oldValue == null) { | ||
203 | parametersOfFrameView.set(index, valueToSet); | ||
204 | } else if (!Objects.equals(valueToSet, oldValue)) { | ||
205 | // Initial value setting resulted in contradictory values. This can happen because two parameter | ||
206 | // variables have been unified but the call provides different values for the parameters. | ||
207 | return false; | ||
208 | } | ||
209 | // If oldValue is not null but equal to newValue, the setting can be ignored | ||
210 | } | ||
211 | } | ||
212 | |||
213 | return true; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * @since 2.0 | ||
219 | */ | ||
220 | public LocalSearchMatcher(ISearchContext searchContext, IPlanDescriptor planDescriptor, List<SearchPlan> plan) { | ||
221 | Preconditions.checkArgument(planDescriptor != null, "Cannot initialize matcher with null query."); | ||
222 | this.planDescriptor = planDescriptor; | ||
223 | this.plan = plan.stream().map(p -> new SearchPlanExecutor(p, searchContext)).collect(Collectors.toList()); | ||
224 | this.adapters = new LinkedList<>(); | ||
225 | } | ||
226 | |||
227 | @Override | ||
228 | public void addAdapter(ILocalSearchAdapter adapter) { | ||
229 | this.adapters.add(adapter); | ||
230 | adapter.adapterRegistered(this); | ||
231 | } | ||
232 | |||
233 | @Override | ||
234 | public void removeAdapter(ILocalSearchAdapter adapter) { | ||
235 | this.adapters.remove(adapter); | ||
236 | adapter.adapterUnregistered(this); | ||
237 | } | ||
238 | |||
239 | @Override | ||
240 | public void addAdapters(List<ILocalSearchAdapter> adapters) { | ||
241 | this.adapters.addAll(adapters); | ||
242 | for (ILocalSearchAdapter adapter : adapters) { | ||
243 | adapter.adapterRegistered(this); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | @Override | ||
248 | public void removeAdapters(List<ILocalSearchAdapter> adapters) { | ||
249 | this.adapters.removeAll(adapters); | ||
250 | for (ILocalSearchAdapter adapter : adapters) { | ||
251 | adapter.adapterUnregistered(this); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | public int getParameterCount() { | ||
256 | return planDescriptor.getQuery().getParameters().size(); | ||
257 | } | ||
258 | |||
259 | private void matchingStarted() { | ||
260 | for (ILocalSearchAdapter adapter : adapters) { | ||
261 | adapter.patternMatchingStarted(this); | ||
262 | } | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * @since 2.0 | ||
267 | */ | ||
268 | public Stream<Tuple> streamMatches(final Object[] parameterValues) { | ||
269 | matchingStarted(); | ||
270 | PlanExecutionIterator it = new PlanExecutionIteratorWithArrayParameters(plan.iterator(), parameterValues); | ||
271 | return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, | ||
272 | Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.DISTINCT), false); | ||
273 | } | ||
274 | |||
275 | /** | ||
276 | * @since 2.0 | ||
277 | */ | ||
278 | public Stream<Tuple> streamMatches(TupleMask parameterSeedMask, final ITuple parameterValues) { | ||
279 | matchingStarted(); | ||
280 | PlanExecutionIterator it = new PlanExecutionIteratorWithTupleParameters( | ||
281 | plan.iterator(), parameterSeedMask, parameterValues); | ||
282 | return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, | ||
283 | Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.DISTINCT), false); | ||
284 | } | ||
285 | |||
286 | /** | ||
287 | * Returns the query specification this matcher used as source for the implementation | ||
288 | * @return never null | ||
289 | */ | ||
290 | public PQuery getQuerySpecification() { | ||
291 | return planDescriptor.getQuery(); | ||
292 | } | ||
293 | |||
294 | |||
295 | /** | ||
296 | * @since 1.5 | ||
297 | */ | ||
298 | public IPlanDescriptor getPlanDescriptor() { | ||
299 | return planDescriptor; | ||
300 | } | ||
301 | } | ||