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}