001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package gov.nist.secauto.oscal.lib.metapath.function.library; 007 008import gov.nist.secauto.metaschema.core.metapath.DynamicContext; 009import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; 010import gov.nist.secauto.metaschema.core.metapath.MetapathException; 011import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; 012import gov.nist.secauto.metaschema.core.metapath.function.IArgument; 013import gov.nist.secauto.metaschema.core.metapath.function.IFunction; 014import gov.nist.secauto.metaschema.core.metapath.function.library.FnDoc; 015import gov.nist.secauto.metaschema.core.metapath.function.library.FnResolveUri; 016import gov.nist.secauto.metaschema.core.metapath.item.IItem; 017import gov.nist.secauto.metaschema.core.metapath.item.ISequence; 018import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem; 019import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; 020import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; 021import gov.nist.secauto.metaschema.core.util.ObjectUtils; 022import gov.nist.secauto.oscal.lib.OscalModelConstants; 023import gov.nist.secauto.oscal.lib.model.Catalog; 024import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionException; 025import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolver; 026 027import java.io.IOException; 028import java.net.URI; 029import java.util.List; 030 031import edu.umd.cs.findbugs.annotations.NonNull; 032 033public final class ResolveProfile { 034 035 @NonNull 036 static final IFunction SIGNATURE_NO_ARG = IFunction.builder() 037 .name("resolve-profile") 038 .namespace(OscalModelConstants.NS_OSCAL) 039 .returnType(INodeItem.type()) 040 .focusDependent() 041 .contextDependent() 042 .deterministic() 043 .returnOne() 044 .functionHandler(ResolveProfile::executeNoArg) 045 .build(); 046 047 @NonNull 048 static final IFunction SIGNATURE_ONE_ARG = IFunction.builder() 049 .name("resolve-profile") 050 .namespace(OscalModelConstants.NS_OSCAL) 051 .argument(IArgument.builder() 052 .name("profile") 053 .type(INodeItem.type()) 054 .zeroOrOne() 055 .build()) 056 .focusIndependent() 057 .contextDependent() 058 .deterministic() 059 .returnType(INodeItem.type()) 060 .returnOne() 061 .functionHandler(ResolveProfile::executeOneArg) 062 .build(); 063 064 @NonNull 065 static final IFunction SIGNATURE_NO_ARG_METAPATH = IFunction.builder() 066 .name("resolve-profile") 067 .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS) 068 .returnType(INodeItem.type()) 069 .focusDependent() 070 .contextDependent() 071 .deterministic() 072 .returnOne() 073 .functionHandler(ResolveProfile::executeNoArg) 074 .build(); 075 076 @NonNull 077 static final IFunction SIGNATURE_ONE_ARG_METAPATH = IFunction.builder() 078 .name("resolve-profile") 079 .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS) 080 .argument(IArgument.builder() 081 .name("profile") 082 .type(INodeItem.type()) 083 .zeroOrOne() 084 .build()) 085 .focusIndependent() 086 .contextDependent() 087 .deterministic() 088 .returnType(INodeItem.type()) 089 .returnOne() 090 .functionHandler(ResolveProfile::executeOneArg) 091 .build(); 092 093 private ResolveProfile() { 094 // disable construction 095 } 096 097 @SuppressWarnings({ "unused", 098 "PMD.OnlyOneReturn" // readability 099 }) 100 @NonNull 101 public static ISequence<?> executeNoArg( 102 @NonNull IFunction function, 103 @NonNull List<ISequence<?>> arguments, 104 @NonNull DynamicContext dynamicContext, 105 IItem focus) { 106 107 if (focus == null) { 108 return ISequence.empty(); 109 } 110 return ISequence.of(resolveProfile(FunctionUtils.asType(focus), dynamicContext)); 111 } 112 113 @SuppressWarnings({ "unused", 114 "PMD.OnlyOneReturn" // readability 115 }) 116 @NonNull 117 public static ISequence<?> executeOneArg( 118 @NonNull IFunction function, 119 @NonNull List<ISequence<?>> arguments, 120 @NonNull DynamicContext dynamicContext, 121 IItem focus) { 122 ISequence<? extends IDocumentNodeItem> arg = FunctionUtils.asType( 123 ObjectUtils.notNull(arguments.get(0))); 124 125 IItem item = arg.getFirstItem(true); 126 if (item == null) { 127 return ISequence.empty(); 128 } 129 130 return ISequence.of(resolveProfile(FunctionUtils.asType(item), dynamicContext)); 131 } 132 133 @NonNull 134 public static IDocumentNodeItem resolveProfile( 135 @NonNull IDocumentNodeItem document, 136 @NonNull DynamicContext dynamicContext) { 137 138 // make this work with unresolved fragments 139 URI documentUri = document.getBaseUri(); 140 String fragment = documentUri.getFragment(); 141 142 IDocumentNodeItem profile; 143 if (fragment == null) { 144 profile = document; 145 } else { 146 IAnyUriItem referenceUri = ResolveReference.resolveReference(IAnyUriItem.valueOf(documentUri), null, document); 147 IAnyUriItem resolvedUri = FnResolveUri.fnResolveUri(referenceUri, null, dynamicContext); 148 profile = FnDoc.fnDoc(resolvedUri, dynamicContext); 149 } 150 151 Object profileObject = INodeItem.toValue(profile); 152 153 IDocumentNodeItem retval; 154 if (profileObject instanceof Catalog) { 155 retval = profile; 156 } else { 157 // this is a profile 158 ProfileResolver resolver 159 = new ProfileResolver(dynamicContext, (uri, source) -> profile.getDocumentUri().resolve(uri)); 160 try { 161 retval = resolver.resolve(profile); 162 } catch (IOException | ProfileResolutionException ex) { 163 throw new MetapathException( 164 String.format("Unable to resolve profile '%s'. %s", 165 profile.getBaseUri(), 166 ex.getLocalizedMessage()), 167 ex); 168 } 169 } 170 return retval; 171 } 172}