1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.metaschema.core.metapath.function;
28
29 import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
30 import gov.nist.secauto.metaschema.core.metapath.ISequence;
31 import gov.nist.secauto.metaschema.core.metapath.MetapathException;
32 import gov.nist.secauto.metaschema.core.metapath.item.IItem;
33 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
34
35 import java.net.URI;
36 import java.util.ArrayList;
37 import java.util.EnumSet;
38 import java.util.LinkedList;
39 import java.util.List;
40 import java.util.Objects;
41 import java.util.Set;
42
43 import javax.xml.namespace.QName;
44
45 import edu.umd.cs.findbugs.annotations.NonNull;
46
47 public interface IFunction {
48 enum FunctionProperty {
49
50
51
52
53
54
55
56
57 DETERMINISTIC,
58
59
60
61
62
63
64
65
66
67 CONTEXT_DEPENDENT,
68
69
70
71
72
73
74
75 FOCUS_DEPENDENT,
76
77
78
79 UNBOUNDED_ARITY;
80 }
81
82
83
84
85
86
87 @NonNull
88 String getName();
89
90
91
92
93
94
95 @NonNull
96 String getNamespace();
97
98
99
100
101
102
103 @NonNull
104 default QName getQName() {
105 return new QName(getNamespace(), getName());
106 }
107
108
109
110
111
112
113 @NonNull
114 Set<FunctionProperty> getProperties();
115
116
117
118
119
120
121 @NonNull
122 List<IArgument> getArguments();
123
124
125
126
127
128
129 int arity();
130
131
132
133
134
135
136
137
138 default boolean isDeterministic() {
139 return getProperties().contains(FunctionProperty.DETERMINISTIC);
140 }
141
142
143
144
145
146
147
148
149
150 default boolean isContextDepenent() {
151 return getProperties().contains(FunctionProperty.CONTEXT_DEPENDENT);
152 }
153
154
155
156
157
158
159
160
161 default boolean isFocusDepenent() {
162 return getProperties().contains(FunctionProperty.FOCUS_DEPENDENT);
163 }
164
165
166
167
168
169
170
171
172 default boolean isArityUnbounded() {
173 return getProperties().contains(FunctionProperty.UNBOUNDED_ARITY);
174 }
175
176
177
178
179
180
181 @NonNull
182 ISequenceType getResult();
183
184
185
186
187
188
189
190
191
192
193
194
195 @NonNull
196 ISequence<?> execute(
197 @NonNull List<ISequence<?>> arguments,
198 @NonNull DynamicContext dynamicContext,
199 @NonNull ISequence<?> focus) throws MetapathException;
200
201
202
203
204
205
206 String toSignature();
207
208 @NonNull
209 static Builder builder() {
210 return new Builder();
211 }
212
213 @SuppressWarnings("PMD.LooseCoupling")
214 class Builder {
215 private String name;
216 private String namespace;
217 @SuppressWarnings("null")
218 @NonNull
219 private final EnumSet<FunctionProperty> properties = EnumSet.noneOf(FunctionProperty.class);
220 @NonNull
221 private final List<IArgument> arguments = new LinkedList<>();
222 private Class<? extends IItem> returnType = IItem.class;
223 private Occurrence returnOccurrence = Occurrence.ONE;
224 private IFunctionExecutor functionHandler;
225
226 @NonNull
227 public Builder name(@NonNull String name) {
228 Objects.requireNonNull(name, "name");
229 if (name.isBlank()) {
230 throw new IllegalArgumentException("the name must be non-blank");
231 }
232 this.name = name.trim();
233 return this;
234 }
235
236 @NonNull
237 public Builder namespace(@NonNull URI uri) {
238 return namespace(ObjectUtils.notNull(uri.toASCIIString()));
239 }
240
241 @NonNull
242 public Builder namespace(@NonNull String name) {
243 Objects.requireNonNull(name, "name");
244 if (name.isBlank()) {
245 throw new IllegalArgumentException("the name must be non-blank");
246 }
247 this.namespace = name.trim();
248 return this;
249 }
250
251 @NonNull
252 public Builder deterministic() {
253 properties.add(FunctionProperty.DETERMINISTIC);
254 return this;
255 }
256
257 @NonNull
258 public Builder nonDeterministic() {
259 properties.remove(FunctionProperty.DETERMINISTIC);
260 return this;
261 }
262
263 @NonNull
264 public Builder contextDependent() {
265 properties.add(FunctionProperty.CONTEXT_DEPENDENT);
266 return this;
267 }
268
269 @NonNull
270 public Builder contextIndependent() {
271 properties.remove(FunctionProperty.CONTEXT_DEPENDENT);
272 return this;
273 }
274
275 @NonNull
276 public Builder focusDependent() {
277 properties.add(FunctionProperty.FOCUS_DEPENDENT);
278 return this;
279 }
280
281 @NonNull
282 public Builder focusIndependent() {
283 properties.remove(FunctionProperty.FOCUS_DEPENDENT);
284 return this;
285 }
286
287 @NonNull
288 public Builder allowUnboundedArity(boolean allow) {
289 if (allow) {
290 properties.add(FunctionProperty.UNBOUNDED_ARITY);
291 } else {
292 properties.remove(FunctionProperty.UNBOUNDED_ARITY);
293 }
294 return this;
295 }
296
297 @NonNull
298 public Builder returnType(@NonNull Class<? extends IItem> type) {
299 Objects.requireNonNull(type, "type");
300 this.returnType = type;
301 return this;
302 }
303
304 @NonNull
305 public Builder returnZeroOrOne() {
306 return returnOccurrence(Occurrence.ZERO_OR_ONE);
307 }
308
309 @NonNull
310 public Builder returnOne() {
311 return returnOccurrence(Occurrence.ONE);
312 }
313
314 @NonNull
315 public Builder returnZeroOrMore() {
316 return returnOccurrence(Occurrence.ZERO_OR_MORE);
317 }
318
319 @NonNull
320 public Builder returnOneOrMore() {
321 return returnOccurrence(Occurrence.ONE_OR_MORE);
322 }
323
324 @NonNull
325 public Builder returnOccurrence(@NonNull Occurrence occurrence) {
326 Objects.requireNonNull(occurrence, "occurrence");
327 this.returnOccurrence = occurrence;
328 return this;
329 }
330
331 @NonNull
332 public Builder argument(@NonNull IArgument.Builder builder) {
333 return argument(builder.build());
334 }
335
336 @NonNull
337 public Builder argument(@NonNull IArgument argument) {
338 Objects.requireNonNull(argument, "argument");
339 this.arguments.add(argument);
340 return this;
341 }
342
343 @NonNull
344 public Builder functionHandler(@NonNull IFunctionExecutor handler) {
345 Objects.requireNonNull(handler, "handler");
346 this.functionHandler = handler;
347 return this;
348 }
349
350 @NonNull
351 public IFunction build() {
352 ISequenceType sequenceType;
353 if (returnType == null) {
354 sequenceType = ISequenceType.EMPTY;
355 } else {
356 sequenceType = new SequenceTypeImpl(
357 returnType,
358 ObjectUtils.requireNonNull(returnOccurrence, "the return occurrence must not be null"));
359 }
360
361 if (properties.contains(FunctionProperty.UNBOUNDED_ARITY) && arguments.isEmpty()) {
362 throw new IllegalStateException("to allow unbounded arity, at least one argument must be provided");
363 }
364
365 return new DefaultFunction(
366 ObjectUtils.requireNonNull(name, "the name must not be null"),
367 ObjectUtils.requireNonNull(namespace, "the namespace must not be null"),
368 properties,
369 new ArrayList<>(arguments),
370 sequenceType,
371 ObjectUtils.requireNonNull(functionHandler, "the function handler must not be null"));
372 }
373 }
374 }