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}