1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.oscal.lib.profile.resolver.policy;
7   
8   import org.apache.logging.log4j.LogManager;
9   import org.apache.logging.log4j.Logger;
10  
11  import java.net.URI;
12  import java.util.List;
13  import java.util.Locale;
14  
15  import dev.metaschema.core.metapath.format.IPathFormatter;
16  import dev.metaschema.core.metapath.item.node.IModelNodeItem;
17  import dev.metaschema.core.util.CustomCollectors;
18  import dev.metaschema.core.util.ObjectUtils;
19  import dev.metaschema.oscal.lib.model.Property;
20  import dev.metaschema.oscal.lib.profile.resolver.support.IEntityItem;
21  import edu.umd.cs.findbugs.annotations.NonNull;
22  
23  public class PropertyReferencePolicy
24      extends AbstractMultiItemTypeReferencePolicy<Property> {
25    private static final Logger LOGGER = LogManager.getLogger(PropertyReferencePolicy.class);
26  
27    @NonNull
28    public static PropertyReferencePolicy create(@NonNull IIdentifierParser identifierParser,
29        @NonNull IEntityItem.ItemType itemType) {
30      return create(identifierParser, ObjectUtils.notNull(List.of(itemType)));
31    }
32  
33    @NonNull
34    public static PropertyReferencePolicy create(@NonNull IIdentifierParser identifierParser,
35        @NonNull List<IEntityItem.ItemType> itemTypes) {
36      return new PropertyReferencePolicy(identifierParser, itemTypes);
37    }
38  
39    public PropertyReferencePolicy(
40        @NonNull IIdentifierParser identifierParser,
41        @NonNull List<IEntityItem.ItemType> itemTypes) {
42      super(identifierParser, itemTypes);
43    }
44  
45    @Override
46    public String getReferenceText(@NonNull Property property) {
47      return property.getValue();
48    }
49  
50    @Override
51    public void setReferenceText(@NonNull Property property, @NonNull String newValue) {
52      property.setValue(newValue);
53    }
54  
55    @Override
56    protected void handleUnselected(
57        @NonNull IModelNodeItem<?, ?> contextItem,
58        @NonNull Property property,
59        @NonNull IEntityItem item,
60        @NonNull ReferenceCountingVisitor.Context visitorContext) {
61      URI linkHref = URI.create(property.getValue());
62      URI sourceUri = item.getSource();
63  
64      URI resolved = sourceUri.resolve(linkHref);
65      if (LOGGER.isDebugEnabled()) {
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      property.setValue(resolved.toString());
72    }
73  
74    @Override
75    protected boolean handleIndexMiss(
76        @NonNull IModelNodeItem<?, ?> contextItem,
77        @NonNull Property property,
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 property '{}' at '{}' should reference a {} identified by '{}',"
84                + " but the identifier was not found in the index.",
85            property.getQName(),
86            contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER),
87            itemTypes.stream()
88                .map(en -> en.name().toLowerCase(Locale.ROOT))
89                .collect(CustomCollectors.joiningWithOxfordComma("or")),
90            identifier);
91      }
92      return true;
93    }
94  }