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.item.node.IModelNodeItem;
9 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
10 import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionEvaluationException;
11 import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem;
12
13 import org.apache.logging.log4j.LogManager;
14 import org.apache.logging.log4j.Logger;
15
16 import java.util.List;
17
18 import edu.umd.cs.findbugs.annotations.NonNull;
19 import edu.umd.cs.findbugs.annotations.Nullable;
20
21 public abstract class AbstractCustomReferencePolicy<TYPE> implements ICustomReferencePolicy<TYPE> {
22 private static final Logger LOGGER = LogManager.getLogger(AbstractCustomReferencePolicy.class);
23
24 @NonNull
25 private final IIdentifierParser identifierParser;
26
27 protected AbstractCustomReferencePolicy(
28 @NonNull IIdentifierParser identifierParser) {
29 this.identifierParser = identifierParser;
30 }
31
32 @Override
33 @NonNull
34 public IIdentifierParser getIdentifierParser() {
35 return identifierParser;
36 }
37
38 /**
39 * Get the possible item types that can be searched in the order in which the
40 * identifier will be looked up.
41 * <p>
42 * The {@code reference} object is provided to allow for context sensitive item
43 * type tailoring.
44 *
45 * @param reference
46 * the reference object
47 * @return a list of item types to search for
48 */
49 @NonNull
50 protected abstract List<IEntityItem.ItemType> getEntityItemTypes(@NonNull TYPE reference);
51
52 /**
53 * Handle an index hit.
54 *
55 * @param contextItem
56 * the node containing the identifier reference
57 * @param reference
58 * the identifier reference object generating the hit
59 * @param item
60 * the referenced item
61 * @param visitorContext
62 * the reference visitor state, which can be used for further
63 * processing
64 * @return {@code true} if the hit was handled or {@code false} otherwise
65 * @throws ProfileResolutionEvaluationException
66 * if there was an error handing the index hit
67 */
68 protected boolean handleIndexHit(
69 @NonNull IModelNodeItem<?, ?> contextItem,
70 @NonNull TYPE reference,
71 @NonNull IEntityItem item,
72 @NonNull ReferenceCountingVisitor.Context visitorContext) {
73
74 if (visitorContext.getIndexer().isSelected(item)) {
75 if (!visitorContext.isResolved(item)) {
76 // this referenced item will need to be resolved
77 ReferenceCountingVisitor.instance().resolveEntity(item, visitorContext);
78 }
79 item.incrementReferenceCount();
80
81 if (item.isIdentifierReassigned()) {
82 String referenceText = ObjectUtils.notNull(getReferenceText(reference));
83 String newReferenceText = getIdentifierParser().update(referenceText, item.getIdentifier());
84 setReferenceText(reference, newReferenceText);
85 if (LOGGER.isDebugEnabled()) {
86 LOGGER.atDebug().log("Mapping {} reference '{}' to '{}'.", item.getItemType().name(), referenceText,
87 newReferenceText);
88 }
89 }
90 handleSelected(contextItem, reference, item, visitorContext);
91 } else {
92 handleUnselected(contextItem, reference, item, visitorContext);
93 }
94 return true;
95 }
96
97 /**
98 * Handle an index hit against an item related to an unselected control.
99 * <p>
100 * Subclasses can override this method to perform extra processing.
101 *
102 * @param contextItem
103 * the node containing the identifier reference
104 * @param reference
105 * the identifier reference object generating the hit
106 * @param item
107 * the referenced item
108 * @param visitorContext
109 * the reference visitor, which can be used for further processing
110 * @throws ProfileResolutionEvaluationException
111 * if there was an error handing the index hit
112 */
113 protected void handleUnselected( // NOPMD noop default
114 @NonNull IModelNodeItem<?, ?> contextItem,
115 @NonNull TYPE reference,
116 @NonNull IEntityItem item,
117 @NonNull ReferenceCountingVisitor.Context visitorContext) {
118 // do nothing by default
119 }
120
121 /**
122 * Handle an index hit against an item related to an selected control.
123 * <p>
124 * Subclasses can override this method to perform extra processing.
125 *
126 * @param contextItem
127 * the node containing the identifier reference
128 * @param reference
129 * the identifier reference object generating the hit
130 * @param item
131 * the referenced item
132 * @param visitorContext
133 * the reference visitor state, which can be used for further
134 * processing
135 * @throws ProfileResolutionEvaluationException
136 * if there was an error handing the index hit
137 */
138 protected void handleSelected( // NOPMD noop default
139 @NonNull IModelNodeItem<?, ?> contextItem,
140 @NonNull TYPE reference,
141 @NonNull IEntityItem item,
142 @NonNull ReferenceCountingVisitor.Context visitorContext) {
143 // do nothing by default
144 }
145
146 /**
147 * Handle an index miss for a reference. This occurs when the referenced item
148 * was not found in the index.
149 * <p>
150 * Subclasses can override this method to perform extra processing.
151 *
152 * @param contextItem
153 * the node containing the identifier reference
154 * @param reference
155 * the identifier reference object generating the hit
156 * @param itemTypes
157 * the possible item types for this reference
158 * @param identifier
159 * the parsed identifier
160 * @param visitorContext
161 * the reference visitor state, which can be used for further
162 * processing
163 * @return {@code true} if the reference is handled by this method or
164 * {@code false} otherwise
165 * @throws ProfileResolutionEvaluationException
166 * if there was an error handing the index miss
167 */
168 protected boolean handleIndexMiss(
169 @NonNull IModelNodeItem<?, ?> contextItem,
170 @NonNull TYPE reference,
171 @NonNull List<IEntityItem.ItemType> itemTypes,
172 @NonNull String identifier,
173 @NonNull ReferenceCountingVisitor.Context visitorContext) {
174 // provide no handler by default
175 return false;
176 }
177
178 /**
179 * Handle the case where the identifier was not a syntax match for an expected
180 * identifier. This can occur when the reference is malformed, using an
181 * unrecognized syntax.
182 * <p>
183 * Subclasses can override this method to perform extra processing.
184 *
185 * @param contextItem
186 * the node containing the identifier reference
187 * @param reference
188 * the identifier reference object generating the hit
189 * @param visitorContext
190 * the reference visitor state, which can be used for further
191 * processing
192 * @return {@code true} if the reference is handled by this method or
193 * {@code false} otherwise
194 * @throws ProfileResolutionEvaluationException
195 * if there was an error handing the index miss due to a non match
196 */
197 protected boolean handleIdentifierNonMatch(
198 @NonNull IModelNodeItem<?, ?> contextItem,
199 @NonNull TYPE reference,
200 @NonNull ReferenceCountingVisitor.Context visitorContext) {
201 // provide no handler by default
202 return false;
203 }
204
205 @Override
206 public boolean handleReference(
207 @NonNull IModelNodeItem<?, ?> contextItem,
208 @NonNull TYPE type,
209 @NonNull ReferenceCountingVisitor.Context visitorContext) {
210 String referenceText = getReferenceText(type);
211
212 // if the reference text does not exist, ignore the reference; otherwise, handle
213 // it.
214 return referenceText == null
215 || handleIdentifier(contextItem, type, getIdentifierParser().parse(referenceText), visitorContext);
216 }
217
218 /**
219 * Handle the provided {@code identifier} for a given {@code type} of reference.
220 *
221 * @param contextItem
222 * the node containing the identifier reference
223 * @param type
224 * the item type of the reference
225 * @param identifier
226 * the identifier
227 * @param visitorContext
228 * the reference visitor state, which can be used for further
229 * processing
230 * @return {@code true} if the reference is handled by this method or
231 * {@code false} otherwise
232 * @throws ProfileResolutionEvaluationException
233 * if there was an error handing the reference
234 */
235 protected boolean handleIdentifier(
236 @NonNull IModelNodeItem<?, ?> contextItem,
237 @NonNull TYPE type,
238 @Nullable String identifier,
239 @NonNull ReferenceCountingVisitor.Context visitorContext) {
240 boolean retval;
241 if (identifier == null) {
242 retval = handleIdentifierNonMatch(contextItem, type, visitorContext);
243 } else {
244 List<IEntityItem.ItemType> itemTypes = getEntityItemTypes(type);
245 IEntityItem item = null;
246 for (IEntityItem.ItemType itemType : itemTypes) {
247 assert itemType != null;
248
249 item = visitorContext.getEntity(itemType, identifier);
250 if (item != null) {
251 break;
252 }
253 }
254
255 if (item == null) {
256 retval = handleIndexMiss(contextItem, type, itemTypes, identifier, visitorContext);
257 } else {
258 retval = handleIndexHit(contextItem, type, item, visitorContext);
259 }
260 }
261 return retval;
262 }
263 }