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.CustomCollectors;
011import gov.nist.secauto.metaschema.core.util.ObjectUtils;
012import gov.nist.secauto.oscal.lib.model.Property;
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 PropertyReferencePolicy
025    extends AbstractMultiItemTypeReferencePolicy<Property> {
026  private static final Logger LOGGER = LogManager.getLogger(PropertyReferencePolicy.class);
027
028  @NonNull
029  public static PropertyReferencePolicy create(@NonNull IIdentifierParser identifierParser,
030      @NonNull IEntityItem.ItemType itemType) {
031    return create(identifierParser, ObjectUtils.notNull(List.of(itemType)));
032  }
033
034  @NonNull
035  public static PropertyReferencePolicy create(@NonNull IIdentifierParser identifierParser,
036      @NonNull List<IEntityItem.ItemType> itemTypes) {
037    return new PropertyReferencePolicy(identifierParser, itemTypes);
038  }
039
040  public PropertyReferencePolicy(
041      @NonNull IIdentifierParser identifierParser,
042      @NonNull List<IEntityItem.ItemType> itemTypes) {
043    super(identifierParser, itemTypes);
044  }
045
046  @Override
047  public String getReferenceText(@NonNull Property property) {
048    return property.getValue();
049  }
050
051  @Override
052  public void setReferenceText(@NonNull Property property, @NonNull String newValue) {
053    property.setValue(newValue);
054  }
055
056  @Override
057  protected void handleUnselected(
058      @NonNull IModelNodeItem<?, ?> contextItem,
059      @NonNull Property property,
060      @NonNull IEntityItem item,
061      @NonNull ReferenceCountingVisitor.Context visitorContext) {
062    URI linkHref = URI.create(property.getValue());
063    URI sourceUri = item.getSource();
064
065    URI resolved = sourceUri.resolve(linkHref);
066    if (LOGGER.isDebugEnabled()) {
067      LOGGER.atTrace().log("At path '{}', remapping orphaned URI '{}' to '{}'",
068          contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER),
069          linkHref.toString(),
070          resolved.toString());
071    }
072    property.setValue(resolved.toString());
073  }
074
075  @Override
076  protected boolean handleIndexMiss(
077      @NonNull IModelNodeItem<?, ?> contextItem,
078      @NonNull Property property,
079      @NonNull List<IEntityItem.ItemType> itemTypes,
080      @NonNull String identifier,
081      @NonNull ReferenceCountingVisitor.Context visitorContext) {
082    if (LOGGER.isWarnEnabled()) {
083      LOGGER.atWarn().log(
084          "The property '{}' at '{}' should reference a {} identified by '{}',"
085              + " but the identifier was not found in the index.",
086          property.getQName(),
087          contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER),
088          itemTypes.stream()
089              .map(en -> en.name().toLowerCase(Locale.ROOT))
090              .collect(CustomCollectors.joiningWithOxfordComma("or")),
091          identifier);
092    }
093    return true;
094  }
095}