001/*
002 * Copyright 2020-2021 Global Biodiversity Information Facility (GBIF)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.gbif.api.model.crawler;
017
018import java.util.Objects;
019import java.util.StringJoiner;
020import java.util.UUID;
021
022import javax.annotation.Nullable;
023import javax.annotation.concurrent.Immutable;
024import javax.annotation.concurrent.ThreadSafe;
025
026import org.apache.commons.lang3.StringUtils;
027
028import com.fasterxml.jackson.annotation.JsonCreator;
029import com.fasterxml.jackson.annotation.JsonIgnore;
030import com.fasterxml.jackson.annotation.JsonProperty;
031
032import static org.gbif.api.util.PreconditionUtils.checkArgument;
033/**
034 * A report of the validity of a DwC-A.
035 * <p/>
036 * A DwC-A can comprise of a core and extensions.  This implementation is intended to be used as follows:
037 * <ol>
038 *   <li>Constructed with an OccurrenceValidationReport when the core of the DwC-A is an Occurrence</li>
039 *   <li>Constructed with a GenericValidationReport when the core of the DwC-A is Taxon or Sample - optionally an
040 *   occurrence report may be given in addition to represent the occurrence extension validity</li>
041 * </ol>
042 */
043@Immutable
044@ThreadSafe
045@SuppressWarnings("unused")
046public class DwcaValidationReport {
047  private final UUID datasetKey;
048
049  // the occurrence report may represent occurrences observed from the core or from extensions.
050  @Nullable
051  private final OccurrenceValidationReport occurrenceReport;
052
053  // the generic report of the core which will always be present unless the core is of type occurrence, where it may be
054  // null
055  @Nullable
056  private final GenericValidationReport genericReport;
057
058  private final String invalidationReason;
059
060  @JsonIgnore
061  public boolean isValid() {
062    return invalidationReason == null
063      && (occurrenceReport == null || occurrenceReport.isValid())
064      && (genericReport == null || genericReport.isValid());
065  }
066
067  @JsonCreator
068  public DwcaValidationReport(@JsonProperty("datasetKey") UUID datasetKey,
069    @JsonProperty("occurrenceReport") OccurrenceValidationReport occurrenceReport,
070    @JsonProperty("genericReport") GenericValidationReport genericReport,
071    @JsonProperty("invalidationReason") String invalidationReason) {
072    this.datasetKey = Objects.requireNonNull(datasetKey, "datasetKey can't be null");
073    // verify one of the 3 is not null
074    checkArgument(invalidationReason != null || occurrenceReport != null || genericReport != null,
075      "Either a report or invalidationReason cannot be null");
076    this.occurrenceReport = occurrenceReport;
077    this.genericReport = genericReport;
078    this.invalidationReason = invalidationReason;
079  }
080
081  public DwcaValidationReport(UUID datasetKey, OccurrenceValidationReport occurrenceReport) {
082    this.datasetKey = Objects.requireNonNull(datasetKey, "datasetKey can't be null");
083    this.occurrenceReport = Objects.requireNonNull(occurrenceReport, "occurrenceReport can't be null");
084    this.genericReport = null;
085    this.invalidationReason = null;
086  }
087
088  public DwcaValidationReport(UUID datasetKey, GenericValidationReport genericReport) {
089    this.datasetKey = Objects.requireNonNull(datasetKey, "datasetKey can't be null");
090    this.occurrenceReport = null;
091    this.genericReport = Objects.requireNonNull(genericReport, "genericReport can't be null");
092    this.invalidationReason = null;
093  }
094
095  public DwcaValidationReport(UUID datasetKey, String invalidationReason) {
096    this.datasetKey = Objects.requireNonNull(datasetKey, "datasetKey can't be null");
097    this.occurrenceReport = null;
098    this.genericReport = null;
099    this.invalidationReason = Objects.requireNonNull(invalidationReason, "invalidationReason can't be null");
100  }
101
102  public UUID getDatasetKey() {
103    return datasetKey;
104  }
105
106  public String getInvalidationReason() {
107    StringBuilder sb = new StringBuilder();
108    if (invalidationReason != null) {
109      sb.append(invalidationReason);
110    }
111    if (occurrenceReport != null && !occurrenceReport.isValid()) {
112      if (sb.length() > 1) {
113        sb.append("\n");
114      }
115      sb.append("Invalid Occurrences: ");
116      sb.append(occurrenceReport.getInvalidationReason());
117    }
118    if (genericReport != null && !genericReport.isValid()) {
119      if (sb.length() > 1) {
120        sb.append("\n");
121      }
122      sb.append("Invalid Checklist: ");
123      sb.append(genericReport.getInvalidationReason());
124    }
125
126    return StringUtils.trimToNull(sb.toString());
127  }
128
129  public OccurrenceValidationReport getOccurrenceReport() {
130    return occurrenceReport;
131  }
132
133  public GenericValidationReport getGenericReport() {
134    return genericReport;
135  }
136
137  @Override
138  public boolean equals(Object o) {
139    if (this == o) {
140      return true;
141    }
142    if (o == null || getClass() != o.getClass()) {
143      return false;
144    }
145    DwcaValidationReport that = (DwcaValidationReport) o;
146    return Objects.equals(datasetKey, that.datasetKey) &&
147      Objects.equals(occurrenceReport, that.occurrenceReport) &&
148      Objects.equals(genericReport, that.genericReport) &&
149      Objects.equals(invalidationReason, that.invalidationReason);
150  }
151
152  @Override
153  public int hashCode() {
154    return Objects.hash(datasetKey, occurrenceReport, genericReport, invalidationReason);
155  }
156
157  @Override
158  public String toString() {
159    return new StringJoiner(", ", DwcaValidationReport.class.getSimpleName() + "[", "]")
160      .add("datasetKey=" + datasetKey)
161      .add("occurrenceReport=" + occurrenceReport)
162      .add("genericReport=" + genericReport)
163      .add("invalidationReason='" + invalidationReason + "'")
164      .toString();
165  }
166}