1
2
3
4
5
6 package gov.nist.secauto.metaschema.core.metapath.function;
7
8 import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
9 import gov.nist.secauto.metaschema.core.metapath.ISequence;
10 import gov.nist.secauto.metaschema.core.metapath.MetapathException;
11 import gov.nist.secauto.metaschema.core.metapath.item.IItem;
12 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
13
14 import java.net.URI;
15 import java.util.ArrayList;
16 import java.util.EnumSet;
17 import java.util.LinkedList;
18 import java.util.List;
19 import java.util.Objects;
20 import java.util.Set;
21 import java.util.stream.Collectors;
22
23 import javax.xml.namespace.QName;
24
25 import edu.umd.cs.findbugs.annotations.NonNull;
26
27
28
29
30 public interface IFunction {
31
32
33
34 enum FunctionProperty {
35
36
37
38
39
40
41
42
43 DETERMINISTIC,
44
45
46
47
48
49
50
51
52
53 CONTEXT_DEPENDENT,
54
55
56
57
58
59
60
61 FOCUS_DEPENDENT,
62
63
64
65 UNBOUNDED_ARITY;
66 }
67
68
69
70
71
72
73 @NonNull
74 default String getName() {
75 return ObjectUtils.notNull(getQName().getLocalPart());
76 }
77
78
79
80
81
82
83 @NonNull
84 QName getQName();
85
86
87
88
89
90
91 @NonNull
92 Set<FunctionProperty> getProperties();
93
94
95
96
97
98
99 @NonNull
100 List<IArgument> getArguments();
101
102
103
104
105
106
107 int arity();
108
109
110
111
112
113
114
115
116 default boolean isDeterministic() {
117 return getProperties().contains(FunctionProperty.DETERMINISTIC);
118 }
119
120
121
122
123
124
125
126
127
128 default boolean isContextDepenent() {
129 return getProperties().contains(FunctionProperty.CONTEXT_DEPENDENT);
130 }
131
132
133
134
135
136
137
138
139 default boolean isFocusDepenent() {
140 return getProperties().contains(FunctionProperty.FOCUS_DEPENDENT);
141 }
142
143
144
145
146
147
148
149
150 default boolean isArityUnbounded() {
151 return getProperties().contains(FunctionProperty.UNBOUNDED_ARITY);
152 }
153
154
155
156
157
158
159 @NonNull
160 ISequenceType getResult();
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 @NonNull
188 ISequence<?> execute(
189 @NonNull List<? extends ISequence<?>> arguments,
190 @NonNull DynamicContext dynamicContext,
191 @NonNull ISequence<?> focus);
192
193
194
195
196
197
198 @NonNull
199 default String toSignature() {
200 return ObjectUtils.notNull(String.format("%s(%s) as %s",
201 getQName(),
202 getArguments().isEmpty() ? ""
203 : getArguments().stream().map(IArgument::toSignature).collect(Collectors.joining(","))
204 + (isArityUnbounded() ? ", ..." : ""),
205 getResult().toSignature()));
206 }
207
208
209
210
211
212
213 @NonNull
214 static Builder builder() {
215 return new Builder();
216 }
217
218
219
220
221 @SuppressWarnings("PMD.LooseCoupling")
222 final class Builder {
223 private String name;
224 private String namespace;
225 @SuppressWarnings("null")
226 @NonNull
227 private final EnumSet<FunctionProperty> properties = EnumSet.noneOf(FunctionProperty.class);
228 @NonNull
229 private final List<IArgument> arguments = new LinkedList<>();
230 @NonNull
231 private Class<? extends IItem> returnType = IItem.class;
232 @NonNull
233 private Occurrence returnOccurrence = Occurrence.ONE;
234 private IFunctionExecutor functionHandler;
235
236 private Builder() {
237
238 }
239
240
241
242
243
244
245
246
247 @NonNull
248 public Builder name(@NonNull String name) {
249 Objects.requireNonNull(name, "name");
250 if (name.isBlank()) {
251 throw new IllegalArgumentException("the name must be non-blank");
252 }
253 this.name = name.trim();
254 return this;
255 }
256
257
258
259
260
261
262
263
264 @NonNull
265 public Builder namespace(@NonNull URI uri) {
266 return namespace(ObjectUtils.notNull(uri.toASCIIString()));
267 }
268
269
270
271
272
273
274
275
276 @NonNull
277 public Builder namespace(@NonNull String name) {
278 Objects.requireNonNull(name, "name");
279 if (name.isBlank()) {
280 throw new IllegalArgumentException("the name must be non-blank");
281 }
282 this.namespace = name.trim();
283 return this;
284 }
285
286
287
288
289
290
291
292 @NonNull
293 public Builder deterministic() {
294 properties.add(FunctionProperty.DETERMINISTIC);
295 return this;
296 }
297
298
299
300
301
302
303
304 @NonNull
305 public Builder nonDeterministic() {
306 properties.remove(FunctionProperty.DETERMINISTIC);
307 return this;
308 }
309
310
311
312
313
314
315
316 @NonNull
317 public Builder contextDependent() {
318 properties.add(FunctionProperty.CONTEXT_DEPENDENT);
319 return this;
320 }
321
322
323
324
325
326
327
328 @NonNull
329 public Builder contextIndependent() {
330 properties.remove(FunctionProperty.CONTEXT_DEPENDENT);
331 return this;
332 }
333
334
335
336
337
338
339
340 @NonNull
341 public Builder focusDependent() {
342 properties.add(FunctionProperty.FOCUS_DEPENDENT);
343 return this;
344 }
345
346
347
348
349
350
351
352 @NonNull
353 public Builder focusIndependent() {
354 properties.remove(FunctionProperty.FOCUS_DEPENDENT);
355 return this;
356 }
357
358
359
360
361
362
363
364
365
366 @NonNull
367 public Builder allowUnboundedArity(boolean allow) {
368 if (allow) {
369 properties.add(FunctionProperty.UNBOUNDED_ARITY);
370 } else {
371 properties.remove(FunctionProperty.UNBOUNDED_ARITY);
372 }
373 return this;
374 }
375
376
377
378
379
380
381
382
383 @NonNull
384 public Builder returnType(@NonNull Class<? extends IItem> type) {
385 Objects.requireNonNull(type, "type");
386 this.returnType = type;
387 return this;
388 }
389
390
391
392
393
394
395 @NonNull
396 public Builder returnZeroOrOne() {
397 return returnOccurrence(Occurrence.ZERO_OR_ONE);
398 }
399
400
401
402
403
404
405 @NonNull
406 public Builder returnOne() {
407 return returnOccurrence(Occurrence.ONE);
408 }
409
410
411
412
413
414
415 @NonNull
416 public Builder returnZeroOrMore() {
417 return returnOccurrence(Occurrence.ZERO_OR_MORE);
418 }
419
420
421
422
423
424
425 @NonNull
426 public Builder returnOneOrMore() {
427 return returnOccurrence(Occurrence.ONE_OR_MORE);
428 }
429
430 @NonNull
431 private Builder returnOccurrence(@NonNull Occurrence occurrence) {
432 Objects.requireNonNull(occurrence, "occurrence");
433 this.returnOccurrence = occurrence;
434 return this;
435 }
436
437
438
439
440
441
442
443
444 @NonNull
445 public Builder argument(@NonNull IArgument.Builder builder) {
446 return argument(builder.build());
447 }
448
449
450
451
452
453
454
455
456 @NonNull
457 public Builder argument(@NonNull IArgument argument) {
458 Objects.requireNonNull(argument, "argument");
459 this.arguments.add(argument);
460 return this;
461 }
462
463
464
465
466
467
468
469
470
471 @NonNull
472 public Builder functionHandler(@NonNull IFunctionExecutor handler) {
473 Objects.requireNonNull(handler, "handler");
474 this.functionHandler = handler;
475 return this;
476 }
477
478
479
480
481
482
483 @NonNull
484 public IFunction build() {
485 if (properties.contains(FunctionProperty.UNBOUNDED_ARITY) && arguments.isEmpty()) {
486 throw new IllegalStateException("to allow unbounded arity, at least one argument must be provided");
487 }
488
489 return new DefaultFunction(
490 ObjectUtils.requireNonNull(name, "the name must not be null"),
491 ObjectUtils.requireNonNull(namespace, "the namespace must not be null"),
492 properties,
493 new ArrayList<>(arguments),
494 ISequenceType.of(returnType, returnOccurrence),
495 ObjectUtils.requireNonNull(functionHandler, "the function handler must not be null"));
496 }
497 }
498 }