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