1
2
3
4
5
6 package dev.metaschema.oscal.lib.metapath.function.library;
7
8 import java.net.URI;
9 import java.util.List;
10
11 import dev.metaschema.core.metapath.DynamicContext;
12 import dev.metaschema.core.metapath.function.FunctionUtils;
13 import dev.metaschema.core.metapath.function.IArgument;
14 import dev.metaschema.core.metapath.function.IFunction;
15 import dev.metaschema.core.metapath.function.InvalidArgumentFunctionException;
16 import dev.metaschema.core.metapath.function.UnidentifiedFunctionError;
17 import dev.metaschema.core.metapath.function.library.FnRoot;
18 import dev.metaschema.core.metapath.item.IItem;
19 import dev.metaschema.core.metapath.item.ISequence;
20 import dev.metaschema.core.metapath.item.atomic.IAnyUriItem;
21 import dev.metaschema.core.metapath.item.atomic.IStringItem;
22 import dev.metaschema.core.metapath.item.atomic.IUuidItem;
23 import dev.metaschema.core.metapath.item.node.INodeItem;
24 import dev.metaschema.core.util.ObjectUtils;
25 import dev.metaschema.oscal.lib.OscalModelConstants;
26 import dev.metaschema.oscal.lib.OscalUtils;
27 import dev.metaschema.oscal.lib.model.BackMatter.Resource;
28 import dev.metaschema.oscal.lib.model.BackMatter.Resource.Rlink;
29 import dev.metaschema.oscal.lib.model.IOscalInstance;
30 import edu.umd.cs.findbugs.annotations.NonNull;
31 import edu.umd.cs.findbugs.annotations.Nullable;
32
33
34
35
36 public final class ResolveReference {
37 private static final String NAME = "resolve-reference";
38
39 @NonNull
40 static final IFunction SIGNATURE_ONE_ARG = IFunction.builder()
41 .name(NAME)
42 .namespace(OscalModelConstants.NS_OSCAL)
43 .argument(IArgument.builder()
44 .name("uri")
45 .type(IAnyUriItem.type())
46 .zeroOrOne()
47 .build())
48 .focusDependent()
49 .contextDependent()
50 .deterministic()
51 .returnZeroOrOne()
52 .returnOne()
53 .functionHandler(ResolveReference::executeOneArg)
54 .build();
55
56 @NonNull
57 static final IFunction SIGNATURE_TWO_ARGS = IFunction.builder()
58 .name(NAME)
59 .namespace(OscalModelConstants.NS_OSCAL)
60 .argument(IArgument.builder()
61 .name("uri")
62 .type(IAnyUriItem.type())
63 .zeroOrOne()
64 .build())
65 .argument(IArgument.builder()
66 .name("mediaType")
67 .type(IStringItem.type())
68 .zeroOrOne()
69 .build())
70 .focusIndependent()
71 .contextDependent()
72 .deterministic()
73 .returnType(IAnyUriItem.type())
74 .returnZeroOrOne()
75 .functionHandler(ResolveReference::executeTwoArg)
76 .build();
77
78 private ResolveReference() {
79
80 }
81
82 @SuppressWarnings({ "unused",
83 "PMD.OnlyOneReturn"
84 })
85 @NonNull
86 private static ISequence<?> executeOneArg(
87 @NonNull IFunction function,
88 @NonNull List<ISequence<?>> arguments,
89 @NonNull DynamicContext dynamicContext,
90 IItem focus) {
91 IAnyUriItem uri = FunctionUtils.asTypeOrNull(arguments.get(0).getFirstItem(true));
92
93 if (uri == null) {
94 return ISequence.empty();
95 }
96
97 INodeItem node = checkForNodeItem(focus);
98 return ISequence.of(resolveReference(uri, null, node));
99 }
100
101 @SuppressWarnings({ "unused",
102 "PMD.OnlyOneReturn"
103 })
104 @NonNull
105 private static ISequence<?> executeTwoArg(
106 @NonNull IFunction function,
107 @NonNull List<ISequence<?>> arguments,
108 @NonNull DynamicContext dynamicContext,
109 IItem focus) {
110 IAnyUriItem uri = FunctionUtils.asTypeOrNull(ObjectUtils.requireNonNull(arguments.get(0).getFirstItem(true)));
111
112 if (uri == null) {
113 return ISequence.empty();
114 }
115
116
117 assert focus != null;
118
119 IStringItem mediaType = FunctionUtils.asTypeOrNull(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true)));
120 INodeItem node = checkForNodeItem(focus);
121 return ISequence.of(resolveReference(uri, mediaType, node));
122 }
123
124
125
126
127
128
129
130
131
132
133
134
135 private static INodeItem checkForNodeItem(@NonNull IItem item) {
136 if (!(item instanceof INodeItem)) {
137
138 throw new InvalidArgumentFunctionException(
139 InvalidArgumentFunctionException.INVALID_ARGUMENT_TYPE,
140 String.format("Item of type '%s' is not a node item.", item.getClass().getName()));
141 }
142 return (INodeItem) item;
143 }
144
145 @NonNull
146 public static IAnyUriItem resolveReference(
147 @NonNull IAnyUriItem reference,
148 @Nullable IStringItem mediaType,
149 @NonNull INodeItem focusedItem) {
150 INodeItem root = FnRoot.fnRoot(focusedItem);
151 IOscalInstance oscalInstance = (IOscalInstance) INodeItem.toValue(root);
152
153 URI referenceUri = reference.asUri();
154 String fragment = referenceUri.getFragment();
155
156 return fragment != null
157 && (referenceUri.getPath() == null || referenceUri.getPath().isEmpty())
158 ? IAnyUriItem.valueOf(resolveReference(
159 fragment,
160 mediaType == null ? null : mediaType.asString(),
161 oscalInstance))
162 : reference;
163 }
164
165 @NonNull
166 public static URI resolveReference(
167 @NonNull String reference,
168 @Nullable String mediaType,
169 @NonNull IOscalInstance oscalInstance) {
170 Resource resource = oscalInstance.getResourceByUuid(IUuidItem.valueOf(reference).asUuid());
171 if (resource == null) {
172 throw new UnidentifiedFunctionError(
173 String.format("A backmatter resource with the id '%s' does not exist.", reference));
174 }
175
176 Rlink rLink = OscalUtils.findMatchingRLink(resource, mediaType);
177 if (rLink == null) {
178 throw new UnidentifiedFunctionError(
179 String.format("The backmatter resource '%s' does not have an rlink entry.", reference));
180 }
181 return rLink.getHref();
182 }
183 }