001package gov.nist.secauto.oscal.lib.model; 002 003import gov.nist.secauto.metaschema.core.datatype.adapter.TokenAdapter; 004import gov.nist.secauto.metaschema.core.datatype.adapter.UriAdapter; 005import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; 006import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLineAdapter; 007import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; 008import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultilineAdapter; 009import gov.nist.secauto.metaschema.core.model.IBoundObject; 010import gov.nist.secauto.metaschema.core.model.IMetaschemaData; 011import gov.nist.secauto.metaschema.core.model.JsonGroupAsBehavior; 012import gov.nist.secauto.metaschema.core.model.constraint.IConstraint; 013import gov.nist.secauto.metaschema.core.util.ObjectUtils; 014import gov.nist.secauto.metaschema.databind.model.annotations.AllowedValue; 015import gov.nist.secauto.metaschema.databind.model.annotations.AllowedValues; 016import gov.nist.secauto.metaschema.databind.model.annotations.BoundAssembly; 017import gov.nist.secauto.metaschema.databind.model.annotations.BoundField; 018import gov.nist.secauto.metaschema.databind.model.annotations.BoundFlag; 019import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs; 020import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly; 021import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints; 022import gov.nist.secauto.oscal.lib.model.control.AbstractPart; 023import java.lang.Override; 024import java.lang.String; 025import java.net.URI; 026import java.util.LinkedList; 027import java.util.List; 028import org.apache.commons.lang3.builder.ReflectionToStringBuilder; 029import org.apache.commons.lang3.builder.ToStringStyle; 030 031/** 032 * An annotated, markup-based textual element of a control's or catalog group's definition, or a child of another part. 033 */ 034@MetaschemaAssembly( 035 formalName = "Part", 036 description = "An annotated, markup-based textual element of a control's or catalog group's definition, or a child of another part.", 037 name = "part", 038 moduleClass = OscalControlCommonModule.class, 039 remarks = "A `part` provides for logical partitioning of prose, and can be thought of as a grouping structure (e.g., section). A `part` can have child parts allowing for arbitrary nesting of prose content (e.g., statement hierarchy). A `part` can contain `prop` objects that allow for enriching prose text with structured name/value information.\n" 040 + "\n" 041 + "A `part` can be assigned an optional `id`, which allows references to this part from within a catalog, or within an instance of another OSCAL model that has a need to reference the part. Examples of where part referencing is used in OSCAL include:\n" 042 + "\n" 043 + "- Referencing a part by id to tailor (make modifications to) a control statement in a profile.\n" 044 + "- Referencing a control statement represented by a part in a system security plan implemented-requirement where a statement-level response is desired.\n" 045 + "\n" 046 + "Use of `part` and `prop` provides for a wide degree of extensibility within the OSCAL catalog model. The optional `ns` provides a means to qualify a part's `name`, allowing for organization-specific vocabularies to be defined with clear semantics. Any organization that extends OSCAL in this way should consistently assign a `ns` value that represents the organization, making a given namespace qualified `name` unique to that organization. This allows the combination of `ns` and `name` to always be unique and unambiguous, even when mixed with extensions from other organizations. Each organization is responsible for governance of their own extensions, and is strongly encouraged to publish their extensions as standards to their user community. If no `ns` is provided, the name is expected to be in the \"OSCAL\" namespace.\n" 047 + "\n" 048 + "To ensure a `ns` is unique to an organization and naming conflicts are avoided, a URI containing a DNS or other globally defined organization name should be used. For example, if FedRAMP and DoD both extend OSCAL, FedRAMP will use the `ns` `http://fedramp.gov/ns/oscal`, while DoD might use the `ns` `https://defense.gov` for any organization specific `name`.\n" 049 + "\n" 050 + "Tools that process OSCAL content are not required to interpret unrecognized OSCAL extensions; however, OSCAL compliant tools should not modify or remove unrecognized extensions, unless there is a compelling reason to do so, such as data sensitivity.", 051 valueConstraints = @ValueConstraints(allowedValues = @AllowedValues(level = IConstraint.Level.ERROR, target = "prop[has-oscal-namespace('http://csrc.nist.gov/ns/oscal')]/@name", values = {@AllowedValue(value = "label", description = "A human-readable label for the parent context, which may be rendered in place of the actual identifier for some use cases."), @AllowedValue(value = "sort-id", description = "An alternative identifier, whose value is easily sortable among other such values in the document."), @AllowedValue(value = "alt-identifier", description = "An alternate or aliased identifier for the parent context.")})) 052) 053public class ControlPart extends AbstractPart implements IBoundObject { 054 private final IMetaschemaData __metaschemaData; 055 056 /** 057 * "A unique identifier for the part." 058 */ 059 @BoundFlag( 060 formalName = "Part Identifier", 061 description = "A unique identifier for the part.", 062 name = "id", 063 typeAdapter = TokenAdapter.class, 064 remarks = "While a part is not required to have an id, it is often desirable for an identifier to be provided, which allows the part to be referenced elsewhere in OSCAL document instances. For this reason, it is RECOMMENDED to provide a part identifier." 065 ) 066 private String _id; 067 068 /** 069 * "A textual label that uniquely identifies the part's semantic type, which exists in a value space qualified by the <code>ns</code>." 070 */ 071 @BoundFlag( 072 formalName = "Part Name", 073 description = "A textual label that uniquely identifies the part's semantic type, which exists in a value space qualified by the `ns`.", 074 name = "name", 075 required = true, 076 typeAdapter = TokenAdapter.class 077 ) 078 private String _name; 079 080 /** 081 * "An optional namespace qualifying the part's <code>name</code>. This allows different organizations to associate distinct semantics with the same name." 082 */ 083 @BoundFlag( 084 formalName = "Part Namespace", 085 description = "An optional namespace qualifying the part's `name`. This allows different organizations to associate distinct semantics with the same name.", 086 name = "ns", 087 defaultValue = "http://csrc.nist.gov/ns/oscal", 088 typeAdapter = UriAdapter.class, 089 remarks = "This value must be an [absolute URI](https://pages.nist.gov/OSCAL/concepts/uri-use/#absolute-uri) that serves as a [naming system identifier](https://pages.nist.gov/OSCAL/concepts/uri-use/#use-as-a-naming-system-identifier).\n" 090 + "\n" 091 + "When a `ns` is not provided, its value should be assumed to be `http://csrc.nist.gov/ns/oscal` and the name should be a name defined by the associated OSCAL model." 092 ) 093 private URI _ns; 094 095 /** 096 * "An optional textual providing a sub-type or characterization of the part's <code>name</code>, or a category to which the part belongs." 097 */ 098 @BoundFlag( 099 formalName = "Part Class", 100 description = "An optional textual providing a sub-type or characterization of the part's `name`, or a category to which the part belongs.", 101 name = "class", 102 typeAdapter = TokenAdapter.class, 103 remarks = "One use of this flag is to distinguish or discriminate between the semantics of multiple parts of the same control with the same `name` and `ns` (since even within a given namespace it can be useful to overload a name).\n" 104 + "\n" 105 + "A `class` can be used in validation rules to express extra constraints over named items of a specific `class` value.\n" 106 + "\n" 107 + "A `class` can also be used in an OSCAL profile as a means to target an alteration to control content." 108 ) 109 private String _clazz; 110 111 @BoundField( 112 formalName = "Part Title", 113 description = "An optional name given to the part, which may be used by a tool for display and navigation.", 114 useName = "title", 115 typeAdapter = MarkupLineAdapter.class 116 ) 117 private MarkupLine _title; 118 119 @BoundAssembly( 120 formalName = "Property", 121 description = "An attribute, characteristic, or quality of the containing object expressed as a namespace qualified name/value pair.", 122 useName = "prop", 123 maxOccurs = -1, 124 groupAs = @GroupAs(name = "props", inJson = JsonGroupAsBehavior.LIST) 125 ) 126 private List<Property> _props; 127 128 @BoundField( 129 formalName = "Part Text", 130 description = "Permits multiple paragraphs, lists, tables etc.", 131 useName = "prose", 132 inXmlWrapped = false, 133 typeAdapter = MarkupMultilineAdapter.class 134 ) 135 private MarkupMultiline _prose; 136 137 @BoundAssembly( 138 formalName = "Part", 139 description = "An annotated, markup-based textual element of a control's or catalog group's definition, or a child of another part.", 140 useName = "part", 141 maxOccurs = -1, 142 groupAs = @GroupAs(name = "parts", inJson = JsonGroupAsBehavior.LIST) 143 ) 144 private List<ControlPart> _parts; 145 146 @BoundAssembly( 147 formalName = "Link", 148 description = "A reference to a local or remote resource, that has a specific relation to the containing object.", 149 useName = "link", 150 maxOccurs = -1, 151 groupAs = @GroupAs(name = "links", inJson = JsonGroupAsBehavior.LIST) 152 ) 153 private List<Link> _links; 154 155 public ControlPart() { 156 this(null); 157 } 158 159 public ControlPart(IMetaschemaData data) { 160 this.__metaschemaData = data; 161 } 162 163 @Override 164 public IMetaschemaData getMetaschemaData() { 165 return __metaschemaData; 166 } 167 168 public String getId() { 169 return _id; 170 } 171 172 public void setId(String value) { 173 _id = value; 174 } 175 176 public String getName() { 177 return _name; 178 } 179 180 public void setName(String value) { 181 _name = value; 182 } 183 184 public URI getNs() { 185 return _ns; 186 } 187 188 public void setNs(URI value) { 189 _ns = value; 190 } 191 192 public String getClazz() { 193 return _clazz; 194 } 195 196 public void setClazz(String value) { 197 _clazz = value; 198 } 199 200 public MarkupLine getTitle() { 201 return _title; 202 } 203 204 public void setTitle(MarkupLine value) { 205 _title = value; 206 } 207 208 public List<Property> getProps() { 209 return _props; 210 } 211 212 public void setProps(List<Property> value) { 213 _props = value; 214 } 215 216 /** 217 * Add a new {@link Property} item to the underlying collection. 218 * @param item the item to add 219 * @return {@code true} 220 */ 221 public boolean addProp(Property item) { 222 Property value = ObjectUtils.requireNonNull(item,"item cannot be null"); 223 if (_props == null) { 224 _props = new LinkedList<>(); 225 } 226 return _props.add(value); 227 } 228 229 /** 230 * Remove the first matching {@link Property} item from the underlying collection. 231 * @param item the item to remove 232 * @return {@code true} if the item was removed or {@code false} otherwise 233 */ 234 public boolean removeProp(Property item) { 235 Property value = ObjectUtils.requireNonNull(item,"item cannot be null"); 236 return _props != null && _props.remove(value); 237 } 238 239 public MarkupMultiline getProse() { 240 return _prose; 241 } 242 243 public void setProse(MarkupMultiline value) { 244 _prose = value; 245 } 246 247 public List<ControlPart> getParts() { 248 return _parts; 249 } 250 251 public void setParts(List<ControlPart> value) { 252 _parts = value; 253 } 254 255 /** 256 * Add a new {@link ControlPart} item to the underlying collection. 257 * @param item the item to add 258 * @return {@code true} 259 */ 260 public boolean addPart(ControlPart item) { 261 ControlPart value = ObjectUtils.requireNonNull(item,"item cannot be null"); 262 if (_parts == null) { 263 _parts = new LinkedList<>(); 264 } 265 return _parts.add(value); 266 } 267 268 /** 269 * Remove the first matching {@link ControlPart} item from the underlying collection. 270 * @param item the item to remove 271 * @return {@code true} if the item was removed or {@code false} otherwise 272 */ 273 public boolean removePart(ControlPart item) { 274 ControlPart value = ObjectUtils.requireNonNull(item,"item cannot be null"); 275 return _parts != null && _parts.remove(value); 276 } 277 278 public List<Link> getLinks() { 279 return _links; 280 } 281 282 public void setLinks(List<Link> value) { 283 _links = value; 284 } 285 286 /** 287 * Add a new {@link Link} item to the underlying collection. 288 * @param item the item to add 289 * @return {@code true} 290 */ 291 public boolean addLink(Link item) { 292 Link value = ObjectUtils.requireNonNull(item,"item cannot be null"); 293 if (_links == null) { 294 _links = new LinkedList<>(); 295 } 296 return _links.add(value); 297 } 298 299 /** 300 * Remove the first matching {@link Link} item from the underlying collection. 301 * @param item the item to remove 302 * @return {@code true} if the item was removed or {@code false} otherwise 303 */ 304 public boolean removeLink(Link item) { 305 Link value = ObjectUtils.requireNonNull(item,"item cannot be null"); 306 return _links != null && _links.remove(value); 307 } 308 309 @Override 310 public String toString() { 311 return new ReflectionToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE).toString(); 312 } 313}