001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package gov.nist.secauto.oscal.lib.profile.resolver.policy;
007
008import gov.nist.secauto.metaschema.core.metapath.format.IPathFormatter;
009import gov.nist.secauto.metaschema.core.metapath.item.node.IModelNodeItem;
010import gov.nist.secauto.metaschema.core.util.CollectionUtil;
011import gov.nist.secauto.metaschema.core.util.CustomCollectors;
012import gov.nist.secauto.oscal.lib.model.Link;
013import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem;
014
015import org.apache.logging.log4j.LogManager;
016import org.apache.logging.log4j.Logger;
017
018import java.net.URI;
019import java.util.List;
020import java.util.Locale;
021
022import edu.umd.cs.findbugs.annotations.NonNull;
023
024public class LinkReferencePolicy
025    extends AbstractMultiItemTypeReferencePolicy<Link> {
026  private static final Logger LOGGER = LogManager.getLogger(LinkReferencePolicy.class);
027
028  @SuppressWarnings("null")
029  @NonNull
030  public static LinkReferencePolicy create(@NonNull IEntityItem.ItemType itemType) {
031    return create(List.of(itemType));
032  }
033
034  @NonNull
035  public static LinkReferencePolicy create(@NonNull List<IEntityItem.ItemType> itemTypes) {
036    return new LinkReferencePolicy(CollectionUtil.requireNonEmpty(itemTypes, "itemTypes"));
037  }
038
039  public LinkReferencePolicy(@NonNull List<IEntityItem.ItemType> itemTypes) {
040    super(IIdentifierParser.FRAGMENT_PARSER, itemTypes);
041  }
042
043  @Override
044  public String getReferenceText(@NonNull Link link) {
045    return link.getHref().toString();
046  }
047
048  @Override
049  public void setReferenceText(@NonNull Link link, @NonNull String newValue) {
050    link.setHref(URI.create(newValue));
051  }
052
053  @Override
054  protected void handleUnselected(
055      @NonNull IModelNodeItem<?, ?> contextItem,
056      @NonNull Link link,
057      @NonNull IEntityItem item,
058      @NonNull ReferenceCountingVisitor.Context visitorContext) {
059    URI linkHref = link.getHref();
060    URI sourceUri = item.getSource();
061
062    URI resolved = sourceUri.resolve(linkHref);
063    if (LOGGER.isTraceEnabled()) {
064      LOGGER.atTrace().log("At path '{}', remapping orphaned URI '{}' to '{}'",
065          contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER),
066          linkHref.toString(),
067          resolved.toString());
068    }
069    link.setHref(resolved);
070  }
071
072  @Override
073  protected boolean handleIndexMiss(
074      @NonNull IModelNodeItem<?, ?> contextItem,
075      @NonNull Link link,
076      @NonNull List<IEntityItem.ItemType> itemTypes,
077      @NonNull String identifier,
078      @NonNull ReferenceCountingVisitor.Context visitorContext) {
079    if (LOGGER.isWarnEnabled()) {
080      LOGGER.atWarn().log(
081          "The link at '{}' with rel '{}' should reference a {} identified by '{}'."
082              + " The index did not contain the identifier.",
083          contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER),
084          link.getRel(),
085          itemTypes.stream()
086              .map(en -> en.name().toLowerCase(Locale.ROOT))
087              .collect(CustomCollectors.joiningWithOxfordComma("or")),
088          identifier);
089    }
090    return true;
091  }
092
093  @Override
094  protected boolean handleIdentifierNonMatch(
095      @NonNull IModelNodeItem<?, ?> contextItem,
096      @NonNull Link reference,
097      @NonNull ReferenceCountingVisitor.Context visitorContext) {
098    if (LOGGER.isDebugEnabled()) {
099      LOGGER.atDebug().log("Ignoring URI '{}' at '{}'",
100          reference.getHref().toString(),
101          contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER));
102    }
103
104    return true;
105  }
106}