001/*
002 * Portions of this software was developed by employees of the National Institute
003 * of Standards and Technology (NIST), an agency of the Federal Government and is
004 * being made available as a public service. Pursuant to title 17 United States
005 * Code Section 105, works of NIST employees are not subject to copyright
006 * protection in the United States. This software may be subject to foreign
007 * copyright. Permission in the United States and in foreign countries, to the
008 * extent that NIST may hold copyright, to use, copy, modify, create derivative
009 * works, and distribute this software and its documentation without fee is hereby
010 * granted on a non-exclusive basis, provided that this notice and disclaimer
011 * of warranty appears in all copies.
012 *
013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
025 */
026
027package gov.nist.secauto.oscal.lib.model.metadata;
028
029import gov.nist.secauto.metaschema.model.common.util.CollectionUtil;
030import gov.nist.secauto.oscal.lib.model.Property;
031
032import java.net.URI;
033import java.util.LinkedList;
034import java.util.List;
035import java.util.Objects;
036import java.util.Optional;
037import java.util.UUID;
038import java.util.stream.Collectors;
039import java.util.stream.Stream;
040
041import javax.xml.namespace.QName;
042
043import edu.umd.cs.findbugs.annotations.NonNull;
044
045public abstract class AbstractProperty implements IProperty {
046
047  @NonNull
048  public static QName qname(URI namespace, @NonNull String name) {
049    return new QName(normalizeNamespace(namespace).toString(), name);
050  }
051
052  @NonNull
053  public static QName qname(@NonNull String name) {
054    return new QName(OSCAL_NAMESPACE.toString(), name);
055  }
056
057  @NonNull
058  public static URI normalizeNamespace(URI namespace) {
059    URI propertyNamespace = namespace;
060    if (propertyNamespace == null) {
061      propertyNamespace = OSCAL_NAMESPACE;
062    }
063    return propertyNamespace;
064  }
065
066  @SuppressWarnings("null")
067  @NonNull
068  public static Optional<Property> find(List<Property> props, @NonNull QName qname) {
069    return CollectionUtil.listOrEmpty(props).stream().filter(prop -> qname.equals(prop.getQName())).findFirst();
070  }
071
072  protected AbstractProperty() {
073    // only concrete classes should construct
074  }
075
076  public static List<Property> merge(@NonNull List<Property> original, @NonNull List<Property> additional) {
077    return Stream.concat(original.stream(), additional.stream())
078        .collect(Collectors.toCollection(LinkedList::new));
079  }
080
081  @Override
082  public boolean isNamespaceEqual(@NonNull URI namespace) {
083    return normalizeNamespace(getNs()).equals(namespace);
084  }
085
086  @NonNull
087  public QName getQName() {
088    return new QName(normalizeNamespace(getNs()).toString(), getName());
089  }
090
091  @NonNull
092  public static Builder builder(@NonNull String name) {
093    return new Builder(name);
094  }
095
096  public static class Builder {
097    @NonNull
098    private final String name;
099
100    private UUID uuid;
101    private URI namespace;
102    private String value;
103    private String clazz;
104
105    public Builder(@NonNull String name) {
106      this.name = Objects.requireNonNull(name, "name");
107    }
108
109    @NonNull
110    public Builder uuid(@NonNull UUID uuid) {
111      this.uuid = Objects.requireNonNull(uuid);
112      return this;
113    }
114
115    @NonNull
116    public Builder namespace(@NonNull URI namespace) {
117      if (IProperty.OSCAL_NAMESPACE.equals(namespace)) {
118        this.namespace = null;
119      } else {
120        this.namespace = Objects.requireNonNull(namespace);
121      }
122      return this;
123    }
124
125    @NonNull
126    public Builder value(@NonNull String value) {
127      this.value = Objects.requireNonNull(value);
128      return this;
129    }
130
131    @NonNull
132    public Builder clazz(@NonNull String clazz) {
133      this.clazz = Objects.requireNonNull(clazz);
134      return this;
135    }
136
137    @NonNull
138    public Property build() {
139      Property retval = new Property();
140      retval.setName(name);
141      retval.setValue(Objects.requireNonNull(value, "value"));
142      if (uuid != null) {
143        retval.setUuid(uuid);
144      }
145      if (namespace != null) {
146        retval.setNs(namespace);
147      }
148      if (clazz != null) {
149        retval.setClazz(clazz);
150      }
151
152      return retval;
153    }
154  }
155}