001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.oscal.lib.profile.resolver.selection; 028 029import gov.nist.secauto.metaschema.model.common.metapath.item.IDocumentNodeItem; 030import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem; 031import gov.nist.secauto.metaschema.model.common.metapath.item.IRootAssemblyNodeItem; 032import gov.nist.secauto.metaschema.model.common.util.ObjectUtils; 033import gov.nist.secauto.oscal.lib.model.Catalog; 034import gov.nist.secauto.oscal.lib.model.CatalogGroup; 035import gov.nist.secauto.oscal.lib.model.Control; 036import gov.nist.secauto.oscal.lib.model.ControlPart; 037import gov.nist.secauto.oscal.lib.profile.resolver.support.AbstractIndexingVisitor; 038import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem; 039import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer; 040import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer.SelectionStatus; 041 042import org.apache.logging.log4j.LogManager; 043import org.apache.logging.log4j.Logger; 044 045import edu.umd.cs.findbugs.annotations.NonNull; 046 047/** 048 * Walks a {@link Catalog} indexing all nodes that can be referenced. 049 * <p> 050 * For each {@link CatalogGroup}, {@link Control}, and {@link ControlPart}, 051 * determine if that object is {@link SelectionStatus#SELECTED} or 052 * {@link SelectionStatus#UNSELECTED}. 053 * <p> 054 * A {@link Control} is {@link SelectionStatus#SELECTED} if it matches the 055 * configured {@link IControlFilter}, otherwise it is 056 * {@link SelectionStatus#UNSELECTED}. 057 * <p> 058 * A {@link CatalogGroup} is {@link SelectionStatus#SELECTED} if it contains a 059 * {@link SelectionStatus#SELECTED} descendant {@link Control}, otherwise it is 060 * {@link SelectionStatus#UNSELECTED}. 061 * <p> 062 * A {@link ControlPart} is {@link SelectionStatus#SELECTED} if its containing 063 * control is {@link SelectionStatus#SELECTED}. 064 * <p> 065 * All other indexed nodes will have the {@link SelectionStatus#UNKNOWN}, since 066 * these nodes require reference counting to determine if they are to be kept or 067 * not. 068 */ 069public class ControlSelectionVisitor 070 extends AbstractIndexingVisitor<IControlSelectionState, Boolean> { 071 private static final Logger LOGGER = LogManager.getLogger(ControlSelectionVisitor.class); 072 073 private static final ControlSelectionVisitor SINGLETON = new ControlSelectionVisitor(); 074 075 public static ControlSelectionVisitor instance() { 076 return SINGLETON; 077 } 078 079 @Override 080 protected IIndexer getIndexer(IControlSelectionState state) { 081 return state.getIndex(); 082 } 083 084 @Override 085 protected Boolean newDefaultResult(IControlSelectionState state) { 086 return false; 087 } 088 089 @Override 090 protected Boolean aggregateResults(Boolean first, Boolean second, IControlSelectionState state) { 091 return first || second; 092 } 093 094 public void visit(@NonNull IDocumentNodeItem catalogDocument, @NonNull IControlSelectionState state) { 095 visitCatalog(catalogDocument, state); 096 } 097 098 public void visitProfile( 099 @NonNull IDocumentNodeItem catalogDocument, 100 @NonNull IDocumentNodeItem profileDocument, 101 @NonNull IControlSelectionState state) { 102 visit(catalogDocument, state); 103 104 IRootAssemblyNodeItem root = profileDocument.getRootAssemblyNodeItem(); 105 106 visitMetadata(root, state); 107 visitBackMatter(root, state); 108 } 109 110 @Override 111 public Boolean visitCatalog(IDocumentNodeItem catalogDocument, IControlSelectionState state) { 112 getIndexer(state).setSelectionStatus(catalogDocument, SelectionStatus.SELECTED); 113 return super.visitCatalog(catalogDocument, state); 114 } 115 116 @Override 117 public Boolean visitGroup(IRequiredValueModelNodeItem groupItem, Boolean childSelected, 118 IControlSelectionState state) { 119 super.visitGroup(groupItem, childSelected, state); 120 if (LOGGER.isTraceEnabled()) { 121 CatalogGroup group = (CatalogGroup) groupItem.getValue(); 122 LOGGER.atTrace().log("Selecting group '{}'. match={}", group.getId(), childSelected); 123 } 124 125 // these should agree 126 assert state.isSelected(groupItem) == childSelected; 127 128 if (childSelected) { 129 getIndexer(state).setSelectionStatus(groupItem, SelectionStatus.SELECTED); 130 } else { 131 getIndexer(state).setSelectionStatus(groupItem, SelectionStatus.UNSELECTED); 132 } 133 134 handlePartSelection(groupItem, childSelected, state); 135 return childSelected; 136 } 137 138 private void handlePartSelection( 139 @NonNull IRequiredValueModelNodeItem groupOrControlItem, 140 boolean selected, 141 IControlSelectionState state) { 142 if (isVisitedItemType(IEntityItem.ItemType.PART)) { 143 SelectionStatus selectionStatus = selected ? SelectionStatus.SELECTED : SelectionStatus.UNSELECTED; 144 145 IIndexer index = getIndexer(state); 146 CHILD_PART_METAPATH.evaluate(groupOrControlItem).asStream() 147 .map(item -> (IRequiredValueModelNodeItem) item) 148 .forEachOrdered(partItem -> { 149 index.setSelectionStatus(ObjectUtils.requireNonNull(partItem), selectionStatus); 150 }); 151 } 152 } 153 154 @Override 155 public Boolean visitControl( 156 IRequiredValueModelNodeItem controlItem, 157 Boolean childResult, 158 IControlSelectionState state) { 159 super.visitControl(controlItem, childResult, state); 160 161 boolean selected = state.isSelected(controlItem); 162 if (selected) { 163 getIndexer(state).setSelectionStatus(controlItem, SelectionStatus.SELECTED); 164 } else { 165 getIndexer(state).setSelectionStatus(controlItem, SelectionStatus.UNSELECTED); 166 } 167 168 handlePartSelection(controlItem, selected, state); 169 return selected; 170 } 171}