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