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