1
2
3
4
5
6 package dev.metaschema.core.metapath.cst;
7
8 import org.antlr.v4.runtime.ParserRuleContext;
9 import org.antlr.v4.runtime.tree.ParseTree;
10 import org.antlr.v4.runtime.tree.TerminalNode;
11
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.Objects;
15 import java.util.function.BiFunction;
16 import java.util.function.Function;
17 import java.util.regex.Matcher;
18 import java.util.regex.Pattern;
19
20 import javax.xml.namespace.QName;
21
22 import dev.metaschema.core.metapath.IExpression;
23 import dev.metaschema.core.metapath.StaticContext;
24 import dev.metaschema.core.metapath.StaticMetapathException;
25 import dev.metaschema.core.metapath.antlr.AbstractAstVisitor;
26 import dev.metaschema.core.metapath.antlr.Metapath10;
27 import dev.metaschema.core.util.ObjectUtils;
28 import edu.umd.cs.findbugs.annotations.NonNull;
29 import edu.umd.cs.findbugs.annotations.Nullable;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 @SuppressWarnings({
50 "PMD.CouplingBetweenObjects"
51 })
52 public abstract class AbstractCSTVisitorBase
53 extends AbstractAstVisitor<IExpression> {
54
55 private static final Pattern QUALIFIED_NAME_PATTERN = Pattern.compile("^Q\\{([^}]*)\\}(.+)$");
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 @NonNull
75 static QName toQName(@NonNull Metapath10.EqnameContext eqname, @NonNull StaticContext context,
76 boolean requireNamespace) {
77 String namespaceUri;
78 String localName;
79 TerminalNode node;
80 if ((node = eqname.URIQualifiedName()) != null) {
81
82
83 Matcher matcher = QUALIFIED_NAME_PATTERN.matcher(node.getText());
84 if (!matcher.matches()) {
85
86 throw new IllegalStateException();
87 }
88 namespaceUri = matcher.group(1);
89 localName = matcher.group(2);
90 } else {
91 String prefix;
92 String[] tokens = eqname.getText().split(":", 2);
93 if (tokens.length == 2) {
94
95
96 prefix = ObjectUtils.notNull(tokens[0]);
97 localName = tokens[1];
98 } else {
99
100
101 prefix = "";
102 localName = tokens[0];
103 }
104 namespaceUri = context.lookupNamespaceForPrefix(prefix);
105 if (namespaceUri == null && requireNamespace) {
106 throw new StaticMetapathException(
107 StaticMetapathException.PREFIX_NOT_EXPANDABLE,
108 String.format("The static context does not have a namespace URI configured for prefix '%s'.", prefix));
109 }
110 }
111
112 QName retval;
113 if (namespaceUri == null) {
114 retval = new QName(localName);
115 } else {
116 if ("http://www.w3.org/2000/xmlns/".equals(namespaceUri)) {
117 throw new StaticMetapathException(StaticMetapathException.NAMESPACE_MISUSE,
118 "The namespace of an expanded QName cannot be: http://www.w3.org/2000/xmlns/");
119 }
120 retval = new QName(namespaceUri, localName);
121 }
122 return retval;
123 }
124
125 @SuppressWarnings("null")
126 @Override
127 @NonNull
128 public IExpression visit(ParseTree tree) {
129 assert tree != null;
130 return super.visit(tree);
131 }
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152 @Nullable
153 protected <CONTEXT extends ParserRuleContext, T, R>
154 List<R> nairyToList(
155 @NonNull CONTEXT context,
156 int startIndex,
157 int step,
158 @NonNull BiFunction<CONTEXT, Integer, R> parser) {
159 int numChildren = context.getChildCount();
160
161 List<R> retval = null;
162 if (startIndex < numChildren) {
163 retval = new ArrayList<>((numChildren - startIndex) / step);
164 for (int idx = startIndex; idx < numChildren; idx += step) {
165 R result = parser.apply(context, idx);
166 retval.add(result);
167 }
168 }
169 return retval;
170 }
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193 @Nullable
194 protected <CONTEXT extends ParserRuleContext, T, R>
195 R nairyToCollection(
196 @NonNull CONTEXT context,
197 int startIndex,
198 int step,
199 @NonNull BiFunction<CONTEXT, Integer, T> parser,
200 @NonNull Function<List<T>, R> supplier) {
201 int numChildren = context.getChildCount();
202
203 R retval = null;
204 if (startIndex < numChildren) {
205 List<T> children = new ArrayList<>((numChildren - startIndex) / step);
206 for (int idx = startIndex; idx < numChildren; idx += step) {
207 T result = parser.apply(context, idx);
208 children.add(result);
209 }
210 retval = supplier.apply(children);
211 }
212 return retval;
213 }
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233 @NonNull
234 protected <CONTEXT extends ParserRuleContext> IExpression
235 handleNAiryCollection(
236 @NonNull CONTEXT context,
237 @NonNull Function<List<IExpression>, IExpression> supplier) {
238 return handleNAiryCollection(context, 1, 2, (ctx, idx) -> {
239
240 ParseTree tree = ctx.getChild(idx + 1);
241 return tree.accept(this);
242 }, supplier);
243 }
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272 @NonNull
273 protected <CONTEXT extends ParserRuleContext> IExpression
274 handleNAiryCollection(
275 @NonNull CONTEXT context,
276 int startIndex,
277 int step,
278 @NonNull BiFunction<CONTEXT, Integer, IExpression> parser,
279 @NonNull Function<List<IExpression>, IExpression> supplier) {
280 int numChildren = context.getChildCount();
281
282 if (numChildren == 0) {
283 throw new IllegalStateException("there should always be a child expression");
284 }
285 if (startIndex > numChildren) {
286 throw new IllegalStateException("Start index is out of bounds");
287 }
288
289 ParseTree leftTree = context.getChild(0);
290 @SuppressWarnings({ "null" })
291 @NonNull
292 IExpression leftResult = leftTree.accept(this);
293
294 IExpression retval;
295 if (numChildren == 1) {
296 retval = leftResult;
297 } else {
298 List<IExpression> children = new ArrayList<>(numChildren - 1 / step);
299 children.add(leftResult);
300 for (int i = startIndex; i < numChildren; i = i + step) {
301 IExpression result = parser.apply(context, i);
302 children.add(result);
303 }
304 IExpression result = ObjectUtils.notNull(supplier.apply(children));
305 retval = result;
306 }
307 return retval;
308 }
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336 protected <CONTEXT extends ParserRuleContext> IExpression handleGroupedNAiry(
337 @NonNull CONTEXT context,
338 int startingIndex,
339 int step,
340 @NonNull ITriFunction<CONTEXT, Integer, IExpression, IExpression> parser) {
341 int numChildren = context.getChildCount();
342 if (startingIndex >= numChildren) {
343 throw new IndexOutOfBoundsException(
344 String.format("The starting index '%d' exceeds the child count '%d'",
345 startingIndex,
346 numChildren));
347 }
348
349 IExpression retval = null;
350 if (numChildren > 0) {
351 ParseTree leftTree = context.getChild(startingIndex);
352 retval = ObjectUtils.notNull(leftTree.accept(this));
353
354 for (int i = startingIndex + 1; i < numChildren; i = i + step) {
355 retval = parser.apply(context, i, retval);
356 }
357 }
358 return retval;
359 }
360
361 @FunctionalInterface
362 interface ITriFunction<T, U, V, R> {
363
364 R apply(T argT, U argU, V argV);
365
366 default <W> ITriFunction<T, U, V, W> andThen(Function<? super R, ? extends W> after) {
367 Objects.requireNonNull(after);
368 return (T t, U u, V v) -> after.apply(apply(t, u, v));
369 }
370 }
371 }