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.support;
28  
29  import gov.nist.secauto.metaschema.model.common.metapath.MetapathExpression;
30  import gov.nist.secauto.metaschema.model.common.metapath.item.IDocumentNodeItem;
31  import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem;
32  import gov.nist.secauto.metaschema.model.common.metapath.item.IRootAssemblyNodeItem;
33  import gov.nist.secauto.metaschema.model.common.util.CollectionUtil;
34  import gov.nist.secauto.metaschema.model.common.util.ObjectUtils;
35  
36  import java.util.Collections;
37  import java.util.EnumSet;
38  import java.util.Set;
39  
40  import edu.umd.cs.findbugs.annotations.NonNull;
41  
42  /**
43   * Visits a catalog document and its children as designated.
44   * <p>
45   * This implementation is stateless. The {@code T} parameter can be used to
46   * convey state as needed.
47   *
48   * @param <T>
49   *          the state type
50   * @param <R>
51   *          the result type
52   */
53  public abstract class AbstractCatalogEntityVisitor<T, R>
54      extends AbstractCatalogVisitor<T, R> {
55    @NonNull
56    public static final MetapathExpression CHILD_PART_METAPATH
57        = MetapathExpression.compile("part|part//part");
58    @NonNull
59    private static final MetapathExpression BACK_MATTER_RESOURCES_METAPATH
60        = MetapathExpression.compile("back-matter/resource");
61    @NonNull
62    private static final Set<IEntityItem.ItemType> GROUP_CONTAINER_TYPES
63        = ObjectUtils.notNull(EnumSet.of(
64            IEntityItem.ItemType.GROUP,
65            IEntityItem.ItemType.CONTROL,
66            IEntityItem.ItemType.PARAMETER,
67            IEntityItem.ItemType.PART));
68    @NonNull
69    private static final Set<IEntityItem.ItemType> CONTROL_CONTAINER_TYPES
70        = ObjectUtils.notNull(EnumSet.of(
71            IEntityItem.ItemType.CONTROL,
72            IEntityItem.ItemType.PARAMETER,
73            IEntityItem.ItemType.PART));
74    @NonNull
75    private final Set<IEntityItem.ItemType> itemTypesToVisit;
76  
77    /**
78     * Create a new visitor that will visit the item types identified by
79     * {@code itemTypesToVisit}.
80     *
81     * @param itemTypesToVisit
82     *          the item type the visitor will visit
83     */
84    public AbstractCatalogEntityVisitor(@NonNull Set<IEntityItem.ItemType> itemTypesToVisit) {
85      this.itemTypesToVisit = CollectionUtil.unmodifiableSet(itemTypesToVisit);
86    }
87  
88    public Set<IEntityItem.ItemType> getItemTypesToVisit() {
89      return CollectionUtil.unmodifiableSet(itemTypesToVisit);
90    }
91  
92    protected boolean isVisitedItemType(@NonNull IEntityItem.ItemType type) {
93      return itemTypesToVisit.contains(type);
94    }
95  
96    @Override
97    public R visitCatalog(IDocumentNodeItem catalogDocument, T state) {
98      R result = super.visitCatalog(catalogDocument, state);
99  
100     IRootAssemblyNodeItem root = catalogDocument.getRootAssemblyNodeItem();
101     visitMetadata(root, state);
102     visitBackMatter(root, state);
103     return result;
104   }
105 
106   @Override
107   protected R visitGroupContainer(IRequiredValueModelNodeItem catalogOrGroup, R initialResult, T state) {
108     R retval;
109     if (Collections.disjoint(getItemTypesToVisit(), GROUP_CONTAINER_TYPES)) {
110       retval = initialResult;
111     } else {
112       retval = super.visitGroupContainer(catalogOrGroup, initialResult, state);
113     }
114     return retval;
115   }
116 
117   @Override
118   protected R visitControlContainer(IRequiredValueModelNodeItem catalogOrGroupOrControl, R initialResult, T state) {
119     R retval;
120     if (Collections.disjoint(getItemTypesToVisit(), CONTROL_CONTAINER_TYPES)) {
121       retval = initialResult;
122     } else {
123       // first descend to all control container children
124       retval = super.visitControlContainer(catalogOrGroupOrControl, initialResult, state);
125 
126       // handle parameters
127       if (isVisitedItemType(IEntityItem.ItemType.PARAMETER)) {
128         retval = catalogOrGroupOrControl.getModelItemsByName("param").stream()
129             .map(paramItem -> {
130               return visitParameter(ObjectUtils.requireNonNull(paramItem), catalogOrGroupOrControl, state);
131             })
132             .reduce(retval, (first, second) -> aggregateResults(first, second, state));
133       } // TODO Auto-generated method stub
134     }
135     return retval;
136   }
137 
138   protected void visitParts(@NonNull IRequiredValueModelNodeItem groupOrControlItem, T state) {
139     // handle parts
140     if (isVisitedItemType(IEntityItem.ItemType.PART)) {
141       CHILD_PART_METAPATH.evaluate(groupOrControlItem).asStream()
142           .map(item -> (IRequiredValueModelNodeItem) item)
143           .forEachOrdered(partItem -> {
144             visitPart(ObjectUtils.requireNonNull(partItem), groupOrControlItem, state);
145           });
146     }
147   }
148 
149   @Override
150   protected R visitGroupInternal(@NonNull IRequiredValueModelNodeItem item, R childResult, T state) {
151     if (isVisitedItemType(IEntityItem.ItemType.PART)) {
152       visitParts(item, state);
153     }
154 
155     R retval = childResult;
156     if (isVisitedItemType(IEntityItem.ItemType.GROUP)) {
157       retval = visitGroup(item, retval, state);
158     }
159     return retval;
160   }
161 
162   @Override
163   protected R visitControlInternal(IRequiredValueModelNodeItem item, R childResult, T state) {
164     if (isVisitedItemType(IEntityItem.ItemType.PART)) {
165       visitParts(item, state);
166     }
167 
168     R retval = childResult;
169     if (isVisitedItemType(IEntityItem.ItemType.CONTROL)) {
170       retval = visitControl(item, retval, state);
171     }
172     return retval;
173   }
174 
175   /**
176    * Called when visiting a parameter.
177    * <p>
178    * Can be overridden by classes extending this interface to support processing
179    * of the visited object.
180    *
181    * @param item
182    *          the Metapath item for the parameter
183    * @param catalogOrGroupOrControl
184    *          the parameter's parent Metapath item
185    * @param state
186    *          the calling context information
187    * @return a meaningful result of the given type
188    */
189   protected R visitParameter(
190       @NonNull IRequiredValueModelNodeItem item,
191       @NonNull IRequiredValueModelNodeItem catalogOrGroupOrControl,
192       T state) {
193     // do nothing
194     return newDefaultResult(state);
195   }
196 
197   /**
198    * Called when visiting a part.
199    * <p>
200    * Can be overridden by classes extending this interface to support processing
201    * of the visited object.
202    *
203    * @param item
204    *          the Metapath item for the part
205    * @param groupOrControl
206    *          the part's parent Metapath item
207    * @param state
208    *          the calling context information
209    */
210   protected void visitPart( // NOPMD noop default
211       @NonNull IRequiredValueModelNodeItem item,
212       @NonNull IRequiredValueModelNodeItem groupOrControl,
213       T state) {
214     // do nothing
215   }
216 
217   /**
218    * Called when visiting the "metadata" section of an OSCAL document.
219    * <p>
220    * Visits each contained role, location, and party.
221    *
222    * @param rootItem
223    *          the root Metaschema node item containing the "metadata" node
224    * @param state
225    *          the calling context information
226    */
227   protected void visitMetadata(@NonNull IRootAssemblyNodeItem rootItem, T state) {
228     rootItem.getModelItemsByName("metadata").forEach(metadataItem -> {
229       if (isVisitedItemType(IEntityItem.ItemType.ROLE)) {
230         metadataItem.getModelItemsByName("role").forEach(roleItem -> {
231           visitRole(ObjectUtils.requireNonNull(roleItem), metadataItem, state);
232         });
233       }
234 
235       if (isVisitedItemType(IEntityItem.ItemType.LOCATION)) {
236         metadataItem.getModelItemsByName("location").forEach(locationItem -> {
237           visitLocation(ObjectUtils.requireNonNull(locationItem), metadataItem, state);
238         });
239       }
240 
241       if (isVisitedItemType(IEntityItem.ItemType.PARTY)) {
242         metadataItem.getModelItemsByName("party").forEach(partyItem -> {
243           visitParty(ObjectUtils.requireNonNull(partyItem), metadataItem, state);
244         });
245       }
246     });
247   }
248 
249   /**
250    * Called when visiting a role in the "metadata" section of an OSCAL document.
251    * <p>
252    * Can be overridden by classes extending this interface to support processing
253    * of the visited object.
254    *
255    * @param item
256    *          the role Metaschema node item which is a child of the "metadata"
257    *          node
258    * @param metadataItem
259    *          the "metadata" Metaschema node item containing the role
260    * @param state
261    *          the calling context information
262    */
263   protected void visitRole( // NOPMD noop default
264       @NonNull IRequiredValueModelNodeItem item,
265       @NonNull IRequiredValueModelNodeItem metadataItem,
266       T state) {
267     // do nothing
268   }
269 
270   /**
271    * Called when visiting a location in the "metadata" section of an OSCAL
272    * document.
273    * <p>
274    * Can be overridden by classes extending this interface to support processing
275    * of the visited object.
276    *
277    * @param item
278    *          the location Metaschema node item which is a child of the "metadata"
279    *          node
280    * @param metadataItem
281    *          the "metadata" Metaschema node item containing the location
282    * @param state
283    *          the calling context information
284    */
285   protected void visitLocation( // NOPMD noop default
286       @NonNull IRequiredValueModelNodeItem item,
287       @NonNull IRequiredValueModelNodeItem metadataItem,
288       T state) {
289     // do nothing
290   }
291 
292   /**
293    * Called when visiting a party in the "metadata" section of an OSCAL document.
294    * <p>
295    * Can be overridden by classes extending this interface to support processing
296    * of the visited object.
297    *
298    * @param item
299    *          the party Metaschema node item which is a child of the "metadata"
300    *          node
301    * @param metadataItem
302    *          the "metadata" Metaschema node item containing the party
303    * @param state
304    *          the calling context information
305    */
306   protected void visitParty( // NOPMD noop default
307       @NonNull IRequiredValueModelNodeItem item,
308       @NonNull IRequiredValueModelNodeItem metadataItem,
309       T state) {
310     // do nothing
311   }
312 
313   /**
314    * Called when visiting the "back-matter" section of an OSCAL document.
315    * <p>
316    * Visits each contained resource.
317    *
318    * @param rootItem
319    *          the root Metaschema node item containing the "back-matter" node
320    * @param state
321    *          the calling context information
322    */
323   protected void visitBackMatter(@NonNull IRootAssemblyNodeItem rootItem, T state) {
324     if (isVisitedItemType(IEntityItem.ItemType.RESOURCE)) {
325       BACK_MATTER_RESOURCES_METAPATH.evaluate(rootItem).asStream()
326           .map(item -> (IRequiredValueModelNodeItem) item)
327           .forEachOrdered(resourceItem -> {
328             visitResource(ObjectUtils.requireNonNull(resourceItem), rootItem, state);
329           });
330     }
331   }
332 
333   /**
334    * Called when visiting a resource in the "back-matter" section of an OSCAL
335    * document.
336    * <p>
337    * Can be overridden by classes extending this interface to support processing
338    * of the visited object.
339    *
340    * @param item
341    *          the resource Metaschema node item which is a child of the "metadata"
342    *          node
343    * @param backMatterItem
344    *          the resource Metaschema node item containing the party
345    * @param state
346    *          the calling context information
347    */
348   protected void visitResource( // NOPMD noop default
349       @NonNull IRequiredValueModelNodeItem item,
350       @NonNull IRootAssemblyNodeItem backMatterItem,
351       T state) {
352     // do nothing
353   }
354 }