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}