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.metaschema.core.model.constraint;
028
029import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
030import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.Level;
031import gov.nist.secauto.metaschema.core.model.validation.IValidationFinding;
032import gov.nist.secauto.metaschema.core.util.CollectionUtil;
033
034import java.net.URI;
035import java.util.Collections;
036import java.util.Comparator;
037import java.util.List;
038
039import edu.umd.cs.findbugs.annotations.NonNull;
040import edu.umd.cs.findbugs.annotations.Nullable;
041
042/**
043 * Represents an individual constraint validation issue.
044 */
045public class ConstraintValidationFinding implements IValidationFinding { // NOPMD - intentional
046  @NonNull
047  private final List<? extends IConstraint> constraints;
048  @NonNull
049  private final CharSequence message;
050  @NonNull
051  private final INodeItem node;
052  @NonNull
053  private final List<? extends INodeItem> targets;
054  private final Throwable cause;
055  private final Level severity;
056
057  private ConstraintValidationFinding(
058      @NonNull List<? extends IConstraint> constraints,
059      @NonNull INodeItem node,
060      @NonNull CharSequence message,
061      @NonNull List<? extends INodeItem> targets,
062      @NonNull Level severity,
063      @Nullable Throwable cause) {
064    this.constraints = constraints;
065    this.node = node;
066    this.message = message;
067    this.targets = targets;
068    this.severity = severity;
069    this.cause = cause;
070  }
071
072  public List<? extends IConstraint> getConstraints() {
073    return constraints;
074  }
075
076  @Override
077  public CharSequence getMessage() {
078    return message;
079  }
080
081  public INodeItem getNode() {
082    return node;
083  }
084
085  public List<? extends INodeItem> getTargets() {
086    return targets;
087  }
088
089  @Override
090  public Throwable getCause() {
091    return cause;
092  }
093
094  @SuppressWarnings("null")
095  @Override
096  public Level getSeverity() {
097    return severity;
098  }
099
100  @SuppressWarnings("null")
101  @Override
102  public @NonNull URI getDocumentUri() {
103    return getNode().getBaseUri();
104  }
105
106  @NonNull
107  public static Builder builder(@NonNull List<? extends IConstraint> constraints, @NonNull INodeItem node) {
108    return new Builder(constraints, node);
109  }
110
111  @NonNull
112  public static Builder builder(@NonNull IConstraint constraint, @NonNull INodeItem node) {
113    return new Builder(CollectionUtil.singletonList(constraint), node);
114  }
115
116  public static final class Builder {
117    @NonNull
118    private final List<? extends IConstraint> constraints;
119    @NonNull
120    private final INodeItem node;
121    private CharSequence message;
122    private List<? extends INodeItem> targets;
123    private Throwable cause;
124    private Level severity;
125
126    private Builder(@NonNull List<? extends IConstraint> constraints, @NonNull INodeItem node) {
127      this.constraints = constraints;
128      this.node = node;
129    }
130
131    @NonNull
132    public Builder message(@NonNull CharSequence message) {
133      this.message = message;
134      return this;
135    }
136
137    @NonNull
138    public Builder target(@NonNull INodeItem target) {
139      this.targets = Collections.singletonList(target);
140      return this;
141    }
142
143    @NonNull
144    public Builder targets(@NonNull List<? extends INodeItem> targets) {
145      this.targets = CollectionUtil.unmodifiableList(targets);
146      return this;
147    }
148
149    @NonNull
150    public Builder cause(@NonNull Throwable cause) {
151      this.cause = cause;
152      return this;
153    }
154
155    @NonNull
156    public Builder severity(@NonNull Level severity) {
157      this.severity = severity;
158      return this;
159    }
160
161    @NonNull
162    public ConstraintValidationFinding build() {
163      if (message == null) {
164        throw new IllegalStateException("Missing message");
165      }
166
167      Level severity = this.severity == null ? constraints.stream()
168          .map(IConstraint::getLevel)
169          .max(Comparator.comparing(Level::ordinal))
170          .get() : this.severity;
171
172      List<? extends INodeItem> targets = this.targets == null ? CollectionUtil.emptyList() : this.targets;
173
174      assert message != null;
175      assert targets != null;
176      assert severity != null;
177
178      return new ConstraintValidationFinding(
179          constraints,
180          node,
181          message,
182          targets,
183          severity,
184          cause);
185    }
186  }
187}