1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.oscal.lib.profile.resolver.support;
7   
8   import gov.nist.secauto.metaschema.core.metapath.item.node.IAssemblyNodeItem;
9   import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
10  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
11  import gov.nist.secauto.oscal.lib.OscalModelConstants;
12  
13  import edu.umd.cs.findbugs.annotations.NonNull;
14  
15  /**
16   * Used to visit a catalog containing groups and controls.
17   *
18   * @param <T>
19   *          the type of the state object used to pass calling context
20   *          information
21   * @param <R>
22   *          the type of the result for visiting a collection of groups and/or
23   *          controls
24   */
25  public abstract class AbstractCatalogVisitor<T, R> implements ICatalogVisitor<T, R> {
26  
27    protected abstract R newDefaultResult(T state);
28  
29    protected abstract R aggregateResults(R first, R second, T state);
30  
31    protected R visitCatalog(@NonNull IDocumentNodeItem catalogDocument, T state) {
32      return catalogDocument.modelItems().reduce(
33          newDefaultResult(state),
34          (result, catalogOrGroup) -> visitGroupContainer(
35              ObjectUtils.requireNonNull((IAssemblyNodeItem) catalogOrGroup), result, state),
36          (result1, result2) -> aggregateResults(result1, result2, state));
37    }
38  
39    /**
40     * Visit the child groups and controls (in that order) of a given catalog or
41     * group container.
42     *
43     * @param catalogOrGroup
44     *          the catalog or group Metapath item currently being visited
45     * @param initialResult
46     *          the initial result value to use when aggregating child results
47     * @param state
48     *          the current visitor state
49     * @return a meaningful result of the given type
50     */
51    protected R visitGroupContainer(
52        @NonNull IAssemblyNodeItem catalogOrGroup,
53        R initialResult,
54        T state) {
55      R result = catalogOrGroup.getModelItemsByName(OscalModelConstants.QNAME_GROUP).stream()
56          .map(groupItem -> {
57            return visitGroupItem(
58                ObjectUtils.requireNonNull((IAssemblyNodeItem) groupItem),
59                state);
60          })
61          .reduce(initialResult, (first, second) -> aggregateResults(first, second, state));
62      return visitControlContainer(catalogOrGroup, result, state);
63    }
64  
65    /**
66     * Called when visiting a group.
67     * <p>
68     * This method will first visit the group's children, then the group itself.
69     *
70     * @param group
71     *          the group Metapath item to visit
72     * @param state
73     *          the current visitor state
74     * @return a meaningful result of the given type
75     */
76    protected R visitGroupItem(@NonNull IAssemblyNodeItem group, T state) {
77      R childResult = visitGroupContainer(group, newDefaultResult(state), state);
78      return visitGroupInternal(group, childResult, state);
79    }
80  
81    /**
82     * Called when visiting a group after visiting it's children.
83     *
84     * @param group
85     *          the group Metapath item currently being visited
86     * @param childResult
87     *          the result of visiting the group's children
88     * @param state
89     *          the current visitor state
90     * @return a meaningful result of the given type
91     */
92    protected R visitGroupInternal(
93        @NonNull IAssemblyNodeItem group,
94        R childResult,
95        T state) {
96      return visitGroup(group, childResult, state);
97    }
98  
99    /**
100    * Visit the child controls (in that order) of a given catalog, group, or
101    * control container.
102    *
103    * @param catalogOrGroupOrControl
104    *          the catalog, group, or control Metapath item currently being visited
105    * @param initialResult
106    *          the initial result value to use when aggregating child results
107    * @param state
108    *          the current visitor state
109    * @return a meaningful result of the given type
110    */
111   protected R visitControlContainer(
112       @NonNull IAssemblyNodeItem catalogOrGroupOrControl,
113       R initialResult,
114       T state) {
115     return catalogOrGroupOrControl.getModelItemsByName(OscalModelConstants.QNAME_CONTROL).stream()
116         .map(control -> {
117           return visitControlItem(ObjectUtils.requireNonNull((IAssemblyNodeItem) control), state);
118         })
119         .reduce(initialResult, (first, second) -> aggregateResults(first, second, state));
120   }
121 
122   /**
123    * Called when visiting a control.
124    * <p>
125    * This method will first visit the control's children, then the control itself.
126    *
127    * @param control
128    *          the control Metapath item to visit
129    * @param state
130    *          the current visitor state
131    * @return a meaningful result of the given type
132    */
133   protected R visitControlItem(@NonNull IAssemblyNodeItem control, T state) {
134     R childResult = visitControlContainer(control, newDefaultResult(state), state);
135     return visitControlInternal(control, childResult, state);
136   }
137 
138   /**
139    * Called when visiting a control after visiting it's children.
140    *
141    * @param control
142    *          the Metapath item for the control currently being visited
143    * @param childResult
144    *          the result of visiting the control's children
145    * @param state
146    *          the calling context information
147    * @return a meaningful result of the given type
148    */
149   protected R visitControlInternal(@NonNull IAssemblyNodeItem control, R childResult, T state) {
150     return visitControl(control, childResult, state);
151   }
152 }