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