1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.oscal.lib.profile.resolver.merge;
7   
8   import gov.nist.secauto.metaschema.core.metapath.item.node.IAssemblyNodeItem;
9   import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
10  import gov.nist.secauto.metaschema.core.metapath.item.node.IRootAssemblyNodeItem;
11  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
12  import gov.nist.secauto.oscal.lib.model.BackMatter.Resource;
13  import gov.nist.secauto.oscal.lib.model.CatalogGroup;
14  import gov.nist.secauto.oscal.lib.model.Control;
15  import gov.nist.secauto.oscal.lib.model.ControlPart;
16  import gov.nist.secauto.oscal.lib.model.Metadata.Location;
17  import gov.nist.secauto.oscal.lib.model.Metadata.Party;
18  import gov.nist.secauto.oscal.lib.model.Metadata.Role;
19  import gov.nist.secauto.oscal.lib.model.Parameter;
20  import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolver;
21  import gov.nist.secauto.oscal.lib.profile.resolver.policy.ReferenceCountingVisitor;
22  import gov.nist.secauto.oscal.lib.profile.resolver.selection.DefaultResult;
23  import gov.nist.secauto.oscal.lib.profile.resolver.selection.FilterNonSelectedVisitor;
24  import gov.nist.secauto.oscal.lib.profile.resolver.support.AbstractCatalogEntityVisitor;
25  import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem;
26  import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem.ItemType;
27  import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer;
28  import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer.SelectionStatus;
29  
30  import java.util.EnumSet;
31  import java.util.UUID;
32  
33  import edu.umd.cs.findbugs.annotations.NonNull;
34  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
35  
36  public class FlatteningStructuringVisitor
37      extends AbstractCatalogEntityVisitor<IIndexer, Void> {
38    @NonNull
39    private final ProfileResolver.UriResolver uriResolver;
40  
41    public FlatteningStructuringVisitor(@NonNull ProfileResolver.UriResolver uriResolver) {
42      super(ObjectUtils.notNull(EnumSet.of(ItemType.GROUP, ItemType.CONTROL)));
43      this.uriResolver = uriResolver;
44    }
45  
46    @Override
47    protected Void newDefaultResult(IIndexer state) {
48      // do nothing
49      return null;
50    }
51  
52    @Override
53    protected Void aggregateResults(Void first, Void second, IIndexer state) {
54      // do nothing
55      return null;
56    }
57  
58    @Override
59    public Void visitCatalog(@NonNull IDocumentNodeItem catalogItem, IIndexer index) {
60      index.resetSelectionStatus();
61  
62      index.setSelectionStatus(catalogItem, SelectionStatus.SELECTED);
63      super.visitCatalog(catalogItem, index);
64  
65      for (ItemType itemType : ItemType.values()) {
66        assert itemType != null;
67        for (IEntityItem item : index.getEntitiesByItemType(itemType)) {
68          item.resetReferenceCount();
69        }
70      }
71  
72      // process references, looking for orphaned links to groups
73      ReferenceCountingVisitor.instance().visitCatalog(catalogItem, index, uriResolver);
74  
75      FlatteningFilterNonSelectedVisitor.instance().visitCatalog(catalogItem, index);
76      return null;
77    }
78  
79    @Override
80    public Void visitGroup(
81        IAssemblyNodeItem item,
82        Void childResult,
83        IIndexer index) {
84      CatalogGroup group = ObjectUtils.requireNonNull((CatalogGroup) item.getValue());
85      String id = group.getId();
86      if (id != null) {
87        IEntityItem entity = index.getEntity(ItemType.GROUP, id);
88        assert entity != null;
89        // refresh the instance
90        entity.setInstance(item);
91      }
92  
93      index.setSelectionStatus(item, SelectionStatus.UNSELECTED);
94      handlePartSelection(item, index, SelectionStatus.UNSELECTED);
95      return super.visitGroup(item, childResult, index);
96    }
97  
98    @Override
99    public Void visitControl(
100       IAssemblyNodeItem item,
101       Void childResult,
102       IIndexer index) {
103     Control control = ObjectUtils.requireNonNull((Control) item.getValue());
104     String id = ObjectUtils.requireNonNull(control.getId());
105     IEntityItem entity = index.getEntity(ItemType.CONTROL, id);
106     assert entity != null;
107     // refresh the instance
108     entity.setInstance(item);
109 
110     index.setSelectionStatus(item, SelectionStatus.SELECTED);
111     handlePartSelection(item, index, SelectionStatus.SELECTED);
112     return null;
113   }
114 
115   @Override
116   protected Void visitParameter(
117       IAssemblyNodeItem item,
118       IAssemblyNodeItem catalogOrGroupOrControl,
119       IIndexer index) {
120     Parameter parameter = ObjectUtils.requireNonNull((Parameter) item.getValue());
121     String id = ObjectUtils.requireNonNull(parameter.getId());
122     IEntityItem entity = index.getEntity(ItemType.PARAMETER, id);
123     assert entity != null;
124     // refresh the instance
125     entity.setInstance(item);
126 
127     return null;
128   }
129 
130   @Override
131   protected void visitRole(
132       IAssemblyNodeItem item,
133       IAssemblyNodeItem metadataItem,
134       IIndexer index) {
135     Role role = ObjectUtils.requireNonNull((Role) item.getValue());
136     String id = ObjectUtils.requireNonNull(role.getId());
137     IEntityItem entity = index.getEntity(ItemType.ROLE, id);
138     assert entity != null;
139     // refresh the instance
140     entity.setInstance(item);
141   }
142 
143   @Override
144   protected void visitLocation(
145       IAssemblyNodeItem item,
146       IAssemblyNodeItem metadataItem,
147       IIndexer index) {
148     Location location = ObjectUtils.requireNonNull((Location) item.getValue());
149     UUID uuid = ObjectUtils.requireNonNull(location.getUuid());
150     IEntityItem entity = index.getEntity(ItemType.LOCATION, uuid);
151     assert entity != null;
152     // refresh the instance
153     entity.setInstance(item);
154   }
155 
156   @Override
157   protected void visitParty(
158       IAssemblyNodeItem item,
159       IAssemblyNodeItem metadataItem,
160       IIndexer index) {
161     Party location = ObjectUtils.requireNonNull((Party) item.getValue());
162     UUID uuid = ObjectUtils.requireNonNull(location.getUuid());
163     IEntityItem entity = index.getEntity(ItemType.PARTY, uuid);
164     assert entity != null;
165     // refresh the instance
166     entity.setInstance(item);
167   }
168 
169   @Override
170   protected void visitResource(
171       IAssemblyNodeItem item,
172       IRootAssemblyNodeItem rootItem,
173       IIndexer index) {
174     Resource location = ObjectUtils.requireNonNull((Resource) item.getValue());
175     UUID uuid = ObjectUtils.requireNonNull(location.getUuid());
176     IEntityItem entity = index.getEntity(ItemType.RESOURCE, uuid);
177     assert entity != null;
178     // refresh the instance
179     entity.setInstance(item);
180   }
181 
182   private static void handlePartSelection(
183       @NonNull IAssemblyNodeItem groupOrControlItem,
184       @NonNull IIndexer index,
185       @NonNull SelectionStatus selectionStatus) {
186     CHILD_PART_METAPATH.evaluate(groupOrControlItem).stream()
187         .map(item -> (IAssemblyNodeItem) item)
188         .forEachOrdered(partItem -> {
189           index.setSelectionStatus(ObjectUtils.requireNonNull(partItem), selectionStatus);
190 
191           ControlPart part = ObjectUtils.requireNonNull((ControlPart) partItem.getValue());
192           String id = part.getId();
193           if (id != null) {
194             IEntityItem entity = index.getEntity(ItemType.PART, id);
195             assert entity != null;
196             // refresh the instance
197             entity.setInstance(partItem);
198           }
199         });
200   }
201 
202   private static final class FlatteningFilterNonSelectedVisitor
203       extends FilterNonSelectedVisitor {
204     private static final FlatteningFilterNonSelectedVisitor SINGLETON = new FlatteningFilterNonSelectedVisitor();
205 
206     @SuppressFBWarnings(value = "SING_SINGLETON_GETTER_NOT_SYNCHRONIZED", justification = "class initialization")
207     public static FlatteningFilterNonSelectedVisitor instance() {
208       return SINGLETON;
209     }
210 
211     @Override
212     public DefaultResult visitControl(IAssemblyNodeItem item, DefaultResult childResult,
213         Context context) {
214       assert childResult != null;
215 
216       Control control = ObjectUtils.requireNonNull((Control) item.getValue());
217       IIndexer index = context.getIndexer();
218       // this control should always be found in the index
219       IEntityItem entity = ObjectUtils.requireNonNull(
220           index.getEntity(ItemType.CONTROL, ObjectUtils.requireNonNull(control.getId()), false));
221 
222       IAssemblyNodeItem parent = ObjectUtils.notNull(item.getParentContentNodeItem());
223       DefaultResult retval = new DefaultResult();
224       if (SelectionStatus.SELECTED.equals(index.getSelectionStatus(item))) {
225         // keep this control
226 
227         // always promote the control and any children
228         retval.promoteControl(control);
229 
230         retval.appendPromoted(childResult);
231         childResult.applyRemovesTo(control);
232 
233         if (parent.getValue() instanceof Control && SelectionStatus.SELECTED.equals(index.getSelectionStatus(parent))) {
234           retval.removeControl(control);
235         }
236       } else {
237         // remove this control and promote any needed children
238 
239         if (SelectionStatus.SELECTED.equals(index.getSelectionStatus(parent))) {
240           retval.removeControl(control);
241         }
242         retval.appendPromoted(ObjectUtils.notNull(childResult));
243         index.removeItem(entity);
244 
245         // remove any associated parts from the index
246         removePartsFromIndex(item, index);
247       }
248       return retval;
249     }
250   }
251 }