1 /*
2 * Portions of this software was developed by employees of the National Institute
3 * of Standards and Technology (NIST), an agency of the Federal Government and is
4 * being made available as a public service. Pursuant to title 17 United States
5 * Code Section 105, works of NIST employees are not subject to copyright
6 * protection in the United States. This software may be subject to foreign
7 * copyright. Permission in the United States and in foreign countries, to the
8 * extent that NIST may hold copyright, to use, copy, modify, create derivative
9 * works, and distribute this software and its documentation without fee is hereby
10 * granted on a non-exclusive basis, provided that this notice and disclaimer
11 * of warranty appears in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT
19 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25 */
26
27 package gov.nist.secauto.oscal.lib.profile.resolver.policy;
28
29 import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem;
30 import gov.nist.secauto.metaschema.model.common.util.ObjectUtils;
31 import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionEvaluationException;
32 import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem;
33
34 import org.apache.logging.log4j.LogManager;
35 import org.apache.logging.log4j.Logger;
36
37 import java.util.List;
38
39 import edu.umd.cs.findbugs.annotations.NonNull;
40 import edu.umd.cs.findbugs.annotations.Nullable;
41
42 public abstract class AbstractCustomReferencePolicy<TYPE> implements ICustomReferencePolicy<TYPE> {
43 private static final Logger LOGGER = LogManager.getLogger(AbstractCustomReferencePolicy.class);
44
45 @NonNull
46 private final IIdentifierParser identifierParser;
47
48 protected AbstractCustomReferencePolicy(
49 @NonNull IIdentifierParser identifierParser) {
50 this.identifierParser = identifierParser;
51 }
52
53 @Override
54 @NonNull
55 public IIdentifierParser getIdentifierParser() {
56 return identifierParser;
57 }
58
59 /**
60 * Get the possible item types that can be searched in the order in which the
61 * identifier will be looked up.
62 * <p>
63 * The {@code reference} object is provided to allow for context sensitive item
64 * type tailoring.
65 *
66 * @param reference
67 * the reference object
68 * @return a list of item types to search for
69 */
70 @NonNull
71 protected abstract List<IEntityItem.ItemType> getEntityItemTypes(@NonNull TYPE reference);
72
73 /**
74 * Handle an index hit.
75 *
76 * @param contextItem
77 * the node containing the identifier reference
78 * @param reference
79 * the identifier reference object generating the hit
80 * @param item
81 * the referenced item
82 * @param visitorContext
83 * the reference visitor state, which can be used for further
84 * processing
85 * @return {@code true} if the hit was handled or {@code false} otherwise
86 * @throws ProfileResolutionEvaluationException
87 * if there was an error handing the index hit
88 */
89 protected boolean handleIndexHit(
90 @NonNull IRequiredValueModelNodeItem contextItem,
91 @NonNull TYPE reference,
92 @NonNull IEntityItem item,
93 @NonNull ReferenceCountingVisitor.Context visitorContext) {
94
95 if (visitorContext.getIndexer().isSelected(item)) {
96 if (!visitorContext.isResolved(item)) {
97 // this referenced item will need to be resolved
98 ReferenceCountingVisitor.instance().resolveEntity(item, visitorContext);
99 }
100 item.incrementReferenceCount();
101
102 if (item.isIdentifierReassigned()) {
103 String referenceText = ObjectUtils.notNull(getReferenceText(reference));
104 String newReferenceText = getIdentifierParser().update(referenceText, item.getIdentifier());
105 setReferenceText(reference, newReferenceText);
106 if (LOGGER.isDebugEnabled()) {
107 LOGGER.atDebug().log("Mapping {} reference '{}' to '{}'.", item.getItemType().name(), referenceText,
108 newReferenceText);
109 }
110 }
111 handleSelected(contextItem, reference, item, visitorContext);
112 } else {
113 handleUnselected(contextItem, reference, item, visitorContext);
114 }
115 return true;
116 }
117
118 /**
119 * Handle an index hit against an item related to an unselected control.
120 * <p>
121 * Subclasses can override this method to perform extra processing.
122 *
123 * @param contextItem
124 * the node containing the identifier reference
125 * @param reference
126 * the identifier reference object generating the hit
127 * @param item
128 * the referenced item
129 * @param visitorContext
130 * the reference visitor, which can be used for further processing
131 * @throws ProfileResolutionEvaluationException
132 * if there was an error handing the index hit
133 */
134 protected void handleUnselected( // NOPMD noop default
135 @NonNull IRequiredValueModelNodeItem contextItem,
136 @NonNull TYPE reference,
137 @NonNull IEntityItem item,
138 @NonNull ReferenceCountingVisitor.Context visitorContext) {
139 // do nothing by default
140 }
141
142 /**
143 * Handle an index hit against an item related to an selected control.
144 * <p>
145 * Subclasses can override this method to perform extra processing.
146 *
147 * @param contextItem
148 * the node containing the identifier reference
149 * @param reference
150 * the identifier reference object generating the hit
151 * @param item
152 * the referenced item
153 * @param visitorContext
154 * the reference visitor state, which can be used for further
155 * processing
156 * @throws ProfileResolutionEvaluationException
157 * if there was an error handing the index hit
158 */
159 protected void handleSelected( // NOPMD noop default
160 @NonNull IRequiredValueModelNodeItem contextItem,
161 @NonNull TYPE reference,
162 @NonNull IEntityItem item,
163 @NonNull ReferenceCountingVisitor.Context visitorContext) {
164 // do nothing by default
165 }
166
167 /**
168 * Handle an index miss for a reference. This occurs when the referenced item
169 * was not found in the index.
170 * <p>
171 * Subclasses can override this method to perform extra processing.
172 *
173 * @param contextItem
174 * the node containing the identifier reference
175 * @param reference
176 * the identifier reference object generating the hit
177 * @param itemTypes
178 * the possible item types for this reference
179 * @param identifier
180 * the parsed identifier
181 * @param visitorContext
182 * the reference visitor state, which can be used for further
183 * processing
184 * @return {@code true} if the reference is handled by this method or
185 * {@code false} otherwise
186 * @throws ProfileResolutionEvaluationException
187 * if there was an error handing the index miss
188 */
189 protected boolean handleIndexMiss(
190 @NonNull IRequiredValueModelNodeItem contextItem,
191 @NonNull TYPE reference,
192 @NonNull List<IEntityItem.ItemType> itemTypes,
193 @NonNull String identifier,
194 @NonNull ReferenceCountingVisitor.Context visitorContext) {
195 // provide no handler by default
196 return false;
197 }
198
199 /**
200 * Handle the case where the identifier was not a syntax match for an expected
201 * identifier. This can occur when the reference is malformed, using an
202 * unrecognized syntax.
203 * <p>
204 * Subclasses can override this method to perform extra processing.
205 *
206 * @param contextItem
207 * the node containing the identifier reference
208 * @param reference
209 * the identifier reference object generating the hit
210 * @param visitorContext
211 * the reference visitor state, which can be used for further
212 * processing
213 * @return {@code true} if the reference is handled by this method or
214 * {@code false} otherwise
215 * @throws ProfileResolutionEvaluationException
216 * if there was an error handing the index miss due to a non match
217 */
218 protected boolean handleIdentifierNonMatch(
219 @NonNull IRequiredValueModelNodeItem contextItem,
220 @NonNull TYPE reference,
221 @NonNull ReferenceCountingVisitor.Context visitorContext) {
222 // provide no handler by default
223 return false;
224 }
225
226 @Override
227 public boolean handleReference(
228 @NonNull IRequiredValueModelNodeItem contextItem,
229 @NonNull TYPE type,
230 @NonNull ReferenceCountingVisitor.Context visitorContext) {
231 String referenceText = getReferenceText(type);
232
233 // if the reference text does not exist, ignore the reference; otherwise, handle
234 // it.
235 return referenceText == null
236 || handleIdentifier(contextItem, type, getIdentifierParser().parse(referenceText), visitorContext);
237 }
238
239 /**
240 * Handle the provided {@code identifier} for a given {@code type} of reference.
241 *
242 * @param contextItem
243 * the node containing the identifier reference
244 * @param type
245 * the item type of the reference
246 * @param identifier
247 * the identifier
248 * @param visitorContext
249 * the reference visitor state, which can be used for further
250 * processing
251 * @return {@code true} if the reference is handled by this method or
252 * {@code false} otherwise
253 * @throws ProfileResolutionEvaluationException
254 * if there was an error handing the reference
255 */
256 protected boolean handleIdentifier(
257 @NonNull IRequiredValueModelNodeItem contextItem,
258 @NonNull TYPE type,
259 @Nullable String identifier,
260 @NonNull ReferenceCountingVisitor.Context visitorContext) {
261 boolean retval;
262 if (identifier == null) {
263 retval = handleIdentifierNonMatch(contextItem, type, visitorContext);
264 } else {
265 List<IEntityItem.ItemType> itemTypes = getEntityItemTypes(type);
266 IEntityItem item = null;
267 for (IEntityItem.ItemType itemType : itemTypes) {
268 assert itemType != null;
269
270 item = visitorContext.getEntity(itemType, identifier);
271 if (item != null) {
272 break;
273 }
274 }
275
276 if (item == null) {
277 retval = handleIndexMiss(contextItem, type, itemTypes, identifier, visitorContext);
278 } else {
279 retval = handleIndexHit(contextItem, type, item, visitorContext);
280 }
281 }
282 return retval;
283 }
284 }