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.merge; 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.BackMatter.Resource; 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.model.Metadata.Location; 038import gov.nist.secauto.oscal.lib.model.Metadata.Party; 039import gov.nist.secauto.oscal.lib.model.Metadata.Role; 040import gov.nist.secauto.oscal.lib.model.Parameter; 041import gov.nist.secauto.oscal.lib.profile.resolver.policy.ReferenceCountingVisitor; 042import gov.nist.secauto.oscal.lib.profile.resolver.selection.DefaultResult; 043import gov.nist.secauto.oscal.lib.profile.resolver.selection.FilterNonSelectedVisitor; 044import gov.nist.secauto.oscal.lib.profile.resolver.support.AbstractCatalogEntityVisitor; 045import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem; 046import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem.ItemType; 047import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer; 048import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer.SelectionStatus; 049 050import java.util.EnumSet; 051import java.util.UUID; 052 053import edu.umd.cs.findbugs.annotations.NonNull; 054 055public class FlatteningStructuringVisitor 056 extends AbstractCatalogEntityVisitor<IIndexer, Void> { 057 private static final FlatteningStructuringVisitor SINGLETON = new FlatteningStructuringVisitor(); 058 059 public static FlatteningStructuringVisitor instance() { 060 return SINGLETON; 061 } 062 063 public FlatteningStructuringVisitor() { 064 super(ObjectUtils.notNull(EnumSet.of(ItemType.GROUP, ItemType.CONTROL))); 065 } 066 067 @Override 068 protected Void newDefaultResult(IIndexer state) { 069 // do nothing 070 return null; 071 } 072 073 @Override 074 protected Void aggregateResults(Void first, Void second, IIndexer state) { 075 // do nothing 076 return null; 077 } 078 079 @Override 080 public Void visitCatalog(@NonNull IDocumentNodeItem catalogItem, IIndexer index) { 081 index.resetSelectionStatus(); 082 083 index.setSelectionStatus(catalogItem, SelectionStatus.SELECTED); 084 super.visitCatalog(catalogItem, index); 085 086 for (ItemType itemType : ItemType.values()) { 087 assert itemType != null; 088 for (IEntityItem item : index.getEntitiesByItemType(itemType)) { 089 item.resetReferenceCount(); 090 } 091 } 092 093 // process references, looking for orphaned links to groups 094 ReferenceCountingVisitor.instance().visitCatalog(catalogItem, index, catalogItem.getDocumentUri()); 095 096 FlatteningFilterNonSelectedVisitor.instance().visitCatalog(catalogItem, index); 097 return null; 098 } 099 100 @Override 101 public Void visitGroup(IRequiredValueModelNodeItem item, Void childResult, IIndexer index) { 102 CatalogGroup group = (CatalogGroup) item.getValue(); 103 String id = group.getId(); 104 if (id != null) { 105 IEntityItem entity = index.getEntity(ItemType.GROUP, id); 106 assert entity != null; 107 // refresh the instance 108 entity.setInstance(item); 109 } 110 111 index.setSelectionStatus(item, SelectionStatus.UNSELECTED); 112 handlePartSelection(item, index, SelectionStatus.UNSELECTED); 113 return super.visitGroup(item, childResult, index); 114 } 115 116 @Override 117 public Void visitControl(IRequiredValueModelNodeItem item, Void childResult, IIndexer index) { 118 Control control = (Control) item.getValue(); 119 String id = ObjectUtils.requireNonNull(control.getId()); 120 IEntityItem entity = index.getEntity(ItemType.CONTROL, id); 121 assert entity != null; 122 // refresh the instance 123 entity.setInstance(item); 124 125 index.setSelectionStatus(item, SelectionStatus.SELECTED); 126 handlePartSelection(item, index, SelectionStatus.SELECTED); 127 return null; 128 } 129 130 @Override 131 protected Void visitParameter(IRequiredValueModelNodeItem item, 132 IRequiredValueModelNodeItem catalogOrGroupOrControl, IIndexer index) { 133 Parameter parameter = (Parameter) item.getValue(); 134 String id = ObjectUtils.requireNonNull(parameter.getId()); 135 IEntityItem entity = index.getEntity(ItemType.PARAMETER, id); 136 assert entity != null; 137 // refresh the instance 138 entity.setInstance(item); 139 140 return null; 141 } 142 143 @Override 144 protected void visitRole(IRequiredValueModelNodeItem item, IRequiredValueModelNodeItem metadataItem, 145 IIndexer index) { 146 Role role = (Role) item.getValue(); 147 String id = ObjectUtils.requireNonNull(role.getId()); 148 IEntityItem entity = index.getEntity(ItemType.ROLE, id); 149 assert entity != null; 150 // refresh the instance 151 entity.setInstance(item); 152 } 153 154 @Override 155 protected void visitLocation(IRequiredValueModelNodeItem item, IRequiredValueModelNodeItem metadataItem, 156 IIndexer index) { 157 Location location = (Location) item.getValue(); 158 UUID uuid = ObjectUtils.requireNonNull(location.getUuid()); 159 IEntityItem entity = index.getEntity(ItemType.LOCATION, uuid); 160 assert entity != null; 161 // refresh the instance 162 entity.setInstance(item); 163 } 164 165 @Override 166 protected void visitParty(IRequiredValueModelNodeItem item, IRequiredValueModelNodeItem metadataItem, 167 IIndexer index) { 168 Party location = (Party) item.getValue(); 169 UUID uuid = ObjectUtils.requireNonNull(location.getUuid()); 170 IEntityItem entity = index.getEntity(ItemType.PARTY, uuid); 171 assert entity != null; 172 // refresh the instance 173 entity.setInstance(item); 174 } 175 176 @Override 177 protected void visitResource(IRequiredValueModelNodeItem item, IRootAssemblyNodeItem rootItem, 178 IIndexer index) { 179 Resource location = (Resource) item.getValue(); 180 UUID uuid = ObjectUtils.requireNonNull(location.getUuid()); 181 IEntityItem entity = index.getEntity(ItemType.RESOURCE, uuid); 182 assert entity != null; 183 // refresh the instance 184 entity.setInstance(item); 185 } 186 187 private static void handlePartSelection( 188 @NonNull IRequiredValueModelNodeItem groupOrControlItem, 189 @NonNull IIndexer index, 190 @NonNull SelectionStatus selectionStatus) { 191 CHILD_PART_METAPATH.evaluate(groupOrControlItem).asStream() 192 .map(item -> (IRequiredValueModelNodeItem) item) 193 .forEachOrdered(partItem -> { 194 index.setSelectionStatus(ObjectUtils.requireNonNull(partItem), selectionStatus); 195 196 ControlPart part = (ControlPart) partItem.getValue(); 197 String id = part.getId(); 198 if (id != null) { 199 IEntityItem entity = index.getEntity(ItemType.PART, id); 200 assert entity != null; 201 // refresh the instance 202 entity.setInstance(partItem); 203 } 204 }); 205 } 206 207 private static final class FlatteningFilterNonSelectedVisitor 208 extends FilterNonSelectedVisitor { 209 private static final FlatteningFilterNonSelectedVisitor SINGLETON = new FlatteningFilterNonSelectedVisitor(); 210 211 public static FlatteningFilterNonSelectedVisitor instance() { 212 return SINGLETON; 213 } 214 215 @Override 216 public DefaultResult visitControl(IRequiredValueModelNodeItem item, DefaultResult childResult, 217 Context context) { 218 assert childResult != null; 219 220 Control control = (Control) item.getValue(); 221 IIndexer index = context.getIndexer(); 222 // this control should always be found in the index 223 IEntityItem entity = ObjectUtils.requireNonNull( 224 index.getEntity(ItemType.CONTROL, ObjectUtils.requireNonNull(control.getId()), false)); 225 226 IRequiredValueModelNodeItem parent = ObjectUtils.notNull(item.getParentContentNodeItem()); 227 DefaultResult retval = new DefaultResult(); 228 if (SelectionStatus.SELECTED.equals(index.getSelectionStatus(item))) { 229 // keep this control 230 231 // always promote the control and any children 232 retval.promoteControl(control); 233 234 retval.appendPromoted(childResult); 235 childResult.applyRemovesTo(control); 236 237 if (parent.getValue() instanceof Control && SelectionStatus.SELECTED.equals(index.getSelectionStatus(parent))) { 238 retval.removeControl(control); 239 } 240 } else { 241 // remove this control and promote any needed children 242 243 if (SelectionStatus.SELECTED.equals(index.getSelectionStatus(parent))) { 244 retval.removeControl(control); 245 } 246 retval.appendPromoted(ObjectUtils.notNull(childResult)); 247 index.removeItem(entity); 248 249 // remove any associated parts from the index 250 removePartsFromIndex(item, index); 251 } 252 return retval; 253 } 254 } 255}