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.selection;
028
029import gov.nist.secauto.metaschema.model.common.util.ObjectUtils;
030import gov.nist.secauto.oscal.lib.model.Catalog;
031import gov.nist.secauto.oscal.lib.model.CatalogGroup;
032import gov.nist.secauto.oscal.lib.model.Control;
033import gov.nist.secauto.oscal.lib.model.Parameter;
034
035import org.apache.logging.log4j.LogManager;
036import org.apache.logging.log4j.Logger;
037
038import java.util.Collection;
039import java.util.Collections;
040import java.util.HashSet;
041import java.util.LinkedHashSet;
042import java.util.List;
043import java.util.Set;
044
045import edu.umd.cs.findbugs.annotations.NonNull;
046import nl.talsmasoftware.lazy4j.Lazy;
047
048public class DefaultResult implements IResult {
049  private static final Logger LOGGER = LogManager.getLogger(DefaultResult.class);
050
051  @NonNull
052  private final Lazy<Set<Control>> promotedControls = ObjectUtils.notNull(Lazy.lazy(LinkedHashSet::new));
053  @NonNull
054  private final Lazy<Set<Parameter>> promotedParameters = ObjectUtils.notNull(Lazy.lazy(LinkedHashSet::new));
055  @NonNull
056  private final Lazy<Set<CatalogGroup>> removedGroups = ObjectUtils.notNull(Lazy.lazy(HashSet::new));
057  @NonNull
058  private final Lazy<Set<Control>> removedControls = ObjectUtils.notNull(Lazy.lazy(HashSet::new));
059  @NonNull
060  private final Lazy<Set<Parameter>> removedParameters = ObjectUtils.notNull(Lazy.lazy(HashSet::new));
061
062  @SuppressWarnings("null")
063  @NonNull
064  protected Collection<Parameter> getPromotedParameters() {
065    return promotedParameters.getIfAvailable().orElse(Collections.emptySet());
066  }
067
068  @SuppressWarnings("null")
069  @NonNull
070  protected Collection<Control> getPromotedControls() {
071    return promotedControls.getIfAvailable().orElse(Collections.emptySet());
072  }
073
074  @SuppressWarnings("null")
075  @NonNull
076  protected Collection<CatalogGroup> getRemovedGroups() {
077    return removedGroups.getIfAvailable().orElse(Collections.emptySet());
078  }
079
080  @SuppressWarnings("null")
081  @NonNull
082  protected Collection<Control> getRemovedControls() {
083    return removedControls.getIfAvailable().orElse(Collections.emptySet());
084  }
085
086  @SuppressWarnings("null")
087  @NonNull
088  protected Collection<Parameter> getRemovedParameters() {
089    return removedParameters.getIfAvailable().orElse(Collections.emptySet());
090  }
091
092  @Override
093  public void promoteParameter(@NonNull Parameter param) {
094    if (LOGGER.isDebugEnabled()) {
095      LOGGER.atDebug().log("promoting parameter '{}'", param.getId());
096    }
097    promotedParameters.get().add(param);
098  }
099
100  @Override
101  public void promoteControl(@NonNull Control control) {
102    if (LOGGER.isDebugEnabled()) {
103      LOGGER.atDebug().log("promoting control '{}'", control.getId());
104    }
105    promotedControls.get().add(control);
106  }
107
108  @Override
109  public void applyTo(@NonNull Catalog parent) {
110    applyRemovesTo(parent);
111    getPromotedParameters().forEach(param -> parent.addParam(ObjectUtils.notNull(param)));
112    getPromotedControls().forEach(control -> {
113      assert control != null;
114      parent.addControl(control);
115      control.setParentControl(null);
116    });
117  }
118
119  @Override
120  public void applyTo(@NonNull CatalogGroup parent) {
121    applyRemovesTo(parent);
122    getPromotedControls().forEach(control -> {
123      assert control != null;
124      parent.addControl(control);
125      control.setParentControl(null);
126    });
127    getPromotedParameters().forEach(param -> parent.addParam(ObjectUtils.notNull(param)));
128  }
129
130  @Override
131  public void applyTo(@NonNull Control parent) {
132    applyRemovesTo(parent);
133    getPromotedControls().forEach(control -> {
134      assert control != null;
135      parent.addControl(control);
136      control.setParentControl(null);
137    });
138    getPromotedParameters().forEach(param -> parent.addParam(ObjectUtils.notNull(param)));
139  }
140
141  public void applyRemovesTo(Catalog parent) {
142    removeItems(parent.getGroups(), getRemovedGroups());
143    removeItems(parent.getControls(), getRemovedControls());
144    removeItems(parent.getParams(), getRemovedParameters());
145  }
146
147  public void applyRemovesTo(CatalogGroup parent) {
148    removeItems(parent.getGroups(), getRemovedGroups());
149    removeItems(parent.getControls(), getRemovedControls());
150    removeItems(parent.getParams(), getRemovedParameters());
151  }
152
153  public void applyRemovesTo(Control parent) {
154    removeItems(parent.getControls(), getRemovedControls());
155    removeItems(parent.getParams(), getRemovedParameters());
156  }
157
158  public DefaultResult append(@NonNull DefaultResult that) {
159    lazyAppend(promotedControls, that.promotedControls);
160    lazyAppend(promotedParameters, that.promotedParameters);
161    lazyAppend(removedGroups, that.removedGroups);
162    lazyAppend(removedControls, that.removedControls);
163    lazyAppend(removedParameters, that.removedParameters);
164    return this;
165  }
166
167  public DefaultResult appendPromoted(@NonNull DefaultResult that) {
168    lazyAppend(promotedControls, that.promotedControls);
169    lazyAppend(promotedParameters, that.promotedParameters);
170    return this;
171  }
172
173  protected static <T> void lazyAppend(@NonNull Lazy<Set<T>> self, @NonNull Lazy<Set<T>> other) {
174    if (other.isAvailable()) {
175      Set<T> otherSet = other.get();
176      if (!otherSet.isEmpty()) {
177        self.get().addAll(otherSet);
178      }
179    }
180  }
181
182  protected static <T> void removeItems(List<T> list, @NonNull Collection<T> itemsToDelete) {
183    itemsToDelete.forEach(item -> {
184      if (!list.remove(item)) {
185        LOGGER.atError().log("item didn't exist in list");
186      }
187    });
188  }
189
190  public void removeGroup(CatalogGroup group) {
191    if (LOGGER.isDebugEnabled()) {
192      LOGGER.atDebug().log("Requesting removal of group '{}'.", group.getId());
193    }
194    removedGroups.get().add(group);
195  }
196
197  public void removeControl(Control control) {
198    if (LOGGER.isDebugEnabled()) {
199      LOGGER.atDebug().log("Requesting removal of control '{}'.", control.getId());
200    }
201    removedControls.get().add(control);
202  }
203
204  public void removeParameter(Parameter parameter) {
205    if (LOGGER.isDebugEnabled()) {
206      LOGGER.atDebug().log("Requesting removal of parameter '{}'.", parameter.getId());
207    }
208    removedParameters.get().add(parameter);
209  }
210}