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.ISequence; 010import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; 011import gov.nist.secauto.metaschema.core.metapath.MetapathException; 012import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; 013import gov.nist.secauto.metaschema.core.metapath.function.IArgument; 014import gov.nist.secauto.metaschema.core.metapath.function.IFunction; 015import gov.nist.secauto.metaschema.core.metapath.function.InvalidTypeFunctionException; 016import gov.nist.secauto.metaschema.core.metapath.item.IItem; 017import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem; 018import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; 019import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; 020import gov.nist.secauto.metaschema.core.metapath.item.node.IAssemblyNodeItem; 021import gov.nist.secauto.metaschema.core.metapath.item.node.IFlagNodeItem; 022import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; 023import gov.nist.secauto.metaschema.core.model.IFlagInstance; 024import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; 025import gov.nist.secauto.metaschema.core.util.ObjectUtils; 026import gov.nist.secauto.oscal.lib.OscalModelConstants; 027import gov.nist.secauto.oscal.lib.model.metadata.AbstractProperty; 028 029import java.net.URI; 030import java.util.List; 031 032import edu.umd.cs.findbugs.annotations.NonNull; 033 034public final class HasOscalNamespace { 035 @NonNull 036 private static final IEnhancedQName NS_FLAG_QNAME = IEnhancedQName.of("ns"); 037 @NonNull 038 static final IFunction SIGNATURE_ONE_ARG = IFunction.builder() 039 .name("has-oscal-namespace") 040 .namespace(OscalModelConstants.NS_OSCAL) 041 .argument(IArgument.builder() 042 .name("namespace") 043 .type(IStringItem.type()) 044 .oneOrMore() 045 .build()) 046 .allowUnboundedArity(true) 047 .returnType(IBooleanItem.type()) 048 .focusDependent() 049 .contextIndependent() 050 .deterministic() 051 .returnOne() 052 .functionHandler(HasOscalNamespace::executeOneArg) 053 .build(); 054 055 @NonNull 056 static final IFunction SIGNATURE_TWO_ARGS = IFunction.builder() 057 .name("has-oscal-namespace") 058 .namespace(OscalModelConstants.NS_OSCAL) 059 .argument(IArgument.builder() 060 .name("propOrPart") 061 .type(IAssemblyNodeItem.type()) 062 .one() 063 .build()) 064 .argument(IArgument.builder() 065 .name("namespace") 066 .type(IStringItem.type()) 067 .oneOrMore() 068 .build()) 069 .allowUnboundedArity(true) 070 .focusIndependent() 071 .contextIndependent() 072 .deterministic() 073 .returnType(IBooleanItem.type()) 074 .returnOne() 075 .functionHandler(HasOscalNamespace::executeTwoArg) 076 .build(); 077 078 @NonNull 079 static final IFunction SIGNATURE_ONE_ARG_METAPATH = IFunction.builder() 080 .name("has-oscal-namespace") 081 .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS) 082 .argument(IArgument.builder() 083 .name("namespace") 084 .type(IStringItem.type()) 085 .oneOrMore() 086 .build()) 087 .allowUnboundedArity(true) 088 .returnType(IBooleanItem.type()) 089 .focusDependent() 090 .contextIndependent() 091 .deterministic() 092 .returnOne() 093 .functionHandler(HasOscalNamespace::executeOneArg) 094 .build(); 095 096 @NonNull 097 static final IFunction SIGNATURE_TWO_ARGS_METAPATH = IFunction.builder() 098 .name("has-oscal-namespace") 099 .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS) 100 .argument(IArgument.builder() 101 .name("propOrPart") 102 .type(IAssemblyNodeItem.type()) 103 .one() 104 .build()) 105 .argument(IArgument.builder() 106 .name("namespace") 107 .type(IStringItem.type()) 108 .oneOrMore() 109 .build()) 110 .allowUnboundedArity(true) 111 .focusIndependent() 112 .contextIndependent() 113 .deterministic() 114 .returnType(IBooleanItem.type()) 115 .returnOne() 116 .functionHandler(HasOscalNamespace::executeTwoArg) 117 .build(); 118 119 private HasOscalNamespace() { 120 // disable construction 121 } 122 123 @SuppressWarnings({ "unused", 124 "PMD.OnlyOneReturn" // readability 125 }) 126 @NonNull 127 public static ISequence<?> executeOneArg( 128 @NonNull IFunction function, 129 @NonNull List<ISequence<?>> arguments, 130 @NonNull DynamicContext dynamicContext, 131 IItem focus) { 132 assert arguments.size() == 1; 133 ISequence<? extends IStringItem> namespaceArgs = FunctionUtils.asType( 134 ObjectUtils.notNull(arguments.get(0))); 135 136 if (namespaceArgs.isEmpty()) { 137 return ISequence.empty(); 138 } 139 140 IAssemblyNodeItem node = FunctionUtils.requireType(IAssemblyNodeItem.class, focus); 141 return ISequence.of(hasNamespace(FunctionUtils.asType(node), namespaceArgs)); 142 } 143 144 @SuppressWarnings({ "unused", 145 "PMD.OnlyOneReturn" // readability 146 }) 147 @NonNull 148 public static ISequence<?> executeTwoArg( 149 @NonNull IFunction function, 150 @NonNull List<ISequence<?>> arguments, 151 @NonNull DynamicContext dynamicContext, 152 IItem focus) { 153 assert arguments.size() == 2; 154 155 ISequence<? extends IStringItem> namespaceArgs = FunctionUtils.asType( 156 ObjectUtils.notNull(arguments.get(1))); 157 if (namespaceArgs.isEmpty()) { 158 return ISequence.empty(); 159 } 160 161 ISequence<? extends IAssemblyNodeItem> nodeSequence = FunctionUtils.asType( 162 ObjectUtils.notNull(arguments.get(0))); 163 164 // always not null, since the first item is required 165 IAssemblyNodeItem node = FunctionUtils.asType(ObjectUtils.requireNonNull(nodeSequence.getFirstItem(true))); 166 return ISequence.of(hasNamespace(node, namespaceArgs)); 167 } 168 169 @SuppressWarnings("PMD.LinguisticNaming") // false positive 170 @NonNull 171 public static IBooleanItem hasNamespace( 172 @NonNull IAssemblyNodeItem propOrPart, 173 @NonNull ISequence<? extends IStringItem> namespaces) { 174 Object propOrPartObject = propOrPart.getValue(); 175 if (propOrPartObject == null) { 176 throw new InvalidTypeFunctionException(InvalidTypeFunctionException.NODE_HAS_NO_TYPED_VALUE, propOrPart); 177 } 178 179 URI nodeNamespace = null; 180 // get the "ns" flag value 181 IFlagNodeItem ns = propOrPart.getFlagByName(NS_FLAG_QNAME); 182 if (ns == null) { 183 // check if the node actually has a "ns" flag 184 IAssemblyDefinition definition = propOrPart.getDefinition(); 185 IFlagInstance flag = definition.getFlagInstanceByName(NS_FLAG_QNAME.getIndexPosition()); 186 if (flag == null) { 187 throw new MetapathException( 188 String.format( 189 "Node at path '%s' bound to '%s' based on the assembly definition '%s' has no OSCAL namespace", 190 propOrPart.getMetapath(), 191 propOrPart.getClass().getName(), 192 propOrPart.getDefinition().getName())); 193 194 } 195 196 Object defaultValue = flag.getDefinition().getDefaultValue(); 197 if (defaultValue != null) { 198 nodeNamespace = IAnyUriItem.valueOf(ObjectUtils.notNull(defaultValue.toString())).asUri(); 199 } 200 } else { 201 nodeNamespace = IAnyUriItem.cast(ObjectUtils.notNull(ns.toAtomicItem())).asUri(); 202 } 203 204 String nodeNamespaceString = AbstractProperty.normalizeNamespace(nodeNamespace).toString(); 205 return IBooleanItem.valueOf(namespaces.stream() 206 .map(node -> nodeNamespaceString.equals(node.asString())) 207 .anyMatch(bool -> bool)); 208 } 209}