001package org.gbif.api.model.crawler;
002
003import java.util.UUID;
004import javax.annotation.Nullable;
005import javax.annotation.concurrent.Immutable;
006import javax.annotation.concurrent.ThreadSafe;
007
008import com.google.common.base.Objects;
009import com.google.common.base.Strings;
010import org.codehaus.jackson.annotate.JsonCreator;
011import org.codehaus.jackson.annotate.JsonProperty;
012
013import static com.google.common.base.Preconditions.checkArgument;
014import static com.google.common.base.Preconditions.checkNotNull;
015
016/**
017 * A report of the validity of a DwC-A.
018 * <p/>
019 * A DwC-A can comprise of a core and extensions.  This implementation is intended to be used as follows:
020 * <ol>
021 *   <li>Constructed with an OccurrenceValidationReport when the core of the DwC-A is an Occurrence</li>
022 *   <li>Constructed with a GenericValidationReport when the core of the DwC-A is Taxon or Sample - optionally an
023 *   occurrence report may be given in addition to represent the occurrence extension validity</li>
024 * </ol>
025 */
026@Immutable
027@ThreadSafe
028public class DwcaValidationReport {
029  private final UUID datasetKey;
030
031  // the occurrence report may represent occurrences observed from the core or from extensions.
032  @Nullable
033  private final OccurrenceValidationReport occurrenceReport;
034
035  // the generic report of the core which will always be present unless the core is of type occurrence, where it may be
036  // null
037  @Nullable
038  private final GenericValidationReport genericReport;
039
040  private final String invalidationReason;
041
042  public boolean isValid() {
043    return invalidationReason == null
044      && (occurrenceReport == null || occurrenceReport.isValid())
045      && (genericReport == null || genericReport.isValid());
046  }
047
048  @JsonCreator
049  public DwcaValidationReport(@JsonProperty("datasetKey") UUID datasetKey,
050    @JsonProperty("occurrenceReport") OccurrenceValidationReport occurrenceReport,
051    @JsonProperty("genericReport") GenericValidationReport genericReport,
052    @JsonProperty("invalidationReason") String invalidationReason) {
053    this.datasetKey = checkNotNull(datasetKey, "datasetKey can't be null");
054    // verify one of the 3 is not null
055    checkArgument(invalidationReason!=null || occurrenceReport != null || genericReport != null,
056        "Either a report or invalidationReason cannot be null");
057    this.occurrenceReport = occurrenceReport;
058    this.genericReport = genericReport;
059    this.invalidationReason = invalidationReason;
060  }
061
062  public DwcaValidationReport(UUID datasetKey, OccurrenceValidationReport occurrenceReport) {
063    this.datasetKey = checkNotNull(datasetKey, "datasetKey can't be null");
064    this.occurrenceReport = checkNotNull(occurrenceReport, "occurrenceReport can't be null");
065    this.genericReport = null;
066    this.invalidationReason = null;
067  }
068
069  public DwcaValidationReport(UUID datasetKey, GenericValidationReport genericReport) {
070    this.datasetKey = checkNotNull(datasetKey, "datasetKey can't be null");
071    this.occurrenceReport = null;
072    this.genericReport = checkNotNull(genericReport, "genericReport can't be null");
073    this.invalidationReason = null;
074  }
075
076  public DwcaValidationReport(UUID datasetKey, String invalidationReason) {
077    this.datasetKey = checkNotNull(datasetKey, "datasetKey can't be null");
078    this.occurrenceReport = null;
079    this.genericReport = null;
080    this.invalidationReason = checkNotNull(invalidationReason, "invalidationReason can't be null");
081  }
082
083  public UUID getDatasetKey() {
084    return datasetKey;
085  }
086
087  public String getInvalidationReason() {
088    StringBuilder sb = new StringBuilder();
089    if (invalidationReason != null) {
090      sb.append(invalidationReason);
091    }
092    if (occurrenceReport != null && !occurrenceReport.isValid()) {
093      if (sb.length() > 1) {
094        sb.append("\n");
095      }
096      sb.append("Invalid Occurrences: ");
097      sb.append(occurrenceReport.getInvalidationReason());
098    }
099    if (genericReport != null && !genericReport.isValid()) {
100      if (sb.length() > 1) {
101        sb.append("\n");
102      }
103      sb.append("Invalid Checklist: ");
104      sb.append(genericReport.getInvalidationReason());
105    }
106    return Strings.emptyToNull(sb.toString());
107  }
108
109  public OccurrenceValidationReport getOccurrenceReport() {
110    return occurrenceReport;
111  }
112
113  public GenericValidationReport getGenericReport() {
114    return genericReport;
115  }
116
117  @Override
118  public int hashCode() {
119    return Objects.hashCode(datasetKey, occurrenceReport, genericReport, invalidationReason);
120  }
121
122  @Override
123  public boolean equals(Object obj) {
124    if (this == obj) {
125      return true;
126    }
127    if (obj == null || getClass() != obj.getClass()) {
128      return false;
129    }
130    final DwcaValidationReport other = (DwcaValidationReport) obj;
131    return Objects.equal(this.datasetKey, other.datasetKey)
132           && Objects.equal(this.occurrenceReport, other.occurrenceReport)
133           && Objects.equal(this.genericReport, other.genericReport)
134           && Objects.equal(this.invalidationReason, other.invalidationReason);
135  }
136
137  @Override
138  public String toString() {
139    return Objects.toStringHelper(this)
140      .add("datasetKey", datasetKey)
141      .add("invalidationReason", invalidationReason)
142      .add("occurrenceReport", occurrenceReport)
143      .add("genericReport", genericReport)
144      .toString();
145  }
146}