1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.oscal.lib.profile.resolver.policy;
7   
8   import com.vladsch.flexmark.ast.InlineLinkNode;
9   import com.vladsch.flexmark.util.sequence.BasedSequence;
10  import com.vladsch.flexmark.util.sequence.CharSubSequence;
11  
12  import gov.nist.secauto.metaschema.core.metapath.format.IPathFormatter;
13  import gov.nist.secauto.metaschema.core.metapath.item.node.IModelNodeItem;
14  import gov.nist.secauto.metaschema.core.util.CustomCollectors;
15  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
16  import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem;
17  
18  import org.apache.logging.log4j.LogManager;
19  import org.apache.logging.log4j.Logger;
20  
21  import java.net.URI;
22  import java.util.List;
23  import java.util.Locale;
24  
25  import edu.umd.cs.findbugs.annotations.NonNull;
26  
27  public class AnchorReferencePolicy
28      extends AbstractCustomReferencePolicy<InlineLinkNode> {
29    private static final Logger LOGGER = LogManager.getLogger(AnchorReferencePolicy.class);
30  
31    public AnchorReferencePolicy() {
32      super(IIdentifierParser.FRAGMENT_PARSER);
33    }
34  
35    @SuppressWarnings("null")
36    @Override
37    protected List<IEntityItem.ItemType> getEntityItemTypes(@NonNull InlineLinkNode link) {
38      return List.of(
39          IEntityItem.ItemType.RESOURCE,
40          IEntityItem.ItemType.CONTROL,
41          IEntityItem.ItemType.GROUP,
42          IEntityItem.ItemType.PART);
43    }
44  
45    @Override
46    public String getReferenceText(@NonNull InlineLinkNode link) {
47      return link.getUrl().toString();
48    }
49  
50    @Override
51    public void setReferenceText(@NonNull InlineLinkNode link, @NonNull String newValue) {
52      link.setUrl(BasedSequence.of(newValue));
53    }
54  
55    @Override
56    protected void handleUnselected(
57        @NonNull IModelNodeItem<?, ?> contextItem,
58        @NonNull InlineLinkNode link,
59        @NonNull IEntityItem item,
60        @NonNull ReferenceCountingVisitor.Context visitorContext) {
61      URI linkHref = ObjectUtils.notNull(URI.create(link.getUrl().toString()));
62      URI sourceUri = item.getSource();
63  
64      URI resolved = visitorContext.getUriResolver().resolve(linkHref, sourceUri);
65      if (LOGGER.isTraceEnabled()) {
66        LOGGER.atTrace().log("At path '{}', remapping orphaned URI '{}' to '{}'",
67            contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER),
68            linkHref.toString(),
69            resolved.toString());
70      }
71      link.setUrl(CharSubSequence.of(resolved.toString()));
72    }
73  
74    @Override
75    protected boolean handleIndexMiss(
76        @NonNull IModelNodeItem<?, ?> contextItem,
77        @NonNull InlineLinkNode reference,
78        @NonNull List<IEntityItem.ItemType> itemTypes,
79        @NonNull String identifier,
80        @NonNull ReferenceCountingVisitor.Context visitorContext) {
81      if (LOGGER.isWarnEnabled()) {
82        LOGGER.atWarn().log(
83            "The anchor at '{}' should reference a {} identified by '{}', but the identifier was not found in the index.",
84            contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER),
85            itemTypes.stream()
86                .map(en -> en.name().toLowerCase(Locale.ROOT))
87                .collect(CustomCollectors.joiningWithOxfordComma("or")),
88            identifier);
89      }
90      return true;
91    }
92  
93    @Override
94    protected boolean handleIdentifierNonMatch(
95        @NonNull IModelNodeItem<?, ?> contextItem,
96        @NonNull InlineLinkNode reference,
97        @NonNull ReferenceCountingVisitor.Context visitorContext) {
98      if (LOGGER.isDebugEnabled()) {
99        LOGGER.atDebug().log("Ignoring URI '{}' at '{}'",
100           reference.getUrl().toStringOrNull(),
101           contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER));
102     }
103 
104     return true;
105   }
106 }