001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package gov.nist.secauto.oscal.lib.model.control.catalog;
007
008import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
009import gov.nist.secauto.metaschema.core.util.CollectionUtil;
010import gov.nist.secauto.metaschema.core.util.ObjectUtils;
011import gov.nist.secauto.metaschema.databind.io.IDeserializationHandler;
012import gov.nist.secauto.oscal.lib.model.Control;
013import gov.nist.secauto.oscal.lib.model.ControlPart;
014import gov.nist.secauto.oscal.lib.model.Link;
015import gov.nist.secauto.oscal.lib.model.Parameter;
016import gov.nist.secauto.oscal.lib.model.Property;
017import gov.nist.secauto.oscal.lib.model.control.AbstractParameter;
018
019import java.util.LinkedList;
020import java.util.List;
021import java.util.Objects;
022import java.util.stream.Stream;
023
024import edu.umd.cs.findbugs.annotations.NonNull;
025
026public abstract class AbstractControl
027    implements IDeserializationHandler, IControl {
028  private Control parent;
029
030  @Override
031  public Control getParentControl() {
032    return parent;
033  }
034
035  @Override
036  public void setParentControl(Control parent) {
037    this.parent = parent;
038  }
039
040  @Override
041  public void beforeDeserialize(Object parent) { // NOPMD noop default
042    // do nothing
043  }
044
045  @Override
046  public void afterDeserialize(Object parent) {
047    if (parent instanceof Control) {
048      setParentControl((Control) parent);
049    }
050  }
051
052  @NonNull
053  @Override
054  public Stream<String> getReferencedParameterIds() {
055
056    // get parameters referenced by the group's parts
057    Stream<String> insertIds = CollectionUtil.listOrEmpty(getParts()).stream()
058        // Get the full part hierarchy
059        .flatMap(part -> Stream.concat(Stream.of(part), part.getPartsRecursively()))
060        // Get the inserts for each part
061        .flatMap(part -> part.getInserts(insert -> "param".equals(insert.getType().toString())))
062        // Get the param ids for each insert
063        .map(insert -> insert.getIdReference().toString())
064        .flatMap(ObjectUtils::filterNull);
065
066    // get parameters referenced by the control's parameters
067    Stream<String> parameterIds = CollectionUtil.listOrEmpty(getParams()).stream()
068        .flatMap(ObjectUtils::filterNull)
069        .flatMap(AbstractParameter::getParameterReferences);
070
071    return ObjectUtils.notNull(
072        Stream.concat(insertIds, parameterIds).distinct());
073  }
074
075  @NonNull
076  public static Builder builder(@NonNull String id) {
077    return new Builder(id);
078  }
079
080  public static class Builder {
081    @NonNull
082    private final String id;
083
084    private String clazz;
085    private MarkupLine title;
086    private final List<Parameter> params = new LinkedList<>();
087    private final List<Property> props = new LinkedList<>();
088    private final List<Link> links = new LinkedList<>();
089    private final List<ControlPart> parts = new LinkedList<>();
090    private final List<Control> controls = new LinkedList<>();
091
092    public Builder(@NonNull String id) {
093      this.id = Objects.requireNonNull(id, "id");
094    }
095
096    @NonNull
097    public Builder clazz(@NonNull String value) {
098      this.clazz = Objects.requireNonNull(value);
099      return this;
100    }
101
102    @NonNull
103    public Builder title(@NonNull String markdown) {
104      this.title = MarkupLine.fromMarkdown(Objects.requireNonNull(markdown));
105      return this;
106    }
107
108    @NonNull
109    public Builder title(@NonNull MarkupLine value) {
110      this.title = Objects.requireNonNull(value);
111      return this;
112    }
113
114    @NonNull
115    public Builder param(@NonNull Parameter value) {
116      this.params.add(Objects.requireNonNull(value));
117      return this;
118    }
119
120    @NonNull
121    public Builder prop(@NonNull Property value) {
122      this.props.add(Objects.requireNonNull(value));
123      return this;
124    }
125
126    @NonNull
127    public Builder link(@NonNull Link value) {
128      this.links.add(Objects.requireNonNull(value));
129      return this;
130    }
131
132    @NonNull
133    public Builder part(@NonNull ControlPart value) {
134      this.parts.add(Objects.requireNonNull(value));
135      return this;
136    }
137
138    @NonNull
139    public Builder control(@NonNull Control value) {
140      this.controls.add(Objects.requireNonNull(value));
141      return this;
142    }
143
144    @NonNull
145    public Control build() {
146      Control retval = new Control();
147      retval.setId(id);
148
149      if (title == null) {
150        throw new IllegalStateException("a title must be provided");
151      }
152      retval.setTitle(title);
153
154      if (clazz != null) {
155        retval.setClazz(clazz);
156      }
157      if (!params.isEmpty()) {
158        retval.setParams(params);
159      }
160      if (!props.isEmpty()) {
161        retval.setProps(props);
162      }
163      if (!links.isEmpty()) {
164        retval.setLinks(links);
165      }
166      if (!parts.isEmpty()) {
167        retval.setParts(parts);
168      }
169      if (!controls.isEmpty()) {
170        controls.forEach(control -> control.setParentControl(retval));
171        retval.setControls(controls);
172      }
173
174      return retval;
175    }
176  }
177}