ControlPart.java

package gov.nist.secauto.oscal.lib.model;

import gov.nist.secauto.metaschema.binding.model.annotations.AllowedValue;
import gov.nist.secauto.metaschema.binding.model.annotations.AllowedValues;
import gov.nist.secauto.metaschema.binding.model.annotations.BoundAssembly;
import gov.nist.secauto.metaschema.binding.model.annotations.BoundField;
import gov.nist.secauto.metaschema.binding.model.annotations.BoundFieldValue;
import gov.nist.secauto.metaschema.binding.model.annotations.BoundFlag;
import gov.nist.secauto.metaschema.binding.model.annotations.GroupAs;
import gov.nist.secauto.metaschema.binding.model.annotations.MetaschemaAssembly;
import gov.nist.secauto.metaschema.binding.model.annotations.ValueConstraints;
import gov.nist.secauto.metaschema.model.common.JsonGroupAsBehavior;
import gov.nist.secauto.metaschema.model.common.constraint.IConstraint;
import gov.nist.secauto.metaschema.model.common.datatype.adapter.TokenAdapter;
import gov.nist.secauto.metaschema.model.common.datatype.adapter.UriAdapter;
import gov.nist.secauto.metaschema.model.common.datatype.markup.MarkupLine;
import gov.nist.secauto.metaschema.model.common.datatype.markup.MarkupLineAdapter;
import gov.nist.secauto.metaschema.model.common.datatype.markup.MarkupMultiline;
import gov.nist.secauto.metaschema.model.common.datatype.markup.MarkupMultilineAdapter;
import gov.nist.secauto.metaschema.model.common.util.ObjectUtils;
import gov.nist.secauto.oscal.lib.model.control.AbstractPart;
import java.lang.Override;
import java.lang.String;
import java.net.URI;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang3.builder.MultilineRecursiveToStringStyle;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;

/**
 * An annotated, markup-based textual element of a control's or catalog group's definition, or a child of another part.
 */
@MetaschemaAssembly(
    formalName = "Part",
    description = "An annotated, markup-based textual element of a control's or catalog group's definition, or a child of another part.",
    name = "part",
    metaschema = OscalControlCommonMetaschema.class,
    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"
            + "\n"
            + "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"
            + "\n"
            + "- Referencing a part by id to tailor (make modifications to) a control statement in a profile.\n"
            + "- Referencing a control statement represented by a part in a system security plan implemented-requirement where a statement-level response is desired.\n"
            + "\n"
            + "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"
            + "\n"
            + "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"
            + "\n"
            + "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."
)
@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.")})
)
public class ControlPart extends AbstractPart {
  @BoundFlag(
      formalName = "Part Identifier",
      description = "A unique identifier for the part.",
      useName = "id",
      typeAdapter = TokenAdapter.class,
      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."
  )
  private String _id;

  @BoundFlag(
      formalName = "Part Name",
      description = "A textual label that uniquely identifies the part's semantic type, which exists in a value space qualified by the `ns`.",
      useName = "name",
      required = true,
      typeAdapter = TokenAdapter.class
  )
  private String _name;

  @BoundFlag(
      formalName = "Part Namespace",
      description = "An optional namespace qualifying the part's `name`. This allows different organizations to associate distinct semantics with the same name.",
      useName = "ns",
      typeAdapter = UriAdapter.class,
      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"
              + "\n"
              + "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."
  )
  private URI _ns;

  @BoundFlag(
      formalName = "Part Class",
      description = "An optional textual providing a sub-type or characterization of the part's `name`, or a category to which the part belongs.",
      useName = "class",
      typeAdapter = TokenAdapter.class,
      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"
              + "\n"
              + "A `class` can be used in validation rules to express extra constraints over named items of a specific `class` value.\n"
              + "\n"
              + "A `class` can also be used in an OSCAL profile as a means to target an alteration to control content."
  )
  private String _clazz;

  /**
   * "An optional name given to the part, which may be used by a tool for display and navigation."
   */
  @BoundField(
      formalName = "Part Title",
      description = "An optional name given to the part, which may be used by a tool for display and navigation.",
      useName = "title"
  )
  @BoundFieldValue(
      typeAdapter = MarkupLineAdapter.class
  )
  private MarkupLine _title;

  @BoundAssembly(
      formalName = "Property",
      description = "An attribute, characteristic, or quality of the containing object expressed as a namespace qualified name/value pair.",
      useName = "prop",
      maxOccurs = -1
  )
  @GroupAs(
      name = "props",
      inJson = JsonGroupAsBehavior.LIST
  )
  private List<Property> _props;

  /**
   * "Permits multiple paragraphs, lists, tables etc."
   */
  @BoundField(
      formalName = "Part Text",
      description = "Permits multiple paragraphs, lists, tables etc.",
      useName = "prose",
      namespace = "##none",
      inXmlWrapped = false
  )
  @BoundFieldValue(
      typeAdapter = MarkupMultilineAdapter.class
  )
  private MarkupMultiline _prose;

  @BoundAssembly(
      formalName = "Part",
      description = "An annotated, markup-based textual element of a control's or catalog group's definition, or a child of another part.",
      useName = "part",
      maxOccurs = -1
  )
  @GroupAs(
      name = "parts",
      inJson = JsonGroupAsBehavior.LIST
  )
  private List<ControlPart> _parts;

  @BoundAssembly(
      formalName = "Link",
      description = "A reference to a local or remote resource, that has a specific relation to the containing object.",
      useName = "link",
      maxOccurs = -1
  )
  @GroupAs(
      name = "links",
      inJson = JsonGroupAsBehavior.LIST
  )
  private List<Link> _links;

  public ControlPart() {
  }

  public String getId() {
    return _id;
  }

  public void setId(String value) {
    _id = value;
  }

  public String getName() {
    return _name;
  }

  public void setName(String value) {
    _name = value;
  }

  public URI getNs() {
    return _ns;
  }

  public void setNs(URI value) {
    _ns = value;
  }

  public String getClazz() {
    return _clazz;
  }

  public void setClazz(String value) {
    _clazz = value;
  }

  public MarkupLine getTitle() {
    return _title;
  }

  public void setTitle(MarkupLine value) {
    _title = value;
  }

  public List<Property> getProps() {
    return _props;
  }

  public void setProps(List<Property> value) {
    _props = value;
  }

  /**
   * Add a new {@link Property} item to the underlying collection.
   * @param item the item to add
   * @return {@code true}
   */
  public boolean addProp(Property item) {
    Property value = ObjectUtils.requireNonNull(item,"item cannot be null");
    if (_props == null) {
      _props = new LinkedList<>();
    }
    return _props.add(value);
  }

  /**
   * Remove the first matching {@link Property} item from the underlying collection.
   * @param item the item to remove
   * @return {@code true} if the item was removed or {@code false} otherwise
   */
  public boolean removeProp(Property item) {
    Property value = ObjectUtils.requireNonNull(item,"item cannot be null");
    return _props == null ? false : _props.remove(value);
  }

  public MarkupMultiline getProse() {
    return _prose;
  }

  public void setProse(MarkupMultiline value) {
    _prose = value;
  }

  public List<ControlPart> getParts() {
    return _parts;
  }

  public void setParts(List<ControlPart> value) {
    _parts = value;
  }

  /**
   * Add a new {@link ControlPart} item to the underlying collection.
   * @param item the item to add
   * @return {@code true}
   */
  public boolean addPart(ControlPart item) {
    ControlPart value = ObjectUtils.requireNonNull(item,"item cannot be null");
    if (_parts == null) {
      _parts = new LinkedList<>();
    }
    return _parts.add(value);
  }

  /**
   * Remove the first matching {@link ControlPart} item from the underlying collection.
   * @param item the item to remove
   * @return {@code true} if the item was removed or {@code false} otherwise
   */
  public boolean removePart(ControlPart item) {
    ControlPart value = ObjectUtils.requireNonNull(item,"item cannot be null");
    return _parts == null ? false : _parts.remove(value);
  }

  public List<Link> getLinks() {
    return _links;
  }

  public void setLinks(List<Link> value) {
    _links = value;
  }

  /**
   * Add a new {@link Link} item to the underlying collection.
   * @param item the item to add
   * @return {@code true}
   */
  public boolean addLink(Link item) {
    Link value = ObjectUtils.requireNonNull(item,"item cannot be null");
    if (_links == null) {
      _links = new LinkedList<>();
    }
    return _links.add(value);
  }

  /**
   * Remove the first matching {@link Link} item from the underlying collection.
   * @param item the item to remove
   * @return {@code true} if the item was removed or {@code false} otherwise
   */
  public boolean removeLink(Link item) {
    Link value = ObjectUtils.requireNonNull(item,"item cannot be null");
    return _links == null ? false : _links.remove(value);
  }

  @Override
  public String toString() {
    return new ReflectionToStringBuilder(this, MultilineRecursiveToStringStyle.MULTI_LINE_STYLE).toString();
  }
}