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.item.IItem;
016import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
017import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
018import gov.nist.secauto.metaschema.core.util.ObjectUtils;
019import gov.nist.secauto.oscal.lib.OscalModelConstants;
020import gov.nist.secauto.oscal.lib.model.Catalog;
021import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionException;
022import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolver;
023
024import java.io.IOException;
025import java.util.List;
026
027import edu.umd.cs.findbugs.annotations.NonNull;
028
029public final class ResolveProfile {
030
031  @NonNull
032  static final IFunction SIGNATURE_NO_ARG = IFunction.builder()
033      .name("resolve-profile")
034      .namespace(OscalModelConstants.NS_OSCAL)
035      .returnType(INodeItem.class)
036      .focusDependent()
037      .contextDependent()
038      .deterministic()
039      .returnOne()
040      .functionHandler(ResolveProfile::executeNoArg)
041      .build();
042
043  @NonNull
044  static final IFunction SIGNATURE_ONE_ARG = IFunction.builder()
045      .name("resolve-profile")
046      .namespace(OscalModelConstants.NS_OSCAL)
047      .argument(IArgument.builder()
048          .name("profile")
049          .type(INodeItem.class)
050          .zeroOrOne()
051          .build())
052      .focusDependent()
053      .contextDependent()
054      .deterministic()
055      .returnType(INodeItem.class)
056      .returnOne()
057      .functionHandler(ResolveProfile::executeOneArg)
058      .build();
059
060  @NonNull
061  static final IFunction SIGNATURE_NO_ARG_METAPATH = IFunction.builder()
062      .name("resolve-profile")
063      .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS)
064      .returnType(INodeItem.class)
065      .focusDependent()
066      .contextDependent()
067      .deterministic()
068      .returnOne()
069      .functionHandler(ResolveProfile::executeNoArg)
070      .build();
071
072  @NonNull
073  static final IFunction SIGNATURE_ONE_ARG_METAPATH = IFunction.builder()
074      .name("resolve-profile")
075      .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS)
076      .argument(IArgument.builder()
077          .name("profile")
078          .type(INodeItem.class)
079          .zeroOrOne()
080          .build())
081      .focusDependent()
082      .contextDependent()
083      .deterministic()
084      .returnType(INodeItem.class)
085      .returnOne()
086      .functionHandler(ResolveProfile::executeOneArg)
087      .build();
088
089  private ResolveProfile() {
090    // disable construction
091  }
092
093  @SuppressWarnings({ "unused",
094      "PMD.OnlyOneReturn" // readability
095  })
096  @NonNull
097  public static ISequence<?> executeNoArg(
098      @NonNull IFunction function,
099      @NonNull List<ISequence<?>> arguments,
100      @NonNull DynamicContext dynamicContext,
101      IItem focus) {
102
103    if (focus == null) {
104      return ISequence.empty();
105    }
106    return ISequence.of(resolveProfile(FunctionUtils.asType(focus), dynamicContext));
107  }
108
109  @SuppressWarnings({ "unused",
110      "PMD.OnlyOneReturn" // readability
111  })
112  @NonNull
113  public static ISequence<?> executeOneArg(
114      @NonNull IFunction function,
115      @NonNull List<ISequence<?>> arguments,
116      @NonNull DynamicContext dynamicContext,
117      IItem focus) {
118    ISequence<? extends IDocumentNodeItem> arg = FunctionUtils.asType(
119        ObjectUtils.notNull(arguments.get(0)));
120
121    IItem item = arg.getFirstItem(true);
122    if (item == null) {
123      return ISequence.empty();
124    }
125
126    return ISequence.of(resolveProfile(FunctionUtils.asType(item), dynamicContext));
127  }
128
129  @NonNull
130  public static IDocumentNodeItem resolveProfile(@NonNull IDocumentNodeItem profile,
131      @NonNull DynamicContext dynamicContext) {
132    Object profileObject = INodeItem.toValue(profile);
133
134    IDocumentNodeItem retval;
135    if (profileObject instanceof Catalog) {
136      retval = profile;
137    } else {
138      // this is a profile
139      ProfileResolver resolver = new ProfileResolver();
140      resolver.setDynamicContext(dynamicContext);
141      try {
142        retval = resolver.resolve(profile);
143      } catch (IOException | ProfileResolutionException ex) {
144        throw new MetapathException(String.format("Fun: Unable to resolve profile '%s'", profile.getBaseUri()), ex);
145      }
146    }
147    return retval;
148  }
149}