001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package dev.metaschema.oscal.lib.profile.resolver.support;
007
008import dev.metaschema.core.metapath.item.node.IAssemblyNodeItem;
009import dev.metaschema.core.metapath.item.node.IDocumentNodeItem;
010import dev.metaschema.core.util.ObjectUtils;
011import dev.metaschema.oscal.lib.OscalModelConstants;
012import edu.umd.cs.findbugs.annotations.NonNull;
013
014/**
015 * Used to visit a catalog containing groups and controls.
016 *
017 * @param <T>
018 *          the type of the state object used to pass calling context
019 *          information
020 * @param <R>
021 *          the type of the result for visiting a collection of groups and/or
022 *          controls
023 */
024public abstract class AbstractCatalogVisitor<T, R> implements ICatalogVisitor<T, R> {
025
026  protected abstract R newDefaultResult(T state);
027
028  protected abstract R aggregateResults(R first, R second, T state);
029
030  protected R visitCatalog(@NonNull IDocumentNodeItem catalogDocument, T state) {
031    return catalogDocument.modelItems().reduce(
032        newDefaultResult(state),
033        (result, catalogOrGroup) -> visitGroupContainer(
034            ObjectUtils.requireNonNull((IAssemblyNodeItem) catalogOrGroup), result, state),
035        (result1, result2) -> aggregateResults(result1, result2, state));
036  }
037
038  /**
039   * Visit the child groups and controls (in that order) of a given catalog or
040   * group container.
041   *
042   * @param catalogOrGroup
043   *          the catalog or group Metapath item currently being visited
044   * @param initialResult
045   *          the initial result value to use when aggregating child results
046   * @param state
047   *          the current visitor state
048   * @return a meaningful result of the given type
049   */
050  protected R visitGroupContainer(
051      @NonNull IAssemblyNodeItem catalogOrGroup,
052      R initialResult,
053      T state) {
054    R result = catalogOrGroup.getModelItemsByName(OscalModelConstants.QNAME_GROUP).stream()
055        .map(groupItem -> {
056          return visitGroupItem(
057              ObjectUtils.requireNonNull((IAssemblyNodeItem) groupItem),
058              state);
059        })
060        .reduce(initialResult, (first, second) -> aggregateResults(first, second, state));
061    return visitControlContainer(catalogOrGroup, result, state);
062  }
063
064  /**
065   * Called when visiting a group.
066   * <p>
067   * This method will first visit the group's children, then the group itself.
068   *
069   * @param group
070   *          the group Metapath item to visit
071   * @param state
072   *          the current visitor state
073   * @return a meaningful result of the given type
074   */
075  protected R visitGroupItem(@NonNull IAssemblyNodeItem group, T state) {
076    R childResult = visitGroupContainer(group, newDefaultResult(state), state);
077    return visitGroupInternal(group, childResult, state);
078  }
079
080  /**
081   * Called when visiting a group after visiting it's children.
082   *
083   * @param group
084   *          the group Metapath item currently being visited
085   * @param childResult
086   *          the result of visiting the group's children
087   * @param state
088   *          the current visitor state
089   * @return a meaningful result of the given type
090   */
091  protected R visitGroupInternal(
092      @NonNull IAssemblyNodeItem group,
093      R childResult,
094      T state) {
095    return visitGroup(group, childResult, state);
096  }
097
098  /**
099   * 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}