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