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