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}