1
2
3
4
5
6 package dev.metaschema.oscal.lib.metapath.function.library;
7
8 import java.io.IOException;
9 import java.net.URI;
10 import java.util.List;
11
12 import dev.metaschema.core.metapath.DynamicContext;
13 import dev.metaschema.core.metapath.MetapathConstants;
14 import dev.metaschema.core.metapath.function.DocumentFunctionException;
15 import dev.metaschema.core.metapath.function.FunctionUtils;
16 import dev.metaschema.core.metapath.function.IArgument;
17 import dev.metaschema.core.metapath.function.IFunction;
18 import dev.metaschema.core.metapath.function.library.FnDoc;
19 import dev.metaschema.core.metapath.function.library.FnResolveUri;
20 import dev.metaschema.core.metapath.item.IItem;
21 import dev.metaschema.core.metapath.item.ISequence;
22 import dev.metaschema.core.metapath.item.atomic.IAnyUriItem;
23 import dev.metaschema.core.metapath.item.node.IDocumentNodeItem;
24 import dev.metaschema.core.metapath.item.node.INodeItem;
25 import dev.metaschema.core.util.ObjectUtils;
26 import dev.metaschema.oscal.lib.OscalModelConstants;
27 import dev.metaschema.oscal.lib.model.Catalog;
28 import dev.metaschema.oscal.lib.profile.resolver.ProfileResolutionException;
29 import dev.metaschema.oscal.lib.profile.resolver.ProfileResolver;
30 import edu.umd.cs.findbugs.annotations.NonNull;
31
32 public final class ResolveProfile {
33
34 @NonNull
35 static final IFunction SIGNATURE_NO_ARG = IFunction.builder()
36 .name("resolve-profile")
37 .namespace(OscalModelConstants.NS_OSCAL)
38 .returnType(INodeItem.type())
39 .focusDependent()
40 .contextDependent()
41 .deterministic()
42 .returnOne()
43 .functionHandler(ResolveProfile::executeNoArg)
44 .build();
45
46 @NonNull
47 static final IFunction SIGNATURE_ONE_ARG = IFunction.builder()
48 .name("resolve-profile")
49 .namespace(OscalModelConstants.NS_OSCAL)
50 .argument(IArgument.builder()
51 .name("profile")
52 .type(INodeItem.type())
53 .zeroOrOne()
54 .build())
55 .focusIndependent()
56 .contextDependent()
57 .deterministic()
58 .returnType(INodeItem.type())
59 .returnOne()
60 .functionHandler(ResolveProfile::executeOneArg)
61 .build();
62
63 @NonNull
64 static final IFunction SIGNATURE_NO_ARG_METAPATH = IFunction.builder()
65 .name("resolve-profile")
66 .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS)
67 .returnType(INodeItem.type())
68 .focusDependent()
69 .contextDependent()
70 .deterministic()
71 .returnOne()
72 .functionHandler(ResolveProfile::executeNoArg)
73 .build();
74
75 @NonNull
76 static final IFunction SIGNATURE_ONE_ARG_METAPATH = IFunction.builder()
77 .name("resolve-profile")
78 .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS)
79 .argument(IArgument.builder()
80 .name("profile")
81 .type(INodeItem.type())
82 .zeroOrOne()
83 .build())
84 .focusIndependent()
85 .contextDependent()
86 .deterministic()
87 .returnType(INodeItem.type())
88 .returnOne()
89 .functionHandler(ResolveProfile::executeOneArg)
90 .build();
91
92 private ResolveProfile() {
93
94 }
95
96 @SuppressWarnings({ "unused",
97 "PMD.OnlyOneReturn"
98 })
99 @NonNull
100 public static ISequence<?> executeNoArg(
101 @NonNull IFunction function,
102 @NonNull List<ISequence<?>> arguments,
103 @NonNull DynamicContext dynamicContext,
104 IItem focus) {
105
106 if (focus == null) {
107 return ISequence.empty();
108 }
109 return ISequence.of(resolveProfile(FunctionUtils.asType(focus), dynamicContext));
110 }
111
112 @SuppressWarnings({ "unused",
113 "PMD.OnlyOneReturn"
114 })
115 @NonNull
116 public static ISequence<?> executeOneArg(
117 @NonNull IFunction function,
118 @NonNull List<ISequence<?>> arguments,
119 @NonNull DynamicContext dynamicContext,
120 IItem focus) {
121 ISequence<? extends IDocumentNodeItem> arg = FunctionUtils.asType(
122 ObjectUtils.notNull(arguments.get(0)));
123
124 IItem item = arg.getFirstItem(true);
125 if (item == null) {
126 return ISequence.empty();
127 }
128
129 return ISequence.of(resolveProfile(FunctionUtils.asType(item), dynamicContext));
130 }
131
132 @NonNull
133 public static IDocumentNodeItem resolveProfile(
134 @NonNull IDocumentNodeItem document,
135 @NonNull DynamicContext dynamicContext) {
136
137
138 URI documentUri = document.getBaseUri();
139 String fragment = documentUri.getFragment();
140
141 IDocumentNodeItem profile;
142 if (fragment == null) {
143 profile = document;
144 } else {
145 IAnyUriItem referenceUri = ResolveReference.resolveReference(IAnyUriItem.valueOf(documentUri), null, document);
146 IAnyUriItem resolvedUri = FnResolveUri.fnResolveUri(referenceUri, null, dynamicContext);
147 profile = FnDoc.fnDoc(resolvedUri, dynamicContext);
148 }
149
150 Object profileObject = INodeItem.toValue(profile);
151
152 IDocumentNodeItem retval;
153 if (profileObject instanceof Catalog) {
154 retval = profile;
155 } else {
156
157 ProfileResolver resolver
158 = new ProfileResolver(dynamicContext, (uri, source) -> profile.getDocumentUri().resolve(uri));
159 try {
160 retval = resolver.resolve(profile);
161 } catch (IOException | ProfileResolutionException ex) {
162 throw new DocumentFunctionException(
163 DocumentFunctionException.ERROR_RETRIEVING_RESOURCE,
164 String.format("Unable to resolve profile '%s'. %s",
165 profile.getBaseUri(),
166 ex.getLocalizedMessage()),
167 ex);
168 }
169 }
170 return retval;
171 }
172 }