View Javadoc
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 }