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