1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.oscal.lib.model.control.catalog;
7   
8   import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
9   import gov.nist.secauto.metaschema.core.util.CollectionUtil;
10  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
11  import gov.nist.secauto.metaschema.databind.io.IDeserializationHandler;
12  import gov.nist.secauto.oscal.lib.model.Control;
13  import gov.nist.secauto.oscal.lib.model.ControlPart;
14  import gov.nist.secauto.oscal.lib.model.Link;
15  import gov.nist.secauto.oscal.lib.model.Parameter;
16  import gov.nist.secauto.oscal.lib.model.Property;
17  import gov.nist.secauto.oscal.lib.model.control.AbstractParameter;
18  
19  import java.util.LinkedList;
20  import java.util.List;
21  import java.util.Objects;
22  import java.util.stream.Stream;
23  
24  import edu.umd.cs.findbugs.annotations.NonNull;
25  
26  public abstract class AbstractControl
27      implements IDeserializationHandler, IControl {
28    private Control parent;
29  
30    @Override
31    public Control getParentControl() {
32      return parent;
33    }
34  
35    @Override
36    public void setParentControl(Control parent) {
37      this.parent = parent;
38    }
39  
40    @Override
41    public void beforeDeserialize(Object parent) { // NOPMD noop default
42      // do nothing
43    }
44  
45    @Override
46    public void afterDeserialize(Object parent) {
47      if (parent instanceof Control) {
48        setParentControl((Control) parent);
49      }
50    }
51  
52    @NonNull
53    @Override
54    public Stream<String> getReferencedParameterIds() {
55  
56      // get parameters referenced by the group's parts
57      Stream<String> insertIds = CollectionUtil.listOrEmpty(getParts()).stream()
58          // Get the full part hierarchy
59          .flatMap(part -> Stream.concat(Stream.of(part), part.getPartsRecursively()))
60          // Get the inserts for each part
61          .flatMap(part -> part.getInserts(insert -> "param".equals(insert.getType().toString())))
62          // Get the param ids for each insert
63          .map(insert -> insert.getIdReference().toString())
64          .flatMap(ObjectUtils::filterNull);
65  
66      // get parameters referenced by the control's parameters
67      Stream<String> parameterIds = CollectionUtil.listOrEmpty(getParams()).stream()
68          .flatMap(ObjectUtils::filterNull)
69          .flatMap(AbstractParameter::getParameterReferences);
70  
71      return ObjectUtils.notNull(
72          Stream.concat(insertIds, parameterIds).distinct());
73    }
74  
75    @NonNull
76    public static Builder builder(@NonNull String id) {
77      return new Builder(id);
78    }
79  
80    public static class Builder {
81      @NonNull
82      private final String id;
83  
84      private String clazz;
85      private MarkupLine title;
86      private final List<Parameter> params = new LinkedList<>();
87      private final List<Property> props = new LinkedList<>();
88      private final List<Link> links = new LinkedList<>();
89      private final List<ControlPart> parts = new LinkedList<>();
90      private final List<Control> controls = new LinkedList<>();
91  
92      public Builder(@NonNull String id) {
93        this.id = Objects.requireNonNull(id, "id");
94      }
95  
96      @NonNull
97      public Builder clazz(@NonNull String value) {
98        this.clazz = Objects.requireNonNull(value);
99        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 }