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}