001/* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014package org.gbif.api.model.checklistbank; 015 016import org.gbif.api.model.common.LinneanClassification; 017import org.gbif.api.model.common.LinneanClassificationKeys; 018import org.gbif.api.util.ClassificationUtils; 019import org.gbif.api.vocabulary.Rank; 020import org.gbif.api.vocabulary.TaxonomicStatus; 021 022import java.io.Serializable; 023import java.util.List; 024import java.util.Objects; 025import java.util.StringJoiner; 026 027import javax.annotation.Nullable; 028import javax.validation.constraints.Max; 029import javax.validation.constraints.Min; 030 031import com.fasterxml.jackson.annotation.JsonProperty; 032 033import io.swagger.v3.oas.annotations.media.Schema; 034 035/** 036 * The resulting lookup of a name usage match. 037 * A single name usage key with its Linnean classification and a confidence value for the match. 038 */ 039@SuppressWarnings("unused") 040public class NameUsageMatch implements LinneanClassification, LinneanClassificationKeys, Serializable { 041 042 private static final long serialVersionUID = -8927655067465421358L; 043 044 private Integer usageKey; 045 private Integer acceptedUsageKey; 046 private String scientificName; 047 private String canonicalName; 048 private Rank rank; 049 private TaxonomicStatus status; // ACCEPTED, SYNONYM or DOUBTFUL only! 050 private Integer confidence; 051 private String note; 052 private MatchType matchType = MatchType.NONE; 053 private List<NameUsageMatch> alternatives; 054 // for LinneanClassification 055 private String kingdom; 056 private String phylum; 057 @JsonProperty("class") 058 private String clazz; 059 private String order; 060 private String family; 061 private String genus; 062 private String subgenus; 063 private String species; 064 // for LinneanClassificationKeys 065 private Integer kingdomKey; 066 private Integer phylumKey; 067 private Integer classKey; 068 private Integer orderKey; 069 private Integer familyKey; 070 private Integer genusKey; 071 private Integer subgenusKey; 072 private Integer speciesKey; 073 074 /** 075 * The confidence that the lookup was correct. 076 * A value between 0 and 100 with higher values being better matches. 077 * 078 * @return the lookup confidence 079 */ 080 @Schema(description = "The confidence that the lookup was correct.\n\n" + 081 "A value between 0 and 100 with higher values being better matches.") 082 @Min(0) 083 @Max(100) 084 public Integer getConfidence() { 085 return confidence; 086 } 087 088 /** 089 * @param confidence the confidence to set 090 */ 091 public void setConfidence(Integer confidence) { 092 this.confidence = confidence; 093 } 094 095 /** 096 * @return the type of match for this result 097 */ 098 @Schema(description = "The type of match for this result.") 099 public MatchType getMatchType() { 100 return matchType; 101 } 102 103 public void setMatchType(MatchType matchType) { 104 this.matchType = matchType; 105 } 106 107 /** 108 * @return the rank of the matching usage 109 */ 110 @Schema(description = "The rank of the matching usage.") 111 @Nullable 112 public Rank getRank() { 113 return rank; 114 } 115 116 public void setRank(Rank rank) { 117 this.rank = rank; 118 } 119 120 /** 121 * The scientific name of the matched name usage. 122 * 123 * @return the scientific name of the matched usage 124 */ 125 @Schema(description = "The scientific name of the matched name usage.") 126 @Nullable 127 public String getScientificName() { 128 return scientificName; 129 } 130 131 public void setScientificName(String scientificName) { 132 this.scientificName = scientificName; 133 } 134 135 /** 136 * The name usage key of the name usage that has been matched. 137 * 138 * @return the usageKey 139 */ 140 @Schema(description = "The name usage key of the name usage that has been matched.") 141 @Nullable 142 public Integer getUsageKey() { 143 return usageKey; 144 } 145 146 /** 147 * @param usageKey the usageKey to set 148 */ 149 public void setUsageKey(Integer usageKey) { 150 this.usageKey = usageKey; 151 } 152 153 /** 154 * The key of the accepted name usage in case the matched usage was a synonym. 155 */ 156 @Schema(description = "The key of the accepted name usage in case the matched usage was a synonym.") 157 @Nullable 158 public Integer getAcceptedUsageKey() { 159 return acceptedUsageKey; 160 } 161 162 public void setAcceptedUsageKey(Integer acceptedUsageKey) { 163 this.acceptedUsageKey = acceptedUsageKey; 164 } 165 166 /** 167 * @return true if it's a synonym 168 */ 169 @Schema(description = "True if the match name is a synonym.") 170 public boolean isSynonym() { 171 return status != null && status.isSynonym(); 172 } 173 174 /** 175 * The taxonomic status of the backbone usage. This field is required and only 3 values are allowed: 176 * <ul> 177 * <li>accepted: regular accepted taxon</li> 178 * <li>synonym: any kind of synonym</li> 179 * <li>doubtful: treated as accepted but in doubt for some reason</li> 180 * </ul> 181 */ 182 @Schema(description = "The taxonomic status of the backbone usage.") 183 public TaxonomicStatus getStatus() { 184 return status; 185 } 186 187 public void setStatus(TaxonomicStatus status) { 188 this.status = status; 189 } 190 191 @Schema(description = "Matched name's kingdom.") 192 @Override 193 @Nullable 194 public String getKingdom() { 195 return kingdom; 196 } 197 198 @Override 199 public void setKingdom(String kingdom) { 200 this.kingdom = kingdom; 201 } 202 203 @Schema(description = "Matched name's phylum.") 204 @Override 205 @Nullable 206 public String getPhylum() { 207 return phylum; 208 } 209 210 @Override 211 public void setPhylum(String phylum) { 212 this.phylum = phylum; 213 } 214 215 @Schema(description = "Matched name's class.") 216 @Override 217 @Nullable 218 public String getClazz() { 219 return clazz; 220 } 221 222 @Override 223 public void setClazz(String clazz) { 224 this.clazz = clazz; 225 } 226 227 @Schema(description = "Matched name's order.") 228 @Override 229 @Nullable 230 public String getOrder() { 231 return order; 232 } 233 234 @Override 235 public void setOrder(String order) { 236 this.order = order; 237 } 238 239 @Schema(description = "Matched name's family.") 240 @Override 241 @Nullable 242 public String getFamily() { 243 return family; 244 } 245 246 @Override 247 public void setFamily(String family) { 248 this.family = family; 249 } 250 251 @Schema(description = "Matched name's genus.") 252 @Override 253 @Nullable 254 public String getGenus() { 255 return genus; 256 } 257 258 @Override 259 public void setGenus(String genus) { 260 this.genus = genus; 261 } 262 263 @Schema(description = "Matched name's subgenus.") 264 @Override 265 @Nullable 266 public String getSubgenus() { 267 return subgenus; 268 } 269 270 @Override 271 public void setSubgenus(String subgenus) { 272 this.subgenus = subgenus; 273 } 274 275 @Schema(description = "Matched name's species.") 276 @Override 277 @Nullable 278 public String getSpecies() { 279 return species; 280 } 281 282 @Override 283 public void setSpecies(String species) { 284 this.species = species; 285 } 286 287 @Schema(description = "Usage key of the kingdom of the matched name.") 288 @Override 289 @Nullable 290 public Integer getKingdomKey() { 291 return kingdomKey; 292 } 293 294 @Override 295 public void setKingdomKey(Integer kingdomKey) { 296 this.kingdomKey = kingdomKey; 297 } 298 299 @Schema(description = "Usage key of the phylum of the matched name.") 300 @Override 301 @Nullable 302 public Integer getPhylumKey() { 303 return phylumKey; 304 } 305 306 @Override 307 public void setPhylumKey(Integer phylumKey) { 308 this.phylumKey = phylumKey; 309 } 310 311 @Schema(description = "Usage key of the class of the matched name.") 312 @Override 313 @Nullable 314 public Integer getClassKey() { 315 return classKey; 316 } 317 318 @Override 319 public void setClassKey(Integer classKey) { 320 this.classKey = classKey; 321 } 322 323 @Schema(description = "Usage key of the order of the matched name.") 324 @Override 325 @Nullable 326 public Integer getOrderKey() { 327 return orderKey; 328 } 329 330 @Override 331 public void setOrderKey(Integer orderKey) { 332 this.orderKey = orderKey; 333 } 334 335 @Schema(description = "Usage key of the family of the matched name.") 336 @Override 337 @Nullable 338 public Integer getFamilyKey() { 339 return familyKey; 340 } 341 342 @Override 343 public void setFamilyKey(Integer familyKey) { 344 this.familyKey = familyKey; 345 } 346 347 @Schema(description = "Usage key of the genus of the matched name.") 348 @Override 349 @Nullable 350 public Integer getGenusKey() { 351 return genusKey; 352 } 353 354 @Override 355 public void setGenusKey(Integer genusKey) { 356 this.genusKey = genusKey; 357 } 358 359 @Schema(description = "Usage key of the subgenus of the matched name.") 360 @Override 361 @Nullable 362 public Integer getSubgenusKey() { 363 return subgenusKey; 364 } 365 366 @Override 367 public void setSubgenusKey(Integer subgenusKey) { 368 this.subgenusKey = subgenusKey; 369 } 370 371 @Schema(description = "Usage key of the species of the matched name.") 372 @Override 373 @Nullable 374 public Integer getSpeciesKey() { 375 return speciesKey; 376 } 377 378 @Override 379 public void setSpeciesKey(Integer speciesKey) { 380 this.speciesKey = speciesKey; 381 } 382 383 @Schema(description = "Usage key of the kingdom of the matched name.") 384 @Override 385 @Nullable 386 public String getHigherRank(Rank rank) { 387 return ClassificationUtils.getHigherRank(this, rank); 388 } 389 390 @Override 391 public Integer getHigherRankKey(Rank rank) { 392 return ClassificationUtils.getHigherRankKey(this, rank); 393 } 394 395 @Nullable 396 public String getCanonicalName() { 397 return canonicalName; 398 } 399 400 public void setCanonicalName(String canonicalName) { 401 this.canonicalName = canonicalName; 402 } 403 404 /** 405 * Optional notes on the matching. 406 */ 407 @Nullable 408 public String getNote() { 409 return note; 410 } 411 412 public void setNote(String note) { 413 this.note = note; 414 } 415 416 /** 417 * @return a list of alternative matches considered 418 */ 419 @Nullable 420 public List<NameUsageMatch> getAlternatives() { 421 return alternatives; 422 } 423 424 public void setAlternatives(List<NameUsageMatch> alternatives) { 425 this.alternatives = alternatives; 426 } 427 428 @Override 429 public boolean equals(Object o) { 430 if (this == o) { 431 return true; 432 } 433 if (o == null || getClass() != o.getClass()) { 434 return false; 435 } 436 NameUsageMatch that = (NameUsageMatch) o; 437 return Objects.equals(usageKey, that.usageKey) && 438 Objects.equals(acceptedUsageKey, that.acceptedUsageKey) && 439 Objects.equals(scientificName, that.scientificName) && 440 Objects.equals(canonicalName, that.canonicalName) && 441 rank == that.rank && 442 status == that.status && 443 Objects.equals(confidence, that.confidence) && 444 Objects.equals(note, that.note) && 445 matchType == that.matchType && 446 Objects.equals(alternatives, that.alternatives) && 447 Objects.equals(kingdom, that.kingdom) && 448 Objects.equals(phylum, that.phylum) && 449 Objects.equals(clazz, that.clazz) && 450 Objects.equals(order, that.order) && 451 Objects.equals(family, that.family) && 452 Objects.equals(genus, that.genus) && 453 Objects.equals(subgenus, that.subgenus) && 454 Objects.equals(species, that.species) && 455 Objects.equals(kingdomKey, that.kingdomKey) && 456 Objects.equals(phylumKey, that.phylumKey) && 457 Objects.equals(classKey, that.classKey) && 458 Objects.equals(orderKey, that.orderKey) && 459 Objects.equals(familyKey, that.familyKey) && 460 Objects.equals(genusKey, that.genusKey) && 461 Objects.equals(subgenusKey, that.subgenusKey) && 462 Objects.equals(speciesKey, that.speciesKey); 463 } 464 465 @Override 466 public int hashCode() { 467 return Objects 468 .hash(usageKey, acceptedUsageKey, scientificName, canonicalName, rank, status, confidence, 469 note, matchType, alternatives, kingdom, phylum, clazz, order, family, genus, subgenus, 470 species, kingdomKey, phylumKey, classKey, orderKey, familyKey, genusKey, subgenusKey, 471 speciesKey); 472 } 473 474 @Override 475 public String toString() { 476 return new StringJoiner(", ", NameUsageMatch.class.getSimpleName() + "[", "]") 477 .add("usageKey=" + usageKey) 478 .add("acceptedUsageKey=" + acceptedUsageKey) 479 .add("scientificName='" + scientificName + "'") 480 .add("canonicalName='" + canonicalName + "'") 481 .add("rank=" + rank) 482 .add("status=" + status) 483 .add("confidence=" + confidence) 484 .add("note='" + note + "'") 485 .add("matchType=" + matchType) 486 .add("alternatives=" + alternatives) 487 .add("kingdom='" + kingdom + "'") 488 .add("phylum='" + phylum + "'") 489 .add("clazz='" + clazz + "'") 490 .add("order='" + order + "'") 491 .add("family='" + family + "'") 492 .add("genus='" + genus + "'") 493 .add("subgenus='" + subgenus + "'") 494 .add("species='" + species + "'") 495 .add("kingdomKey=" + kingdomKey) 496 .add("phylumKey=" + phylumKey) 497 .add("classKey=" + classKey) 498 .add("orderKey=" + orderKey) 499 .add("familyKey=" + familyKey) 500 .add("genusKey=" + genusKey) 501 .add("subgenusKey=" + subgenusKey) 502 .add("speciesKey=" + speciesKey) 503 .toString(); 504 } 505 506 public enum MatchType { 507 508 /** 509 * An exact, straight match. 510 */ 511 EXACT, 512 513 /** 514 * A fuzzy, non exact match. 515 */ 516 FUZZY, 517 518 /** 519 * Matching on a higher rank than the lowest name given. 520 */ 521 HIGHERRANK, 522 523 /** 524 * Matching on species/infraspecies level 525 * when the verbatim data in fact referred to a broader species aggregate/complex. 526 * @see <a href="https://github.com/gbif/portal-feedback/issues/2935">gbif/portal-feedback#2935</a> 527 */ 528 AGGREGATE, 529 530 /** 531 * No match or matching several names with too little information to keep apart. 532 */ 533 NONE 534 } 535}