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.metaschema.core.util.ObjectUtils; 016import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem; 017 018import org.apache.logging.log4j.LogManager; 019import org.apache.logging.log4j.Logger; 020 021import java.net.URI; 022import java.util.List; 023import java.util.Locale; 024 025import edu.umd.cs.findbugs.annotations.NonNull; 026 027public class AnchorReferencePolicy 028 extends AbstractCustomReferencePolicy<InlineLinkNode> { 029 private static final Logger LOGGER = LogManager.getLogger(AnchorReferencePolicy.class); 030 031 public AnchorReferencePolicy() { 032 super(IIdentifierParser.FRAGMENT_PARSER); 033 } 034 035 @SuppressWarnings("null") 036 @Override 037 protected List<IEntityItem.ItemType> getEntityItemTypes(@NonNull InlineLinkNode link) { 038 return List.of( 039 IEntityItem.ItemType.RESOURCE, 040 IEntityItem.ItemType.CONTROL, 041 IEntityItem.ItemType.GROUP, 042 IEntityItem.ItemType.PART); 043 } 044 045 @Override 046 public String getReferenceText(@NonNull InlineLinkNode link) { 047 return link.getUrl().toString(); 048 } 049 050 @Override 051 public void setReferenceText(@NonNull InlineLinkNode link, @NonNull String newValue) { 052 link.setUrl(BasedSequence.of(newValue)); 053 } 054 055 @Override 056 protected void handleUnselected( 057 @NonNull IModelNodeItem<?, ?> contextItem, 058 @NonNull InlineLinkNode link, 059 @NonNull IEntityItem item, 060 @NonNull ReferenceCountingVisitor.Context visitorContext) { 061 URI linkHref = ObjectUtils.notNull(URI.create(link.getUrl().toString())); 062 URI sourceUri = item.getSource(); 063 064 URI resolved = visitorContext.getUriResolver().resolve(linkHref, sourceUri); 065 if (LOGGER.isTraceEnabled()) { 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 link.setUrl(CharSubSequence.of(resolved.toString())); 072 } 073 074 @Override 075 protected boolean handleIndexMiss( 076 @NonNull IModelNodeItem<?, ?> contextItem, 077 @NonNull InlineLinkNode reference, 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 anchor at '{}' should reference a {} identified by '{}', but the identifier was not found in the index.", 084 contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER), 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 InlineLinkNode reference, 097 @NonNull ReferenceCountingVisitor.Context visitorContext) { 098 if (LOGGER.isDebugEnabled()) { 099 LOGGER.atDebug().log("Ignoring URI '{}' at '{}'", 100 reference.getUrl().toStringOrNull(), 101 contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER)); 102 } 103 104 return true; 105 } 106}