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