1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
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.ISequence;
10  import gov.nist.secauto.metaschema.core.metapath.MetapathConstants;
11  import gov.nist.secauto.metaschema.core.metapath.MetapathException;
12  import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
13  import gov.nist.secauto.metaschema.core.metapath.function.IArgument;
14  import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
15  import gov.nist.secauto.metaschema.core.metapath.item.IItem;
16  import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
17  import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
18  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
19  import gov.nist.secauto.oscal.lib.OscalModelConstants;
20  import gov.nist.secauto.oscal.lib.model.Catalog;
21  import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionException;
22  import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolver;
23  
24  import java.io.IOException;
25  import java.util.List;
26  
27  import edu.umd.cs.findbugs.annotations.NonNull;
28  
29  public final class ResolveProfile {
30  
31    @NonNull
32    static final IFunction SIGNATURE_NO_ARG = IFunction.builder()
33        .name("resolve-profile")
34        .namespace(OscalModelConstants.NS_OSCAL)
35        .returnType(INodeItem.class)
36        .focusDependent()
37        .contextDependent()
38        .deterministic()
39        .returnOne()
40        .functionHandler(ResolveProfile::executeNoArg)
41        .build();
42  
43    @NonNull
44    static final IFunction SIGNATURE_ONE_ARG = IFunction.builder()
45        .name("resolve-profile")
46        .namespace(OscalModelConstants.NS_OSCAL)
47        .argument(IArgument.builder()
48            .name("profile")
49            .type(INodeItem.class)
50            .zeroOrOne()
51            .build())
52        .focusDependent()
53        .contextDependent()
54        .deterministic()
55        .returnType(INodeItem.class)
56        .returnOne()
57        .functionHandler(ResolveProfile::executeOneArg)
58        .build();
59  
60    @NonNull
61    static final IFunction SIGNATURE_NO_ARG_METAPATH = IFunction.builder()
62        .name("resolve-profile")
63        .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS)
64        .returnType(INodeItem.class)
65        .focusDependent()
66        .contextDependent()
67        .deterministic()
68        .returnOne()
69        .functionHandler(ResolveProfile::executeNoArg)
70        .build();
71  
72    @NonNull
73    static final IFunction SIGNATURE_ONE_ARG_METAPATH = IFunction.builder()
74        .name("resolve-profile")
75        .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS)
76        .argument(IArgument.builder()
77            .name("profile")
78            .type(INodeItem.class)
79            .zeroOrOne()
80            .build())
81        .focusDependent()
82        .contextDependent()
83        .deterministic()
84        .returnType(INodeItem.class)
85        .returnOne()
86        .functionHandler(ResolveProfile::executeOneArg)
87        .build();
88  
89    private ResolveProfile() {
90      // disable construction
91    }
92  
93    @SuppressWarnings({ "unused",
94        "PMD.OnlyOneReturn" // readability
95    })
96    @NonNull
97    public static ISequence<?> executeNoArg(
98        @NonNull IFunction function,
99        @NonNull List<ISequence<?>> arguments,
100       @NonNull DynamicContext dynamicContext,
101       IItem focus) {
102 
103     if (focus == null) {
104       return ISequence.empty();
105     }
106     return ISequence.of(resolveProfile(FunctionUtils.asType(focus), dynamicContext));
107   }
108 
109   @SuppressWarnings({ "unused",
110       "PMD.OnlyOneReturn" // readability
111   })
112   @NonNull
113   public static ISequence<?> executeOneArg(
114       @NonNull IFunction function,
115       @NonNull List<ISequence<?>> arguments,
116       @NonNull DynamicContext dynamicContext,
117       IItem focus) {
118     ISequence<? extends IDocumentNodeItem> arg = FunctionUtils.asType(
119         ObjectUtils.notNull(arguments.get(0)));
120 
121     IItem item = arg.getFirstItem(true);
122     if (item == null) {
123       return ISequence.empty();
124     }
125 
126     return ISequence.of(resolveProfile(FunctionUtils.asType(item), dynamicContext));
127   }
128 
129   @NonNull
130   public static IDocumentNodeItem resolveProfile(@NonNull IDocumentNodeItem profile,
131       @NonNull DynamicContext dynamicContext) {
132     Object profileObject = INodeItem.toValue(profile);
133 
134     IDocumentNodeItem retval;
135     if (profileObject instanceof Catalog) {
136       retval = profile;
137     } else {
138       // this is a profile
139       ProfileResolver resolver = new ProfileResolver();
140       resolver.setDynamicContext(dynamicContext);
141       try {
142         retval = resolver.resolve(profile);
143       } catch (IOException | ProfileResolutionException ex) {
144         throw new MetapathException(String.format("Fun: Unable to resolve profile '%s'", profile.getBaseUri()), ex);
145       }
146     }
147     return retval;
148   }
149 }