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}