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 com.vladsch.flexmark.ast.InlineLinkNode; 009import com.vladsch.flexmark.util.sequence.BasedSequence; 010import com.vladsch.flexmark.util.sequence.CharSubSequence; 011 012import gov.nist.secauto.metaschema.core.metapath.format.IPathFormatter; 013import gov.nist.secauto.metaschema.core.metapath.item.node.IModelNodeItem; 014import gov.nist.secauto.metaschema.core.util.CustomCollectors; 015import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem; 016 017import org.apache.logging.log4j.LogManager; 018import org.apache.logging.log4j.Logger; 019 020import java.net.URI; 021import java.util.List; 022import java.util.Locale; 023 024import edu.umd.cs.findbugs.annotations.NonNull; 025 026public class AnchorReferencePolicy 027 extends AbstractCustomReferencePolicy<InlineLinkNode> { 028 private static final Logger LOGGER = LogManager.getLogger(AnchorReferencePolicy.class); 029 030 public AnchorReferencePolicy() { 031 super(IIdentifierParser.FRAGMENT_PARSER); 032 } 033 034 @SuppressWarnings("null") 035 @Override 036 protected List<IEntityItem.ItemType> getEntityItemTypes(@NonNull InlineLinkNode link) { 037 return List.of( 038 IEntityItem.ItemType.RESOURCE, 039 IEntityItem.ItemType.CONTROL, 040 IEntityItem.ItemType.GROUP, 041 IEntityItem.ItemType.PART); 042 } 043 044 @Override 045 public String getReferenceText(@NonNull InlineLinkNode link) { 046 return link.getUrl().toString(); 047 } 048 049 @Override 050 public void setReferenceText(@NonNull InlineLinkNode link, @NonNull String newValue) { 051 link.setUrl(BasedSequence.of(newValue)); 052 } 053 054 @Override 055 protected void handleUnselected( 056 @NonNull IModelNodeItem<?, ?> contextItem, 057 @NonNull InlineLinkNode link, 058 @NonNull IEntityItem item, 059 @NonNull ReferenceCountingVisitor.Context visitorContext) { 060 URI linkHref = URI.create(link.getUrl().toString()); 061 URI sourceUri = item.getSource(); 062 063 URI resolved = sourceUri.resolve(linkHref); 064 if (LOGGER.isTraceEnabled()) { 065 LOGGER.atTrace().log("At path '{}', remapping orphaned URI '{}' to '{}'", 066 contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER), 067 linkHref.toString(), 068 resolved.toString()); 069 } 070 link.setUrl(CharSubSequence.of(resolved.toString())); 071 } 072 073 @Override 074 protected boolean handleIndexMiss( 075 @NonNull IModelNodeItem<?, ?> contextItem, 076 @NonNull InlineLinkNode reference, 077 @NonNull List<IEntityItem.ItemType> itemTypes, 078 @NonNull String identifier, 079 @NonNull ReferenceCountingVisitor.Context visitorContext) { 080 if (LOGGER.isErrorEnabled()) { 081 LOGGER.atError().log( 082 "The anchor at '{}' should reference a {} identified by '{}', but the identifier was not found in the index.", 083 contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER), 084 itemTypes.stream() 085 .map(en -> en.name().toLowerCase(Locale.ROOT)) 086 .collect(CustomCollectors.joiningWithOxfordComma("or")), 087 identifier); 088 } 089 return true; 090 } 091 092 @Override 093 protected boolean handleIdentifierNonMatch( 094 @NonNull IModelNodeItem<?, ?> contextItem, 095 @NonNull InlineLinkNode reference, 096 @NonNull ReferenceCountingVisitor.Context visitorContext) { 097 if (LOGGER.isDebugEnabled()) { 098 LOGGER.atDebug().log("Ignoring URI '{}' at '{}'", 099 reference.getUrl().toStringOrNull(), 100 contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER)); 101 } 102 103 return true; 104 } 105}