AbstractCatalogVisitor.java

/*
 * SPDX-FileCopyrightText: none
 * SPDX-License-Identifier: CC0-1.0
 */

package gov.nist.secauto.oscal.lib.profile.resolver.support;

import gov.nist.secauto.metaschema.core.metapath.item.node.IAssemblyNodeItem;
import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
import gov.nist.secauto.oscal.lib.OscalModelConstants;

import edu.umd.cs.findbugs.annotations.NonNull;

/**
 * Used to visit a catalog containing groups and controls.
 *
 * @param <T>
 *          the type of the state object used to pass calling context
 *          information
 * @param <R>
 *          the type of the result for visiting a collection of groups and/or
 *          controls
 */
public abstract class AbstractCatalogVisitor<T, R> implements ICatalogVisitor<T, R> {

  protected abstract R newDefaultResult(T state);

  protected abstract R aggregateResults(R first, R second, T state);

  protected R visitCatalog(@NonNull IDocumentNodeItem catalogDocument, T state) {
    return catalogDocument.modelItems().reduce(
        newDefaultResult(state),
        (result, catalogOrGroup) -> visitGroupContainer(
            ObjectUtils.requireNonNull((IAssemblyNodeItem) catalogOrGroup), result, state),
        (result1, result2) -> aggregateResults(result1, result2, state));
  }

  /**
   * Visit the child groups and controls (in that order) of a given catalog or
   * group container.
   *
   * @param catalogOrGroup
   *          the catalog or group Metapath item currently being visited
   * @param initialResult
   *          the initial result value to use when aggregating child results
   * @param state
   *          the current visitor state
   * @return a meaningful result of the given type
   */
  protected R visitGroupContainer(
      @NonNull IAssemblyNodeItem catalogOrGroup,
      R initialResult,
      T state) {
    R result = catalogOrGroup.getModelItemsByName(OscalModelConstants.QNAME_GROUP).stream()
        .map(groupItem -> {
          return visitGroupItem(
              ObjectUtils.requireNonNull((IAssemblyNodeItem) groupItem),
              state);
        })
        .reduce(initialResult, (first, second) -> aggregateResults(first, second, state));
    return visitControlContainer(catalogOrGroup, result, state);
  }

  /**
   * Called when visiting a group.
   * <p>
   * This method will first visit the group's children, then the group itself.
   *
   * @param group
   *          the group Metapath item to visit
   * @param state
   *          the current visitor state
   * @return a meaningful result of the given type
   */
  protected R visitGroupItem(@NonNull IAssemblyNodeItem group, T state) {
    R childResult = visitGroupContainer(group, newDefaultResult(state), state);
    return visitGroupInternal(group, childResult, state);
  }

  /**
   * Called when visiting a group after visiting it's children.
   *
   * @param group
   *          the group Metapath item currently being visited
   * @param childResult
   *          the result of visiting the group's children
   * @param state
   *          the current visitor state
   * @return a meaningful result of the given type
   */
  protected R visitGroupInternal(
      @NonNull IAssemblyNodeItem group,
      R childResult,
      T state) {
    return visitGroup(group, childResult, state);
  }

  /**
   * Visit the child controls (in that order) of a given catalog, group, or
   * control container.
   *
   * @param catalogOrGroupOrControl
   *          the catalog, group, or control Metapath item currently being visited
   * @param initialResult
   *          the initial result value to use when aggregating child results
   * @param state
   *          the current visitor state
   * @return a meaningful result of the given type
   */
  protected R visitControlContainer(
      @NonNull IAssemblyNodeItem catalogOrGroupOrControl,
      R initialResult,
      T state) {
    return catalogOrGroupOrControl.getModelItemsByName(OscalModelConstants.QNAME_CONTROL).stream()
        .map(control -> {
          return visitControlItem(ObjectUtils.requireNonNull((IAssemblyNodeItem) control), state);
        })
        .reduce(initialResult, (first, second) -> aggregateResults(first, second, state));
  }

  /**
   * Called when visiting a control.
   * <p>
   * This method will first visit the control's children, then the control itself.
   *
   * @param control
   *          the control Metapath item to visit
   * @param state
   *          the current visitor state
   * @return a meaningful result of the given type
   */
  protected R visitControlItem(@NonNull IAssemblyNodeItem control, T state) {
    R childResult = visitControlContainer(control, newDefaultResult(state), state);
    return visitControlInternal(control, childResult, state);
  }

  /**
   * Called when visiting a control after visiting it's children.
   *
   * @param control
   *          the Metapath item for the control currently being visited
   * @param childResult
   *          the result of visiting the control's children
   * @param state
   *          the calling context information
   * @return a meaningful result of the given type
   */
  protected R visitControlInternal(@NonNull IAssemblyNodeItem control, R childResult, T state) {
    return visitControl(control, childResult, state);
  }
}