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 gov.nist.secauto.metaschema.core.metapath.format.IPathFormatter;
9   import gov.nist.secauto.metaschema.core.metapath.item.node.IModelNodeItem;
10  import gov.nist.secauto.metaschema.core.util.CollectionUtil;
11  import gov.nist.secauto.metaschema.core.util.CustomCollectors;
12  import gov.nist.secauto.oscal.lib.model.Link;
13  import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem;
14  
15  import org.apache.logging.log4j.LogManager;
16  import org.apache.logging.log4j.Logger;
17  
18  import java.net.URI;
19  import java.util.List;
20  import java.util.Locale;
21  
22  import edu.umd.cs.findbugs.annotations.NonNull;
23  
24  public class LinkReferencePolicy
25      extends AbstractMultiItemTypeReferencePolicy<Link> {
26    private static final Logger LOGGER = LogManager.getLogger(LinkReferencePolicy.class);
27  
28    @SuppressWarnings("null")
29    @NonNull
30    public static LinkReferencePolicy create(@NonNull IEntityItem.ItemType itemType) {
31      return create(List.of(itemType));
32    }
33  
34    @NonNull
35    public static LinkReferencePolicy create(@NonNull List<IEntityItem.ItemType> itemTypes) {
36      return new LinkReferencePolicy(CollectionUtil.requireNonEmpty(itemTypes, "itemTypes"));
37    }
38  
39    public LinkReferencePolicy(@NonNull List<IEntityItem.ItemType> itemTypes) {
40      super(IIdentifierParser.FRAGMENT_PARSER, itemTypes);
41    }
42  
43    @Override
44    public String getReferenceText(@NonNull Link link) {
45      return link.getHref().toString();
46    }
47  
48    @Override
49    public void setReferenceText(@NonNull Link link, @NonNull String newValue) {
50      link.setHref(URI.create(newValue));
51    }
52  
53    @Override
54    protected void handleUnselected(
55        @NonNull IModelNodeItem<?, ?> contextItem,
56        @NonNull Link link,
57        @NonNull IEntityItem item,
58        @NonNull ReferenceCountingVisitor.Context visitorContext) {
59      URI linkHref = link.getHref();
60      URI sourceUri = item.getSource();
61  
62      URI resolved = sourceUri.resolve(linkHref);
63      if (LOGGER.isTraceEnabled()) {
64        LOGGER.atTrace().log("At path '{}', remapping orphaned URI '{}' to '{}'",
65            contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER),
66            linkHref.toString(),
67            resolved.toString());
68      }
69      link.setHref(resolved);
70    }
71  
72    @Override
73    protected boolean handleIndexMiss(
74        @NonNull IModelNodeItem<?, ?> contextItem,
75        @NonNull Link link,
76        @NonNull List<IEntityItem.ItemType> itemTypes,
77        @NonNull String identifier,
78        @NonNull ReferenceCountingVisitor.Context visitorContext) {
79      if (LOGGER.isWarnEnabled()) {
80        LOGGER.atWarn().log(
81            "The link at '{}' with rel '{}' should reference a {} identified by '{}'."
82                + " The index did not contain the identifier.",
83            contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER),
84            link.getRel(),
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 Link reference,
97        @NonNull ReferenceCountingVisitor.Context visitorContext) {
98      if (LOGGER.isDebugEnabled()) {
99        LOGGER.atDebug().log("Ignoring URI '{}' at '{}'",
100           reference.getHref().toString(),
101           contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER));
102     }
103 
104     return true;
105   }
106 }