1
2
3
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.item.IItem;
12 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
13 import gov.nist.secauto.metaschema.core.metapath.type.IAtomicOrUnionType;
14 import gov.nist.secauto.metaschema.core.metapath.type.IItemType;
15 import gov.nist.secauto.metaschema.core.qname.EQNameFactory;
16 import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
17 import gov.nist.secauto.metaschema.core.qname.NamespaceCache;
18 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
19 import gov.nist.secauto.metaschema.core.util.CustomCollectors;
20 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
21
22 import java.net.URI;
23 import java.net.URISyntaxException;
24 import java.util.Map;
25 import java.util.Map.Entry;
26 import java.util.Objects;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.function.Function;
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 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
36
37
38
39
40
41
42 public final class StaticContext {
43 @NonNull
44 private static final Map<String, String> WELL_KNOWN_NAMESPACES;
45 @NonNull
46 private static final Map<String, String> WELL_KNOWN_URI_TO_PREFIX;
47
48 static {
49 Map<String, String> knownNamespaces = new ConcurrentHashMap<>();
50 knownNamespaces.put(
51 MetapathConstants.PREFIX_METAPATH,
52 MetapathConstants.NS_METAPATH);
53 knownNamespaces.put(
54 MetapathConstants.PREFIX_METAPATH_FUNCTIONS,
55 MetapathConstants.NS_METAPATH_FUNCTIONS);
56 knownNamespaces.put(
57 MetapathConstants.PREFIX_METAPATH_FUNCTIONS_MATH,
58 MetapathConstants.NS_METAPATH_FUNCTIONS_MATH);
59 knownNamespaces.put(
60 MetapathConstants.PREFIX_METAPATH_FUNCTIONS_ARRAY,
61 MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY);
62 knownNamespaces.put(
63 MetapathConstants.PREFIX_METAPATH_FUNCTIONS_MAP,
64 MetapathConstants.NS_METAPATH_FUNCTIONS_MAP);
65 WELL_KNOWN_NAMESPACES = CollectionUtil.unmodifiableMap(knownNamespaces);
66
67 WELL_KNOWN_NAMESPACES.forEach(
68 (prefix, namespace) -> NamespaceCache.instance().indexOf(ObjectUtils.notNull(namespace)));
69
70 WELL_KNOWN_URI_TO_PREFIX = ObjectUtils.notNull(WELL_KNOWN_NAMESPACES.entrySet().stream()
71 .collect(Collectors.toUnmodifiableMap(
72 (Function<? super Entry<String, String>, ? extends String>) Entry::getValue,
73 Map.Entry::getKey,
74 (v1, v2) -> v1)));
75 }
76
77 @Nullable
78 private final URI baseUri;
79 @NonNull
80 private final Map<String, String> knownPrefixToNamespace;
81 @NonNull
82 private final Map<String, String> knownNamespacesToPrefix;
83 @Nullable
84 private final String defaultModelNamespace;
85 @Nullable
86 private final String defaultFunctionNamespace;
87 private final boolean useWildcardWhenNamespaceNotDefaulted;
88
89
90
91
92
93
94
95
96
97
98 @SuppressFBWarnings("MS_EXPOSE_REP")
99 public static Map<String, String> getWellKnownNamespacesMap() {
100 return WELL_KNOWN_NAMESPACES;
101 }
102
103
104
105
106
107
108
109 @SuppressFBWarnings("MS_EXPOSE_REP")
110 public static Map<String, String> getWellKnownURIToPrefixMap() {
111 return WELL_KNOWN_URI_TO_PREFIX;
112 }
113
114
115
116
117
118
119
120
121
122 @Nullable
123 public static String getWellKnownPrefixForUri(@NonNull String uri) {
124 return WELL_KNOWN_URI_TO_PREFIX.get(uri);
125 }
126
127
128
129
130
131
132 @NonNull
133 public static StaticContext instance() {
134 return builder().build();
135 }
136
137 private StaticContext(Builder builder) {
138 this.baseUri = builder.baseUri;
139 this.knownPrefixToNamespace = CollectionUtil.unmodifiableMap(ObjectUtils.notNull(Map.copyOf(builder.namespaces)));
140 this.knownNamespacesToPrefix = ObjectUtils.notNull(builder.namespaces.entrySet().stream()
141 .map(entry -> Map.entry(entry.getValue(), entry.getKey()))
142 .collect(Collectors.toUnmodifiableMap(
143 Map.Entry::getKey,
144 Map.Entry::getValue,
145 CustomCollectors.useFirstMapper())));
146 this.defaultModelNamespace = builder.defaultModelNamespace;
147 this.defaultFunctionNamespace = builder.defaultFunctionNamespace;
148 this.useWildcardWhenNamespaceNotDefaulted = builder.useWildcardWhenNamespaceNotDefaulted;
149 }
150
151
152
153
154
155
156
157
158 @Nullable
159 public URI getBaseUri() {
160 return baseUri;
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181 @Nullable
182 private String lookupNamespaceURIForPrefix(@NonNull String prefix) {
183 String retval = knownPrefixToNamespace.get(prefix);
184 if (retval == null) {
185
186 retval = WELL_KNOWN_NAMESPACES.get(prefix);
187 }
188 return retval;
189 }
190
191 @Nullable
192 private String lookupPrefixForNamespaceURI(@NonNull String namespace) {
193 String retval = knownNamespacesToPrefix.get(namespace);
194 if (retval == null) {
195
196 retval = WELL_KNOWN_URI_TO_PREFIX.get(namespace);
197 }
198 return retval;
199 }
200
201
202
203
204
205
206
207
208
209
210 @Nullable
211 public String lookupNamespaceForPrefix(@NonNull String prefix) {
212 String result = lookupNamespaceURIForPrefix(prefix);
213 return result == null ? null : result;
214 }
215
216
217
218
219
220
221
222
223
224
225 @Nullable
226 public String lookupPrefixForNamespace(@NonNull String namespace) {
227 String result = lookupPrefixForNamespaceURI(namespace);
228 return result == null ? XMLConstants.DEFAULT_NS_PREFIX : result;
229 }
230
231
232
233
234
235
236
237 @Nullable
238 private String getDefaultModelNamespace() {
239 return defaultModelNamespace;
240 }
241
242
243
244
245
246
247
248 @Nullable
249 private String getDefaultFunctionNamespace() {
250 return defaultFunctionNamespace;
251 }
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278 @NonNull
279 public IEnhancedQName parseAtomicTypeName(@NonNull String name) {
280 return EQNameFactory.instance().parseName(
281 name,
282 this::resolveAtomicTypePrefix);
283 }
284
285 private String resolveAtomicTypePrefix(@NonNull String prefix) {
286 String ns = lookupNamespaceForPrefix(prefix);
287 if (ns == null) {
288 checkForUnknownPrefix(prefix);
289
290 ns = MetapathConstants.NS_METAPATH;
291 }
292 return ns;
293 }
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318 @NonNull
319 public IAtomicOrUnionType<?> lookupAtomicType(@NonNull String name) {
320 IEnhancedQName qname = parseAtomicTypeName(name);
321 return lookupAtomicType(qname);
322 }
323
324
325
326
327
328
329
330
331
332
333
334 @NonNull
335 public static IAtomicOrUnionType<?> lookupAtomicType(@NonNull IEnhancedQName qname) {
336 IAtomicOrUnionType<?> retval = DataTypeService.instance().getAtomicTypeByQNameIndex(qname.getIndexPosition());
337 if (retval == null) {
338 throw new StaticMetapathException(
339 StaticMetapathException.UNKNOWN_TYPE,
340 String.format("The atomic type named '%s' was not found.", qname));
341 }
342 return retval;
343 }
344
345
346
347
348
349
350
351
352
353
354
355 @NonNull
356 public static <T extends IAnyAtomicItem> IAtomicOrUnionType<T> lookupAtomicType(Class<T> clazz) {
357 IAtomicOrUnionType<T> retval = DataTypeService.instance().getAtomicTypeByItemClass(clazz);
358 if (retval == null) {
359 throw new StaticMetapathException(
360 StaticMetapathException.UNKNOWN_TYPE,
361 String.format("The atomic type for item class '%s' was not found.", clazz.getName()));
362 }
363 return retval;
364 }
365
366
367
368
369
370
371
372
373
374
375
376 @NonNull
377 public static IItemType lookupItemType(Class<? extends IItem> clazz) {
378 IItemType retval = DataTypeService.instance().getItemTypeByItemClass(clazz);
379 if (retval == null) {
380 throw new StaticMetapathException(
381 StaticMetapathException.UNKNOWN_TYPE,
382 String.format("The item type for item class '%s' was not found.", clazz.getName()));
383 }
384 return retval;
385 }
386
387 @NonNull
388 private IEnhancedQName parseFunctionName(@NonNull String name) {
389 return EQNameFactory.instance().parseName(
390 name,
391 this::resolveFunctionPrefix);
392 }
393
394 @NonNull
395 private String resolveFunctionPrefix(@NonNull String prefix) {
396 String ns = lookupNamespaceForPrefix(prefix);
397 if (ns == null) {
398 checkForUnknownPrefix(prefix);
399
400 ns = getDefaultFunctionNamespace();
401 }
402 return ns == null ? XMLConstants.NULL_NS_URI : ns;
403 }
404
405
406
407
408
409
410
411
412
413
414
415 private static void checkForUnknownPrefix(@NonNull String prefix) {
416 if (!prefix.isEmpty()) {
417 throw new StaticMetapathException(
418 StaticMetapathException.PREFIX_NOT_EXPANDABLE,
419 String.format("The namespace prefix '%s' is not expandable.",
420 prefix));
421 }
422 }
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449 @NonNull
450 public IFunction lookupFunction(@NonNull String name, int arity) {
451 IEnhancedQName qname = parseFunctionName(name);
452 return lookupFunction(qname, arity);
453 }
454
455
456
457
458
459
460
461
462
463
464
465
466
467 @NonNull
468 public static IFunction lookupFunction(@NonNull IEnhancedQName qname, int arity) {
469 return FunctionService.getInstance().getFunction(
470 Objects.requireNonNull(qname, "name"),
471 arity);
472 }
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498 @NonNull
499 public IEnhancedQName parseFlagName(@NonNull String name) {
500 return EQNameFactory.instance().parseName(
501 name,
502 this::resolveBasicPrefix);
503 }
504
505 private String resolveBasicPrefix(@NonNull String prefix) {
506 String ns = lookupNamespaceForPrefix(prefix);
507 if (ns == null) {
508 checkForUnknownPrefix(prefix);
509 }
510 return ns == null ? XMLConstants.NULL_NS_URI : ns;
511 }
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533 @NonNull
534 public IEnhancedQName parseModelName(@NonNull String name) {
535 return EQNameFactory.instance().parseName(
536 name,
537 this::resolveModelReferencePrefix);
538 }
539
540 @NonNull
541 private String resolveModelReferencePrefix(@NonNull String prefix) {
542 String ns = lookupNamespaceForPrefix(prefix);
543 if (ns == null) {
544 checkForUnknownPrefix(prefix);
545 ns = getDefaultModelNamespace();
546 }
547 return ns == null ? XMLConstants.NULL_NS_URI : ns;
548 }
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571 @NonNull
572 public IEnhancedQName parseVariableName(@NonNull String name) {
573 return EQNameFactory.instance().parseName(
574 name,
575 this::resolveBasicPrefix);
576 }
577
578
579
580
581
582
583
584 @NonNull
585 public Builder buildFrom() {
586 Builder builder = builder();
587 builder.baseUri = this.baseUri;
588 builder.namespaces.putAll(this.knownPrefixToNamespace);
589 builder.defaultModelNamespace = this.defaultModelNamespace;
590 builder.defaultFunctionNamespace = this.defaultFunctionNamespace;
591 return builder;
592 }
593
594
595
596
597
598
599
600
601
602 public boolean isUseWildcardWhenNamespaceNotDefaulted() {
603 return useWildcardWhenNamespaceNotDefaulted && getDefaultModelNamespace() == null;
604 }
605
606
607
608
609
610
611
612 @NonNull
613 public static Builder builder() {
614 return new Builder();
615 }
616
617
618
619
620 public static final class Builder {
621 private boolean useWildcardWhenNamespaceNotDefaulted;
622 @Nullable
623 private URI baseUri;
624 @NonNull
625 private final Map<String, String> namespaces = new ConcurrentHashMap<>();
626 @Nullable
627 private String defaultModelNamespace;
628 @Nullable
629 private String defaultFunctionNamespace = MetapathConstants.NS_METAPATH_FUNCTIONS;
630
631 private Builder() {
632
633 }
634
635
636
637
638
639
640
641
642
643
644 @NonNull
645 public Builder baseUri(@NonNull URI uri) {
646 this.baseUri = uri;
647 return this;
648 }
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669 @NonNull
670 public Builder namespace(@NonNull String prefix, @NonNull URI uri) {
671 return namespace(prefix, ObjectUtils.notNull(uri.toASCIIString()));
672 }
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687 @NonNull
688 public Builder namespace(@NonNull String prefix, @NonNull String uri) {
689 if (MetapathConstants.PREFIX_METAPATH.equals(prefix)) {
690
691 throw new IllegalArgumentException(
692 "Redefining the prefix '" + MetapathConstants.PREFIX_METAPATH + "' is not allowed.");
693 }
694 this.namespaces.put(prefix, uri);
695 NamespaceCache.instance().indexOf(uri);
696 return this;
697 }
698
699
700
701
702
703
704
705
706
707 @NonNull
708 public Builder defaultModelNamespace(@NonNull URI namespace) {
709 String uri = ObjectUtils.notNull(namespace.toASCIIString());
710 this.defaultModelNamespace = uri;
711 NamespaceCache.instance().indexOf(uri);
712 return this;
713 }
714
715
716
717
718
719
720
721
722
723
724 @NonNull
725 public Builder defaultModelNamespace(@NonNull String uri) {
726 try {
727 this.defaultModelNamespace = new URI(uri).toASCIIString();
728 } catch (URISyntaxException ex) {
729 throw new IllegalArgumentException(ex);
730 }
731 NamespaceCache.instance().indexOf(uri);
732 return this;
733 }
734
735
736
737
738
739
740
741
742
743 @NonNull
744 public Builder defaultFunctionNamespace(@NonNull URI namespace) {
745 String uri = ObjectUtils.notNull(namespace.toASCIIString());
746 this.defaultFunctionNamespace = uri;
747 NamespaceCache.instance().indexOf(uri);
748 return this;
749 }
750
751
752
753
754
755
756
757
758
759
760 @NonNull
761 public Builder defaultFunctionNamespace(@NonNull String uri) {
762 try {
763 this.defaultFunctionNamespace = new URI(uri).toASCIIString();
764 } catch (URISyntaxException ex) {
765 throw new IllegalArgumentException(ex);
766 }
767 NamespaceCache.instance().indexOf(uri);
768 return this;
769 }
770
771
772
773
774
775
776
777
778 public Builder useWildcardWhenNamespaceNotDefaulted(boolean value) {
779 this.useWildcardWhenNamespaceNotDefaulted = value;
780 return this;
781 }
782
783
784
785
786
787
788 @NonNull
789 public StaticContext build() {
790 return new StaticContext(this);
791 }
792 }
793
794
795
796
797 @FunctionalInterface
798 public interface EQNameResolver {
799
800
801
802
803
804
805
806
807
808
809 @NonNull
810 IEnhancedQName resolve(@NonNull String name);
811 }
812 }