1 /*
2 * SPDX-FileCopyrightText: none
3 * SPDX-License-Identifier: CC0-1.0
4 */
5
6 package gov.nist.secauto.metaschema.core.metapath;
7
8 import gov.nist.secauto.metaschema.core.datatype.DataTypeService;
9 import gov.nist.secauto.metaschema.core.metapath.function.FunctionService;
10 import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
11 import gov.nist.secauto.metaschema.core.metapath.function.IFunctionResolver;
12 import gov.nist.secauto.metaschema.core.metapath.item.IItem;
13 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
14 import gov.nist.secauto.metaschema.core.metapath.type.IAtomicOrUnionType;
15 import gov.nist.secauto.metaschema.core.metapath.type.IItemType;
16 import gov.nist.secauto.metaschema.core.qname.EQNameFactory;
17 import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
18 import gov.nist.secauto.metaschema.core.qname.NamespaceCache;
19 import gov.nist.secauto.metaschema.core.qname.WellKnown;
20 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
21 import gov.nist.secauto.metaschema.core.util.CustomCollectors;
22 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
23
24 import java.net.URI;
25 import java.net.URISyntaxException;
26 import java.util.Map;
27 import java.util.Objects;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.stream.Collectors;
30
31 import javax.xml.XMLConstants;
32
33 import edu.umd.cs.findbugs.annotations.NonNull;
34 import edu.umd.cs.findbugs.annotations.Nullable;
35
36 // add support for default namespace
37 /**
38 * An implementation of the Metapath
39 * <a href="https://www.w3.org/TR/xpath-31/#static_context">static context</a>.
40 */
41 // FIXME: refactor well-known into a new class
42 public final class StaticContext {
43 @Nullable
44 private final URI baseUri;
45 @NonNull
46 private final Map<String, String> knownPrefixToNamespace;
47 @NonNull
48 private final Map<String, String> knownNamespacesToPrefix;
49 @Nullable
50 private final String defaultModelNamespace;
51 @Nullable
52 private final String defaultFunctionNamespace;
53 private final boolean useWildcardWhenNamespaceNotDefaulted;
54 @NonNull
55 private final IFunctionResolver functionResolver;
56
57 /**
58 * Get the mapping of prefix to namespace URI for all well-known namespaces
59 * provided by default to the static context.
60 * <p>
61 * These namespaces can be overridden using the
62 * {@link Builder#namespace(String, URI)} method.
63 * <p>
64 * This method has been deprecated. While
65 * {@link WellKnown#getWellKnownPrefixesToNamespaces()} can be used in place of
66 * this method, it is generally preferred to call
67 * {@link WellKnown#getWellKnownUriForPrefix(String)} instead.
68 *
69 * @return the mapping of prefix to namespace URI for all well-known namespaces
70 */
71 @NonNull
72 @Deprecated(since = "2.2.0", forRemoval = true)
73 public static Map<String, String> getWellKnownNamespacesMap() {
74 return WellKnown.getWellKnownPrefixesToNamespaces();
75 }
76
77 /**
78 * Get the mapping of namespace URIs to prefixes for all well-known namespaces
79 * provided by default to the static context.
80 * <p>
81 * This method has been deprecated. While
82 * {@link WellKnown#getWellKnownURIsToPrefixes()} can be used in place of this
83 * method, it is generally preferred to call
84 * {@link WellKnown#getWellKnownPrefixForUri(String)} instead.
85 *
86 * @return the mapping of namespace URI to prefix for all well-known namespaces
87 */
88 @NonNull
89 @Deprecated(since = "2.2.0", forRemoval = true)
90 public static Map<String, String> getWellKnownURIToPrefixMap() {
91 return WellKnown.getWellKnownURIsToPrefixes();
92 }
93
94 /**
95 * Get the namespace prefix associated with the provided URI, if the URI is
96 * well-known.
97 * <p>
98 * This method has been deprecated. While
99 * {@link WellKnown#getWellKnownPrefixForUri(String)} can be used in place of
100 * this method.
101 *
102 * @param uri
103 * the URI to get the prefix for
104 * @return the prefix or {@code null} if the provided URI is not well-known
105 */
106 @Deprecated(since = "2.2.0", forRemoval = true)
107 @Nullable
108 public static String getWellKnownPrefixForUri(@NonNull String uri) {
109 return WellKnown.getWellKnownPrefixForUri(uri);
110 }
111
112 /**
113 * Create a new static context instance using default values.
114 *
115 * @return a new static context instance
116 */
117 @NonNull
118 public static StaticContext instance() {
119 return builder().build();
120 }
121
122 private StaticContext(Builder builder) {
123 this.baseUri = builder.baseUri;
124 this.knownPrefixToNamespace = CollectionUtil.unmodifiableMap(ObjectUtils.notNull(Map.copyOf(builder.namespaces)));
125 this.knownNamespacesToPrefix = ObjectUtils.notNull(builder.namespaces.entrySet().stream()
126 .map(entry -> Map.entry(entry.getValue(), entry.getKey()))
127 .collect(Collectors.toUnmodifiableMap(
128 Map.Entry::getKey,
129 Map.Entry::getValue,
130 CustomCollectors.useFirstMapper())));
131 this.defaultModelNamespace = builder.defaultModelNamespace;
132 this.defaultFunctionNamespace = builder.defaultFunctionNamespace;
133 this.useWildcardWhenNamespaceNotDefaulted = builder.useWildcardWhenNamespaceNotDefaulted;
134 this.functionResolver = builder.functionResolver;
135 }
136
137 /**
138 * Get the static base URI to use in resolving URIs handled by the Metapath
139 * processor. This URI, if provided, will be used when a document base URI is
140 * not available.
141 *
142 * @return the base URI or {@code null} if not defined
143 */
144 @Nullable
145 public URI getBaseUri() {
146 return baseUri;
147 }
148
149 /**
150 * Get the namespace URI associated with the provided {@code prefix}, if any is
151 * bound.
152 * <p>
153 * This method uses the namespaces set by the
154 * {@link Builder#namespace(String, URI)} method, falling back to the well-known
155 * namespace bindings when a prefix match is not found.
156 * <p>
157 * The well-known namespace bindings can be retrieved using the
158 * {@link StaticContext#getWellKnownNamespacesMap()} method.
159 *
160 * @param prefix
161 * the namespace prefix
162 * @return the namespace URI bound to the prefix, or {@code null} if no
163 * namespace is bound to the prefix
164 * @see Builder#namespace(String, URI)
165 * @see #getWellKnownNamespacesMap()
166 */
167 @Nullable
168 private String lookupNamespaceURIForPrefix(@NonNull String prefix) {
169 String retval = knownPrefixToNamespace.get(prefix);
170 if (retval == null) {
171 // fall back to well-known namespaces
172 retval = WellKnown.getWellKnownUriForPrefix(prefix);
173 }
174 return retval;
175 }
176
177 @Nullable
178 private String lookupPrefixForNamespaceURI(@NonNull String namespace) {
179 String retval = knownNamespacesToPrefix.get(namespace);
180 if (retval == null) {
181 // fall back to well-known namespaces
182 retval = WellKnown.getWellKnownPrefixForUri(namespace);
183 }
184 return retval;
185 }
186
187 /**
188 * Get the namespace associated with the provided {@code prefix} as a string, if
189 * any is bound.
190 *
191 * @param prefix
192 * the namespace prefix
193 * @return the namespace string bound to the prefix, or {@code null} if no
194 * namespace is bound to the prefix
195 */
196 @Nullable
197 public String lookupNamespaceForPrefix(@NonNull String prefix) {
198 String result = lookupNamespaceURIForPrefix(prefix);
199 return result == null ? null : result;
200 }
201
202 /**
203 * Get the prefix associated with the provided {@code namespace} as a string, if
204 * any is bound.
205 *
206 * @param namespace
207 * the namespace
208 * @return the prefix string bound to the prefix, or {@code null} if no prefix
209 * is bound to the namespace
210 */
211 @Nullable
212 public String lookupPrefixForNamespace(@NonNull String namespace) {
213 return lookupPrefixForNamespaceURI(namespace);
214 }
215
216 /**
217 * Get the default namespace for assembly, field, or flag references that have
218 * no namespace prefix.
219 *
220 * @return the namespace if defined or {@code null} otherwise
221 */
222 @Nullable
223 private String getDefaultModelNamespace() {
224 return defaultModelNamespace;
225 }
226
227 /**
228 * Get the default namespace for function references that have no namespace
229 * prefix.
230 *
231 * @return the namespace if defined or {@code null} otherwise
232 */
233 @Nullable
234 private String getDefaultFunctionNamespace() {
235 return defaultFunctionNamespace;
236 }
237
238 /**
239 * Parse the name of an atomic type.
240 * <p>
241 * This method will attempt to identify the namespace corresponding to a given
242 * prefix.
243 * <p>
244 * The prefix will be resolved using the following lookup order, advancing to
245 * the next when a {@code null} value is returned:
246 * <ol>
247 * <li>Lookup the prefix using the namespaces registered with the static
248 * context.
249 * <li>Lookup the prefix in the well-known namespaces.
250 * </ol>
251 * <p>
252 * If an empty prefix is provided, the {@link MetapathConstants#NS_METAPATH}
253 * namespace will be used.
254 *
255 * @param name
256 * the name
257 * @return the parsed qualified name
258 * @throws StaticMetapathException
259 * with the code {@link StaticMetapathException#PREFIX_NOT_EXPANDABLE}
260 * if a non-empty prefix is provided
261 */
262 @NonNull
263 public IEnhancedQName parseAtomicTypeName(@NonNull String name) {
264 return EQNameFactory.instance().parseName(
265 name,
266 this::resolveAtomicTypePrefix);
267 }
268
269 private String resolveAtomicTypePrefix(@NonNull String prefix) {
270 String ns = lookupNamespaceForPrefix(prefix);
271 if (ns == null) {
272 checkForUnknownPrefix(prefix);
273 // use the default data type namespace
274 ns = MetapathConstants.NS_METAPATH;
275 }
276 return ns;
277 }
278
279 /**
280 * Lookup the atomic type with the provided name in the static context.
281 * <p>
282 * This method will first attempt to expand the namespace prefix for a lexical
283 * QName. A {@link StaticMetapathException} with the code
284 * {@link StaticMetapathException#PREFIX_NOT_EXPANDABLE} if the prefix is not
285 * known to the static context.
286 * <p>
287 * Once the qualified name has been produced, the atomic type will be retrieved
288 * from the available atomic types. If the atomic type was not found, a
289 * {@link StaticMetapathException} with the code
290 * {@link StaticMetapathException#UNKNOWN_TYPE} will be thrown. Otherwise, the
291 * type information is returned for the matching atomic type.
292 *
293 * @param name
294 * the namespace qualified or lexical name of the data type.
295 * @return the data type information
296 * @throws StaticMetapathException
297 * with the code {@link StaticMetapathException#PREFIX_NOT_EXPANDABLE}
298 * if the lexical name was not able to be expanded or the code
299 * {@link StaticMetapathException#NO_FUNCTION_MATCH} if a matching
300 * type was not found
301 */
302 @NonNull
303 public IAtomicOrUnionType<?> lookupAtomicType(@NonNull String name) {
304 IEnhancedQName qname = parseAtomicTypeName(name);
305 return lookupAtomicType(qname);
306 }
307
308 /**
309 * Lookup a known Metapath atomic type based on the type's qualified name.
310 *
311 * @param qname
312 * the qualified name
313 * @return the type
314 * @throws StaticMetapathException
315 * with the code {@link StaticMetapathException#UNKNOWN_TYPE} if the
316 * type was not found
317 */
318 @NonNull
319 public static IAtomicOrUnionType<?> lookupAtomicType(@NonNull IEnhancedQName qname) {
320 IAtomicOrUnionType<?> retval = DataTypeService.instance().getAtomicTypeByQNameIndex(qname.getIndexPosition());
321 if (retval == null) {
322 throw new StaticMetapathException(
323 StaticMetapathException.UNKNOWN_TYPE,
324 String.format("The atomic type named '%s' was not found.", qname));
325 }
326 return retval;
327 }
328
329 /**
330 * Lookup a known Metapath atomic type based on the type's item class.
331 *
332 * @param <T>
333 * the Java type of the item to get the type information for
334 * @param clazz
335 * the item class associated with the atomic type
336 * @return the type
337 * @throws StaticMetapathException
338 * with the code {@link StaticMetapathException#UNKNOWN_TYPE} if the
339 * type was not found
340 */
341 @NonNull
342 public static <T extends IAnyAtomicItem> IAtomicOrUnionType<T> lookupAtomicType(Class<T> clazz) {
343 IAtomicOrUnionType<T> retval = DataTypeService.instance().getAtomicTypeByItemClass(clazz);
344 if (retval == null) {
345 throw new StaticMetapathException(
346 StaticMetapathException.UNKNOWN_TYPE,
347 String.format("The atomic type for item class '%s' was not found.", clazz.getName()));
348 }
349 return retval;
350 }
351
352 /**
353 * Lookup a known Metapath item type based on the type's item class.
354 *
355 * @param clazz
356 * the item class associated with the atomic type
357 * @return the type
358 * @throws StaticMetapathException
359 * with the code {@link StaticMetapathException#UNKNOWN_TYPE} if the
360 * type was not found
361 */
362 @NonNull
363 public static IItemType lookupItemType(Class<? extends IItem> clazz) {
364 IItemType retval = DataTypeService.instance().getItemTypeByItemClass(clazz);
365 if (retval == null) {
366 throw new StaticMetapathException(
367 StaticMetapathException.UNKNOWN_TYPE,
368 String.format("The item type for item class '%s' was not found.", clazz.getName()));
369 }
370 return retval;
371 }
372
373 /**
374 * Parse a function name.
375 * <p>
376 * This method will attempt to identify the namespace corresponding to a given
377 * prefix.
378 * <p>
379 * The prefix will be resolved using the following lookup order, advancing to
380 * the next when a {@code null} value is returned:
381 * <ol>
382 * <li>Lookup the prefix using the namespaces registered with the static
383 * context.
384 * <li>Lookup the prefix in the well-known namespaces.
385 * </ol>
386 * If an empty prefix is provided, the
387 * {@link Builder#defaultFunctionNamespace(String)} namespace will be used.
388 *
389 * @param name
390 * the name
391 * @return the parsed qualified name
392 */
393 @NonNull
394 public IEnhancedQName parseFunctionName(@NonNull String name) {
395 return EQNameFactory.instance().parseName(
396 name,
397 this::resolveFunctionPrefix);
398 }
399
400 @NonNull
401 private String resolveFunctionPrefix(@NonNull String prefix) {
402 String ns = lookupNamespaceForPrefix(prefix);
403 if (ns == null) {
404 checkForUnknownPrefix(prefix);
405 // use the default namespace, since the namespace was omitted
406 ns = getDefaultFunctionNamespace();
407 }
408 return ns == null ? XMLConstants.NULL_NS_URI : ns;
409 }
410
411 /**
412 * Checks if the provided prefix is not-empty, which means the prefix was not
413 * resolvable.
414 *
415 * @param prefix
416 * the lexical prefix to check
417 * @throws StaticMetapathException
418 * with the code {@link StaticMetapathException#PREFIX_NOT_EXPANDABLE}
419 * if a non-empty prefix is provided
420 */
421 private static void checkForUnknownPrefix(@NonNull String prefix) {
422 if (!prefix.isEmpty()) {
423 throw new StaticMetapathException(
424 StaticMetapathException.PREFIX_NOT_EXPANDABLE,
425 String.format("The namespace prefix '%s' is not expandable.",
426 prefix));
427 }
428 }
429
430 /**
431 * Lookup a known Metapath function based on the function's name and arity.
432 * <p>
433 * This method will first attempt to expand the namespace prefix for a lexical
434 * QName. A {@link StaticMetapathException} with the code
435 * {@link StaticMetapathException#PREFIX_NOT_EXPANDABLE} if the prefix is not
436 * known to the static context.
437 * <p>
438 * Once the qualified name has been produced, the function will be retrieved
439 * from the available functions. If the function was not found, a
440 * {@link StaticMetapathException} with the code
441 * {@link StaticMetapathException#UNKNOWN_TYPE} will be thrown. Otherwise, the
442 * data type information is returned for the matching data type.
443 *
444 * @param name
445 * the qualified or lexical name of the function
446 * @param arity
447 * the number of arguments
448 * @return the type
449 * @throws StaticMetapathException
450 * with the code {@link StaticMetapathException#PREFIX_NOT_EXPANDABLE}
451 * if the lexical name was not able to be expanded or the code
452 * {@link StaticMetapathException#NO_FUNCTION_MATCH} if a matching
453 * function was not found
454 */
455 @NonNull
456 public IFunction lookupFunction(@NonNull String name, int arity) {
457 IEnhancedQName qname = parseFunctionName(name);
458 return functionResolver.getFunction(qname, arity);
459 }
460
461 /**
462 * Lookup a known Metapath function based on the function's name and arity.
463 *
464 * @param qname
465 * the qualified name of the function
466 * @param arity
467 * the number of arguments
468 * @return the function
469 * @throws StaticMetapathException
470 * with the code {@link StaticMetapathException#NO_FUNCTION_MATCH} if
471 * a matching function was not found
472 */
473 @NonNull
474 public static IFunction lookupFunction(@NonNull IEnhancedQName qname, int arity) {
475 return FunctionService.getInstance().getFunction(
476 Objects.requireNonNull(qname, "name"),
477 arity);
478 }
479
480 /**
481 * Parse a flag name.
482 * <p>
483 * This method will attempt to identify the namespace corresponding to a given
484 * prefix.
485 * <p>
486 * The prefix will be resolved using the following lookup order, advancing to
487 * the next when a {@code null} value is returned:
488 * <ol>
489 * <li>Lookup the prefix using the namespaces registered with the static
490 * context.
491 * <li>Lookup the prefix in the well-known namespaces.
492 * </ol>
493 * If an empty prefix is provided, the {@link XMLConstants#NULL_NS_URI}
494 * namespace will be used.
495 *
496 * @param name
497 * the name
498 * @return the parsed qualified name
499 * @throws StaticMetapathException
500 * with the code {@link StaticMetapathException#PREFIX_NOT_EXPANDABLE}
501 * if a non-empty prefix is provided
502 */
503 @NonNull
504 public IEnhancedQName parseFlagName(@NonNull String name) {
505 return EQNameFactory.instance().parseName(
506 name,
507 this::resolveBasicPrefix);
508 }
509
510 private String resolveBasicPrefix(@NonNull String prefix) {
511 String ns = lookupNamespaceForPrefix(prefix);
512 if (ns == null) {
513 checkForUnknownPrefix(prefix);
514 }
515 return ns == null ? XMLConstants.NULL_NS_URI : ns;
516 }
517
518 /**
519 * Parse a model name.
520 * <p>
521 * This method will attempt to identify the namespace corresponding to a given
522 * prefix.
523 * <p>
524 * The prefix will be resolved using the following lookup order, advancing to
525 * the next when a {@code null} value is returned:
526 * <ol>
527 * <li>Lookup the prefix using the namespaces registered with the static
528 * context.
529 * <li>Lookup the prefix in the well-known namespaces.
530 * </ol>
531 * If an empty prefix is provided, the
532 * {@link Builder#defaultModelNamespace(String)} namespace will be used.
533 *
534 * @param name
535 * the name
536 * @return the parsed qualified name
537 */
538 @NonNull
539 public IEnhancedQName parseModelName(@NonNull String name) {
540 return EQNameFactory.instance().parseName(
541 name,
542 this::resolveModelReferencePrefix);
543 }
544
545 @NonNull
546 private String resolveModelReferencePrefix(@NonNull String prefix) {
547 String ns = lookupNamespaceForPrefix(prefix);
548 if (ns == null) {
549 checkForUnknownPrefix(prefix);
550 ns = getDefaultModelNamespace();
551 }
552 return ns == null ? XMLConstants.NULL_NS_URI : ns;
553 }
554
555 /**
556 * Parse a variable name.
557 * <p>
558 * This method will attempt to identify the namespace corresponding to a given
559 * prefix.
560 * <p>
561 * The prefix will be resolved using the following lookup order, advancing to
562 * the next when a {@code null} value is returned:
563 * <ol>
564 * <li>Lookup the prefix using the namespaces registered with the static
565 * context.
566 * <li>Lookup the prefix in the well-known namespaces.
567 * </ol>
568 * If an empty prefix is provided, the {@link XMLConstants#NULL_NS_URI}
569 * namespace will be used.
570 *
571 * @param name
572 * the name
573 * @return the parsed qualified name
574 */
575 @NonNull
576 public IEnhancedQName parseVariableName(@NonNull String name) {
577 return EQNameFactory.instance().parseName(
578 name,
579 this::resolveBasicPrefix);
580 }
581
582 /**
583 * Get a new static context builder that is pre-populated with the setting of
584 * this static context.
585 *
586 * @return a new builder
587 */
588 @NonNull
589 public Builder buildFrom() {
590 Builder builder = builder();
591 builder.baseUri = this.baseUri;
592 builder.namespaces.putAll(this.knownPrefixToNamespace);
593 builder.defaultModelNamespace = this.defaultModelNamespace;
594 builder.defaultFunctionNamespace = this.defaultFunctionNamespace;
595 return builder;
596 }
597
598 /**
599 * Indicates if a name match should use a wildcard for the namespace if the
600 * namespace does not have a value and the default model namespace is
601 * {@code null}.
602 *
603 * @return {@code true} if a wildcard match on the name space should be used or
604 * {@code false} otherwise
605 */
606 public boolean isUseWildcardWhenNamespaceNotDefaulted() {
607 return useWildcardWhenNamespaceNotDefaulted && getDefaultModelNamespace() == null;
608 }
609
610 /**
611 * Create a new static context builder that allows for fine-grained adjustments
612 * when creating a new static context.
613 *
614 * @return a new builder
615 */
616 @NonNull
617 public static Builder builder() {
618 return new Builder();
619 }
620
621 /**
622 * A builder used to generate the static context.
623 */
624 public static final class Builder {
625 private boolean useWildcardWhenNamespaceNotDefaulted; // false
626 @Nullable
627 private URI baseUri;
628 @NonNull
629 private final Map<String, String> namespaces = new ConcurrentHashMap<>();
630 @Nullable
631 private String defaultModelNamespace;
632 @Nullable
633 private String defaultFunctionNamespace = MetapathConstants.NS_METAPATH_FUNCTIONS;
634 @NonNull
635 private IFunctionResolver functionResolver = FunctionService.getInstance();
636
637 private Builder() {
638 // avoid direct construction
639 }
640
641 /**
642 * Sets the static base URI to use in resolving URIs handled by the Metapath
643 * processor, when a document base URI is not available. There is only a single
644 * base URI. Subsequent calls to this method will change the base URI.
645 *
646 * @param uri
647 * the base URI to use
648 * @return this builder
649 */
650 @NonNull
651 public Builder baseUri(@NonNull URI uri) {
652 this.baseUri = uri;
653 return this;
654 }
655
656 /**
657 * Adds a new prefix to namespace URI binding to the mapping of
658 * <a href="https://www.w3.org/TR/xpath-31/#dt-static-namespaces">statically
659 * known namespaces</a>.
660 * <p>
661 * A namespace set by this method can be resolved using the
662 * {@link StaticContext#lookupNamespaceForPrefix(String)} method.
663 * <p>
664 * Well-known namespace bindings are used by default, which are provided by
665 * {@link WellKnown}.
666 *
667 * @param prefix
668 * the prefix to associate with the namespace, which may be
669 * @param uri
670 * the namespace URI
671 * @return this builder
672 * @see StaticContext#lookupNamespaceForPrefix(String)
673 */
674 @NonNull
675 public Builder namespace(@NonNull String prefix, @NonNull URI uri) {
676 return namespace(prefix, ObjectUtils.notNull(uri.toASCIIString()));
677 }
678
679 /**
680 * A convenience method for {@link #namespace(String, URI)}.
681 *
682 * @param prefix
683 * the prefix to associate with the namespace, which may be
684 * @param uri
685 * the namespace URI
686 * @return this builder
687 * @throws IllegalArgumentException
688 * if the provided prefix or URI is invalid
689 * @see StaticContext#lookupNamespaceForPrefix(String)
690 */
691 @NonNull
692 public Builder namespace(@NonNull String prefix, @NonNull String uri) {
693 if (MetapathConstants.PREFIX_METAPATH.equals(prefix)) {
694 // check for https://www.w3.org/TR/xpath-31/#ERRXPST0070 for "meta"
695 throw new IllegalArgumentException(
696 "Redefining the prefix '" + MetapathConstants.PREFIX_METAPATH + "' is not allowed.");
697 }
698 this.namespaces.put(prefix, uri);
699 NamespaceCache.instance().indexOf(uri);
700 return this;
701 }
702
703 /**
704 * Defines the default namespace to use for assembly, field, or flag references
705 * that have no namespace prefix.
706 *
707 * @param namespace
708 * the namespace URI
709 * @return this builder
710 */
711 @NonNull
712 public Builder defaultModelNamespace(@NonNull URI namespace) {
713 String uri = ObjectUtils.notNull(namespace.toASCIIString());
714 this.defaultModelNamespace = uri;
715 NamespaceCache.instance().indexOf(uri);
716 return this;
717 }
718
719 /**
720 * A convenience method for {@link #defaultModelNamespace(URI)}.
721 *
722 * @param uri
723 * the namespace URI
724 * @return this builder
725 * @throws IllegalArgumentException
726 * if the provided URI is invalid
727 */
728 @NonNull
729 public Builder defaultModelNamespace(@NonNull String uri) {
730 try {
731 this.defaultModelNamespace = new URI(uri).toASCIIString();
732 } catch (URISyntaxException ex) {
733 throw new IllegalArgumentException(ex);
734 }
735 NamespaceCache.instance().indexOf(uri);
736 return this;
737 }
738
739 /**
740 * Defines the default namespace to use for assembly, field, or flag references
741 * that have no namespace prefix.
742 *
743 * @param namespace
744 * the namespace URI
745 * @return this builder
746 */
747 @NonNull
748 public Builder defaultFunctionNamespace(@NonNull URI namespace) {
749 String uri = ObjectUtils.notNull(namespace.toASCIIString());
750 this.defaultFunctionNamespace = uri;
751 NamespaceCache.instance().indexOf(uri);
752 return this;
753 }
754
755 /**
756 * A convenience method for {@link #defaultFunctionNamespace(URI)}.
757 *
758 * @param uri
759 * the namespace URI
760 * @return this builder
761 * @throws IllegalArgumentException
762 * if the provided URI is invalid
763 */
764 @NonNull
765 public Builder defaultFunctionNamespace(@NonNull String uri) {
766 try {
767 this.defaultFunctionNamespace = new URI(uri).toASCIIString();
768 } catch (URISyntaxException ex) {
769 throw new IllegalArgumentException(ex);
770 }
771 NamespaceCache.instance().indexOf(uri);
772 return this;
773 }
774
775 /**
776 * Set the name matching behavior for when a model node has no namespace.
777 *
778 * @param value
779 * {@code true} if on or {@code false} otherwise
780 * @return this builder
781 */
782 public Builder useWildcardWhenNamespaceNotDefaulted(boolean value) {
783 this.useWildcardWhenNamespaceNotDefaulted = value;
784 return this;
785 }
786
787 /**
788 * Set the function resolver used to lookup function implementations.
789 * <p>
790 * By default, the {@link FunctionService} is used to load function
791 * implementations using the service provider interface.
792 *
793 * @param resolver
794 * the resolver to use instead of the default resolver
795 * @return this builder
796 */
797 public Builder functionResolver(@NonNull IFunctionResolver resolver) {
798 this.functionResolver = resolver;
799 return this;
800 }
801
802 /**
803 * Construct a new static context using the information provided to the builder.
804 *
805 * @return the new static context
806 */
807 @NonNull
808 public StaticContext build() {
809 return new StaticContext(this);
810 }
811 }
812 }