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