1
2
3
4
5
6 package dev.metaschema.core.metapath.cst.items;
7
8 import java.util.ArrayList;
9 import java.util.Collection;
10 import java.util.Collections;
11 import java.util.Iterator;
12 import java.util.LinkedHashMap;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.NoSuchElementException;
16 import java.util.stream.Collectors;
17 import java.util.stream.Stream;
18
19 import dev.metaschema.core.metapath.DynamicContext;
20 import dev.metaschema.core.metapath.IExpression;
21 import dev.metaschema.core.metapath.cst.AbstractExpression;
22 import dev.metaschema.core.metapath.cst.IExpressionVisitor;
23 import dev.metaschema.core.metapath.function.library.FnBoolean;
24 import dev.metaschema.core.metapath.item.IItem;
25 import dev.metaschema.core.metapath.item.ISequence;
26 import dev.metaschema.core.metapath.item.atomic.IBooleanItem;
27 import dev.metaschema.core.qname.IEnhancedQName;
28 import dev.metaschema.core.util.ObjectUtils;
29 import edu.umd.cs.findbugs.annotations.NonNull;
30
31
32
33
34
35
36 public class Quantified
37 extends AbstractExpression {
38
39
40
41 public enum Quantifier {
42
43
44
45
46
47
48
49
50
51 SOME,
52
53
54
55
56
57
58
59
60
61 EVERY;
62 }
63
64 @NonNull
65 private final Quantifier quantifier;
66 @NonNull
67 private final Map<IEnhancedQName, IExpression> inClauses;
68 @NonNull
69 private final IExpression satisfies;
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 public Quantified(
86 @NonNull String text,
87 @NonNull Quantifier quantifier,
88 @NonNull Map<IEnhancedQName, IExpression> inClauses,
89 @NonNull IExpression satisfies) {
90 super(text);
91 this.quantifier = quantifier;
92 this.inClauses = inClauses;
93 this.satisfies = satisfies;
94 }
95
96
97
98
99
100
101 @NonNull
102 public Quantifier getQuantifier() {
103 return quantifier;
104 }
105
106
107
108
109
110
111
112 @NonNull
113 public Map<IEnhancedQName, IExpression> getInClauses() {
114 return inClauses;
115 }
116
117
118
119
120
121
122
123 @NonNull
124 public IExpression getSatisfies() {
125 return satisfies;
126 }
127
128 @Override
129 public List<? extends IExpression> getChildren() {
130 return ObjectUtils.notNull(Stream.concat(inClauses.values().stream(), Stream.of(satisfies))
131 .collect(Collectors.toList()));
132 }
133
134 @Override
135 protected ISequence<IBooleanItem> evaluate(DynamicContext dynamicContext, ISequence<?> focus) {
136 Map<IEnhancedQName, ISequence<? extends IItem>> clauses = getInClauses().entrySet().stream()
137 .map(entry -> Map.entry(
138 entry.getKey(),
139 entry.getValue().accept(dynamicContext, focus)))
140 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
141
142 List<IEnhancedQName> clauseKeys = new ArrayList<>(clauses.keySet());
143 List<? extends Collection<? extends IItem>> clauseValues = new ArrayList<>(clauses.values());
144
145 boolean retval = true;
146 for (List<IItem> product : new CartesianProduct<>(clauseValues)) {
147 DynamicContext subDynamicContext = dynamicContext.subContext();
148 for (int idx = 0; idx < product.size(); idx++) {
149 IEnhancedQName var = clauseKeys.get(idx);
150 IItem item = product.get(idx);
151
152 assert var != null;
153
154 subDynamicContext.bindVariableValue(var, ISequence.of(item));
155 }
156 boolean result = FnBoolean.fnBooleanAsPrimitive(getSatisfies().accept(subDynamicContext, focus));
157 if (quantifier == Quantifier.EVERY && !result) {
158
159 retval = false;
160 break;
161 }
162 if (quantifier == Quantifier.SOME) {
163 if (result) {
164
165 retval = true;
166 break;
167 }
168
169 retval = false;
170 }
171 }
172
173 return ISequence.of(IBooleanItem.valueOf(retval));
174 }
175
176 @Override
177 public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
178 return visitor.visitQuantified(this, context);
179 }
180
181
182
183
184
185
186
187
188
189
190 public static <T extends IItem> Iterable<List<T>> cartesianProduct(
191 @NonNull List<? extends Collection<? extends T>> axes) {
192 return new CartesianProduct<>(axes);
193 }
194
195
196 private static class CartesianProductIterator<T extends IItem> implements Iterator<List<T>> {
197 private final Object[][] dimensions;
198 private final int length;
199 private final int[] indizes;
200 private boolean reachedMax;
201
202 @SuppressWarnings({
203 "PMD.UseVarargs",
204 "PMD.ArrayIsStoredDirectly"
205 })
206 CartesianProductIterator(final Object[][] dimensions) {
207 this.dimensions = dimensions;
208 this.length = dimensions.length;
209 this.indizes = new int[length];
210 }
211
212 private void increment(final int index) {
213 if (index >= length) {
214 reachedMax = true;
215 } else {
216 indizes[index]++;
217 if (indizes[index] == dimensions[index].length) {
218 indizes[index] = 0;
219 increment(index + 1);
220 }
221 }
222 }
223
224 private void increment() {
225 increment(0);
226 }
227
228 @SuppressWarnings("unchecked")
229 @Override
230 public List<T> next() {
231 if (reachedMax) {
232 throw new NoSuchElementException();
233 }
234
235 List<T> list = new ArrayList<>();
236 for (int i = 0; i < length; i++) {
237 list.add((T) dimensions[i][indizes[i]]);
238 }
239
240 increment();
241
242 return Collections.unmodifiableList(list);
243 }
244
245 @Override
246 public boolean hasNext() {
247 return !reachedMax;
248 }
249
250 @Override
251 public void remove() {
252 throw new UnsupportedOperationException("remove not supported");
253 }
254 }
255
256
257 private static final class CartesianProduct<T extends IItem> implements Iterable<List<T>> {
258 private final Object[][] dimensions;
259 private final long size;
260
261 private CartesianProduct(final List<? extends Collection<? extends T>> axes) {
262 Object[][] dimensions = new Object[axes.size()][];
263 long size = dimensions.length == 0 ? 0 : 1;
264 for (int i = 0; i < axes.size(); i++) {
265 dimensions[i] = axes.get(i).toArray();
266 size *= dimensions[i].length;
267 }
268 this.dimensions = dimensions;
269 this.size = size;
270 }
271
272 @Override
273 public Iterator<List<T>> iterator() {
274 if (size == 0) {
275 return Collections.emptyListIterator();
276 }
277 return new CartesianProductIterator<>(dimensions);
278 }
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293 }
294 }