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.Constants; 017import org.gbif.api.model.common.LinneanClassification; 018import org.gbif.api.model.common.LinneanClassificationKeys; 019import org.gbif.api.util.ClassificationUtils; 020import org.gbif.api.vocabulary.NameType; 021import org.gbif.api.vocabulary.NameUsageIssue; 022import org.gbif.api.vocabulary.NomenclaturalStatus; 023import org.gbif.api.vocabulary.Origin; 024import org.gbif.api.vocabulary.Rank; 025import org.gbif.api.vocabulary.TaxonomicStatus; 026 027import java.net.URI; 028import java.util.Date; 029import java.util.EnumSet; 030import java.util.HashSet; 031import java.util.LinkedHashMap; 032import java.util.Objects; 033import java.util.Set; 034import java.util.StringJoiner; 035import java.util.UUID; 036 037import javax.annotation.Nullable; 038import javax.validation.constraints.NotNull; 039 040import com.fasterxml.jackson.annotation.JsonIgnore; 041import com.fasterxml.jackson.annotation.JsonProperty; 042 043import io.swagger.v3.oas.annotations.media.Schema; 044 045/** 046 * A usage of a <em>scientific name</em> according to one particular Checklist including the GBIF Taxonomic Backbone, 047 * the NUB. It is shown as species in the portal and API. 048 * <br> 049 * Backbone (NUB) usages have key==nubKey. Backbone usages can also be detected by either the NameUsage.isNub() method 050 * or by manually comparing the datasetKey with the fixed backbone datasetKey, see Constants.NUB_DATASET_KEY. 051 * <br> 052 * Name usages from other checklists with names that also exist in the backbone will have a nubKey that points to the related usage in the NUB. 053 * <br> 054 * To store not eagerly loaded subresources such as vernacular names or synonyms with a usage please use 055 * the {@link NameUsageContainer} class. 056 */ 057@SuppressWarnings("unused") 058public class NameUsage implements LinneanClassification, LinneanClassificationKeys { 059 060 private Integer key; 061 private Integer nubKey; 062 private Integer nameKey; 063 private String taxonID; 064 private Integer sourceTaxonKey; 065 // for LinneanClassification 066 private String kingdom; 067 private String phylum; 068 @JsonProperty("class") 069 private String clazz; 070 private String order; 071 private String family; 072 private String genus; 073 private String subgenus; 074 private String species; 075 // for LinneanClassificationKeys 076 private Integer kingdomKey; 077 private Integer phylumKey; 078 private Integer classKey; 079 private Integer orderKey; 080 private Integer familyKey; 081 private Integer genusKey; 082 private Integer subgenusKey; 083 private Integer speciesKey; 084 085 private UUID datasetKey; 086 private UUID constituentKey; 087 private Integer parentKey; 088 private String parent; 089 private Integer proParteKey; 090 private Integer acceptedKey; 091 private String accepted; 092 private Integer basionymKey; 093 private String basionym; 094 095 private String scientificName; 096 private String canonicalName; 097 private String vernacularName; 098 private String authorship; 099 private NameType nameType; 100 private Rank rank; 101 private Origin origin; 102 private TaxonomicStatus taxonomicStatus; 103 private Set<NomenclaturalStatus> nomenclaturalStatus = new HashSet<>(); 104 private String remarks; 105 private String publishedIn; 106 private String accordingTo; 107 108 private int numDescendants; 109 private URI references; 110 111 private Date modified; 112 private Date deleted; 113 private Date lastCrawled; 114 private Date lastInterpreted; 115 private Set<NameUsageIssue> issues = EnumSet.noneOf(NameUsageIssue.class); 116 117 public NameUsage() {} 118 119 public NameUsage(NameUsage other) { 120 this.key = other.key; 121 this.nubKey = other.nubKey; 122 this.nameKey = other.nameKey; 123 this.taxonID = other.taxonID; 124 this.sourceTaxonKey = other.sourceTaxonKey; 125 this.kingdom = other.kingdom; 126 this.phylum = other.phylum; 127 this.clazz = other.clazz; 128 this.order = other.order; 129 this.family = other.family; 130 this.genus = other.genus; 131 this.subgenus = other.subgenus; 132 this.species = other.species; 133 this.kingdomKey = other.kingdomKey; 134 this.phylumKey = other.phylumKey; 135 this.classKey = other.classKey; 136 this.orderKey = other.orderKey; 137 this.familyKey = other.familyKey; 138 this.genusKey = other.genusKey; 139 this.subgenusKey = other.subgenusKey; 140 this.speciesKey = other.speciesKey; 141 this.datasetKey = other.datasetKey; 142 this.constituentKey = other.constituentKey; 143 this.parentKey = other.parentKey; 144 this.parent = other.parent; 145 this.proParteKey = other.proParteKey; 146 this.acceptedKey = other.acceptedKey; 147 this.accepted = other.accepted; 148 this.basionymKey = other.basionymKey; 149 this.basionym = other.basionym; 150 this.scientificName = other.scientificName; 151 this.canonicalName = other.canonicalName; 152 this.vernacularName = other.vernacularName; 153 this.authorship = other.authorship; 154 this.nameType = other.nameType; 155 this.rank = other.rank; 156 this.origin = other.origin; 157 this.taxonomicStatus = other.taxonomicStatus; 158 this.nomenclaturalStatus = other.nomenclaturalStatus; 159 this.remarks = other.remarks; 160 this.publishedIn = other.publishedIn; 161 this.accordingTo = other.accordingTo; 162 this.numDescendants = other.numDescendants; 163 this.references = other.references; 164 this.modified = other.modified; 165 this.deleted = other.deleted; 166 this.lastCrawled = other.lastCrawled; 167 this.lastInterpreted = other.lastInterpreted; 168 this.issues = other.issues; 169 } 170 171 /** 172 * @return the name key for retrieving a parsed name object 173 */ 174 @Schema(description = "The key for retrieving a parsed name object.\n\n" + 175 "*You are more likely to need the `key` or `nubKey` properties*") 176 public Integer getNameKey() { 177 return nameKey; 178 } 179 180 public void setNameKey(Integer nameKey) { 181 this.nameKey = nameKey; 182 } 183 184 /** 185 * For backbone taxa the source taxon key refers to the original name usage that was used during backbone building 186 * and is the primary reason that this taxon exists in the backbone. 187 * <br/> 188 * All backbone name usages are built from several underlying checklist usages, 189 * but these are sorted by priority and the usage key for the highest priority one becomes the sourceTaxonKey 190 * for a backbone usage. 191 * <br/> 192 * Some backbone usages do not have any source record altogether. 193 * For example if there is a subspecies found, but no matching parent species, 194 * the missing species will be created nevertheless and has no primary source. 195 * 196 * @return The key of the name usage this backbone taxon is derived from. 197 */ 198 @Nullable 199 @Schema(description = "The key of the name usage from which this backbone taxon derives.\n" + 200 "\n" + 201 "For backbone taxa the source taxon key refers to the original name usage that was used during " + 202 "backbone building and is the primary reason that this taxon exists in the backbone.\n" + 203 "\n" + 204 "All backbone name usages are built from several underlying checklist usages, but these are sorted by priority " + 205 "and the usage key for the highest priority one becomes the sourceTaxonKey for a backbone usage.\n" + 206 "\n" + 207 "Some backbone usages do not have any source record at all; for example if there is a subspecies found, but no matching " + 208 "parent species, the missing species will be created nevertheless and has no primary source.") 209 public Integer getSourceTaxonKey() { 210 return sourceTaxonKey; 211 } 212 213 public void setSourceTaxonKey(Integer sourceTaxonKey) { 214 this.sourceTaxonKey = sourceTaxonKey; 215 } 216 217 /** 218 * @return the scientific name of the accepted name 219 */ 220 @Schema(description = "The scientific name of the accepted name.") 221 public String getAccepted() { 222 return accepted; 223 } 224 225 /** 226 * Sets the scientific name of the basionym, i.e. original name usage. 227 */ 228 public void setAccepted(String accepted) { 229 this.accepted = accepted; 230 } 231 232 /** 233 * @return the name usage key of the accepted name 234 */ 235 @Schema(description = "The name usage key of the accepted name.") 236 public Integer getAcceptedKey() { 237 return acceptedKey; 238 } 239 240 /** 241 * Sets the usage key for the accepted name. 242 */ 243 public void setAcceptedKey(Integer acceptedKey) { 244 this.acceptedKey = acceptedKey; 245 } 246 247 /** 248 * The taxon concept reference is usually a reference to some publication or author and year. 249 * <br> 250 * The dwc:taxonAccordingTo reference is usually appended to the scientific name to further qualify the concept 251 * with "sensu" or "sec." being used for concatenation. E.g. "Acer nigrum sec. Gleason Cronquist 1991". 252 * <br> 253 * In the case of backbone taxa this refers to the primary checklist the name was found in. 254 * 255 * @return the taxon concept reference 256 */ 257 @Schema(description = "The taxon concept reference.\n\n" + 258 "This is usually a reference to some publication or an author and year.\n\n" + 259 "The Darwin Core `taxonAccordingTo` reference is usually appended to the scientific name to further qualify " + 260 "the concept with “sensu” or “sec.” being used for concatenation; for example “_Acer nigrum_ sec. Gleason Cronquist 1991”.\n\n" + 261 "In the case of backbone taxa, this refers to the primary checklist in which the name was found.") 262 @Nullable 263 public String getAccordingTo() { 264 return accordingTo; 265 } 266 267 /** 268 * @param accordingTo the accordingTo to set 269 */ 270 public void setAccordingTo(String accordingTo) { 271 this.accordingTo = accordingTo; 272 } 273 274 /** 275 * Returns the authorship information for the scientific name. 276 * 277 * @return the authorship 278 */ 279 @Schema(description = "The authorship for the scientific name.") 280 @Nullable 281 public String getAuthorship() { 282 return authorship; 283 } 284 285 /** 286 * @param authorship the authorship to set 287 */ 288 public void setAuthorship(String authorship) { 289 this.authorship = authorship; 290 } 291 292 /** 293 * @return the scientific name of the basionym 294 */ 295 @Schema(description = "The scientific name of the basionym.") 296 public String getBasionym() { 297 return basionym; 298 } 299 300 /** 301 * sets the basionym name. 302 */ 303 public void setBasionym(String basionym) { 304 this.basionym = basionym; 305 } 306 307 /** 308 * Returns the earlier name (basionym) for this scientific name. Return null if the basionym does not exist. 309 * 310 * @return the basionymKey 311 */ 312 @Schema(description = "The name usage key of the basionym.") 313 @Nullable 314 public Integer getBasionymKey() { 315 return basionymKey; 316 } 317 318 /** 319 * @param basionymKey the basionymKey to set 320 */ 321 public void setBasionymKey(Integer basionymKey) { 322 this.basionymKey = basionymKey; 323 } 324 325 /** 326 * @return the canonicalName 327 */ 328 @Schema(description = "The canonical name; the name without authorship or references.") 329 @Nullable 330 public String getCanonicalName() { 331 return canonicalName; 332 } 333 334 /** 335 * @param canonicalName the canonicalName to set 336 */ 337 public void setCanonicalName(String canonicalName) { 338 this.canonicalName = canonicalName; 339 } 340 341 /** 342 * Returns the key of the checklist that "hosts" this name usage. 343 * 344 * @return the datasetKey 345 */ 346 @Schema(description = "The checklist that “hosts” this name usage.\n\n" + 347 "For a backbone name usage, this will be `d7dddbf4-2cf0-4f39-9b2a-bb099caae36c`.") 348 @NotNull 349 public UUID getDatasetKey() { 350 return datasetKey; 351 } 352 353 /** 354 * @param datasetKey the datasetKey to set 355 */ 356 public void setDatasetKey(UUID datasetKey) { 357 this.datasetKey = datasetKey; 358 } 359 360 /** 361 * Return the key that uniquely identifies this name usage. 362 * 363 * @return the key 364 */ 365 @Schema(description = "The name usage key that uniquely identifies this name usage.") 366 @NotNull 367 public Integer getKey() { 368 return key; 369 } 370 371 /** 372 * @param key the key to set 373 */ 374 public void setKey(Integer key) { 375 this.key = key; 376 } 377 378 /** 379 * @return the type of name string classified by CLB. 380 */ 381 @Schema(description = "The type of name string classified by Checklistbank.") 382 public NameType getNameType() { 383 return nameType; 384 } 385 386 /** 387 * @param nameType the type of name string 388 */ 389 public void setNameType(NameType nameType) { 390 this.nameType = nameType; 391 } 392 393 /** 394 * The status related to the conformance to the relevant rules of nomenclature. 395 * <blockquote> 396 * <p> 397 * <i>Example:</i> "invalid", "misapplied", "homotypic synonym", "accepted" 398 * </p> 399 * </blockquote> 400 * 401 * @return the set of known nomenclaturalStatus values 402 * 403 * @see <a href="http://rs.gbif.org/vocabulary/gbif/nomenclatural_status.xml">Nomenclatural Status GBIF 404 * Vocabulary</a> 405 */ 406 @Schema(description = "The nomenclatural statuses of this name usage.") 407 public Set<NomenclaturalStatus> getNomenclaturalStatus() { 408 return nomenclaturalStatus; 409 } 410 411 /** 412 * @param nomenclaturalStatus the nomenclaturalStatus to set 413 */ 414 public void setNomenclaturalStatus(Set<NomenclaturalStatus> nomenclaturalStatus) { 415 this.nomenclaturalStatus = nomenclaturalStatus; 416 } 417 418 /** 419 * @return the taxon key of the matching backbone name usage 420 */ 421 @Schema(description = "The taxon key of the matching backbone name usage.\n\n" + 422 "If this is equal to `key`, this name usage is a backbone name usage.") 423 @Nullable 424 public Integer getNubKey() { 425 return nubKey; 426 } 427 428 /** 429 * @param nubKey the nubKey to set 430 */ 431 public void setNubKey(Integer nubKey) { 432 this.nubKey = nubKey; 433 } 434 435 /** 436 * The number of all accepted taxonomic elements under this usage. 437 * 438 * @return the number of descendants 439 */ 440 @Schema(description = "A total count of all accepted taxonomic elements under this usage.") 441 public int getNumDescendants() { 442 return numDescendants; 443 } 444 445 /** 446 * @param numDescendants the n umber of descendants to set 447 */ 448 public void setNumDescendants(int numDescendants) { 449 this.numDescendants = numDescendants; 450 } 451 452 /** 453 * The origin of this name usage record, i.e. the reason why it exists. 454 * In most cases this is because the record existed explicitly in the checklist sources, but 455 * some usages are created de novo because they exist implicitly in the data. 456 * 457 * @return the name usage origin 458 * 459 * @see Origin 460 */ 461 @Schema(description = "The name usage origin.\n\n" + 462 "The origin of this name usage record, the reason it exists.\n\n" + 463 "In most cases this is because the record existed explicitly in the checklist sources, but some usages are " + 464 "created *de novo* because the exist implicitly in the data.") 465 @NotNull 466 public Origin getOrigin() { 467 return origin; 468 } 469 470 /** 471 * @param origin the origin to set 472 */ 473 public void setOrigin(Origin origin) { 474 this.origin = origin; 475 } 476 477 /** 478 * The scientific name of the parent. 479 * 480 * @return the parent name 481 */ 482 @Schema(description = "The scientific name of the parent.") 483 public String getParent() { 484 return parent; 485 } 486 487 /** 488 * @param parent the parent name to set 489 */ 490 public void setParent(String parent) { 491 this.parent = parent; 492 } 493 494 /** 495 * Returns the immediate parent. If this usage if for the highest taxonomic level, return null. 496 * 497 * @return the parentKey 498 */ 499 @Schema(description = "The name usage key of the immediate parent. Null for the highest taxonomic level.") 500 @Nullable 501 public Integer getParentKey() { 502 return parentKey; 503 } 504 505 /** 506 * @param parentKey the parentKey to set 507 */ 508 public void setParentKey(Integer parentKey) { 509 this.parentKey = parentKey; 510 } 511 512 /** 513 * Pro parte synonyms, i.e. a synonym with multiple accepted names, are grouped by a single, primary name usage key. 514 * 515 * @return the primary name usage key for a pro parte synonym or null 516 */ 517 @Schema(description = "The primary name usage key for a *pro parte* synonym.\n\n" + 518 "Synonyms with multiple accepted names are grouped by a single, primary name usage key.") 519 public Integer getProParteKey() { 520 return proParteKey; 521 } 522 523 /** 524 * Sets the pro parte usage key. 525 */ 526 public void setProParteKey(Integer proParteKey) { 527 this.proParteKey = proParteKey; 528 } 529 530 /** 531 * Original publication for this name usage. 532 * 533 * @return the publishedIn 534 */ 535 @Schema(description = "Original publication for this name usage.") 536 @Nullable 537 public String getPublishedIn() { 538 return publishedIn; 539 } 540 541 /** 542 * @param publishedIn the publishedIn to set 543 */ 544 public void setPublishedIn(String publishedIn) { 545 this.publishedIn = publishedIn; 546 } 547 548 /** 549 * Returns the rank for this usage. 550 * <blockquote> 551 * <p> 552 * <i>Example:</i> "Kingdom", "Genus" 553 * </p> 554 * </blockquote> 555 * 556 * @return the rank 557 */ 558 @Schema(description = "The rank for this usage.") 559 @Nullable 560 public Rank getRank() { 561 return rank; 562 } 563 564 /** 565 * @param rank the rank to set 566 */ 567 public void setRank(Rank rank) { 568 this.rank = rank; 569 } 570 571 /** 572 * The scientific name (with date and authorship information if applicable). 573 * <blockquote> 574 * <p> 575 * <i>Example:</i> "Coleoptera" (order), "Vespertilionidae" (family), "Manis" (genus), "Ctenomys sociabilis" (genus + 576 * specific name), "Ambystoma tigrinum diaboli" (genus + specific name + infraspecific name), 577 * "Quercus agrifolia var. oxyadenia (Torr.)" 578 * </p> 579 * </blockquote> 580 * 581 * @return the scientificName 582 */ 583 @Schema(description = "The scientific name, with date and authorship information if available.\n\n" + 584 "Examples: *Coleoptera* (order), *Vespertilionidae* (family), *Manis* (genus), *Ctenomys sociabilis* " + 585 "(genus + specificEpithet), *Ambystoma tigrinum diaboli* (zoology, genus + specific name + infraspecific name) " + 586 "*Quercus agrifolia* var. *oxyadenia* (Torr.) (botany, genus + specific epithet + infraspecific epithet + authorship)") 587 @NotNull 588 public String getScientificName() { 589 return scientificName; 590 } 591 592 /** 593 * @param scientificName the scientificName to set 594 */ 595 public void setScientificName(String scientificName) { 596 this.scientificName = scientificName; 597 } 598 599 /** 600 * Return the optional sub dataset key for this usage. 601 * 602 * @return the subDatasetKey or null 603 */ 604 @Schema(description = "The optional sub-dataset key for this usage.") 605 @Nullable 606 public UUID getConstituentKey() { 607 return constituentKey; 608 } 609 610 /** 611 * @param constituentKey to set 612 */ 613 public void setConstituentKey(UUID constituentKey) { 614 this.constituentKey = constituentKey; 615 } 616 617 /** 618 * A common or vernacular name for this usage. 619 * <blockquote> 620 * <p> 621 * <i>Example:</i> Andean Condor", "Condor Andino", "American Eagle", "Gänsegeier". 622 * </p> 623 * </blockquote> 624 * 625 * @return the vernacularName 626 */ 627 @Schema(description = "A common or vernacular name for this usage.") 628 @Nullable 629 public String getVernacularName() { 630 return vernacularName; 631 } 632 633 /** 634 * @param vernacularName the vernacularName to set 635 */ 636 public void setVernacularName(String vernacularName) { 637 this.vernacularName = vernacularName; 638 } 639 640 @Schema(description = "Kingdom.") 641 @Override 642 public String getKingdom() { 643 return kingdom; 644 } 645 646 @Override 647 public void setKingdom(String kingdom) { 648 this.kingdom = kingdom; 649 } 650 651 @Schema(description = "Phylum.") 652 @Override 653 public String getPhylum() { 654 return phylum; 655 } 656 657 @Override 658 public void setPhylum(String phylum) { 659 this.phylum = phylum; 660 } 661 662 @Schema(description = "Class.") 663 @Override 664 public String getClazz() { 665 return clazz; 666 } 667 668 @Override 669 public void setClazz(String clazz) { 670 this.clazz = clazz; 671 } 672 673 @Schema(description = "Order.") 674 @Override 675 public String getOrder() { 676 return order; 677 } 678 679 @Override 680 public void setOrder(String order) { 681 this.order = order; 682 } 683 684 @Schema(description = "Family.") 685 @Override 686 public String getFamily() { 687 return family; 688 } 689 690 @Override 691 public void setFamily(String family) { 692 this.family = family; 693 } 694 695 @Schema(description = "Genus.") 696 @Override 697 public String getGenus() { 698 return genus; 699 } 700 701 @Override 702 public void setGenus(String genus) { 703 this.genus = genus; 704 } 705 706 @Schema(description = "Subgenus.") 707 @Override 708 public String getSubgenus() { 709 return subgenus; 710 } 711 712 @Override 713 public void setSubgenus(String subgenus) { 714 this.subgenus = subgenus; 715 } 716 717 @Schema(description = "Species.") 718 @Override 719 public String getSpecies() { 720 return species; 721 } 722 723 @Override 724 public void setSpecies(String species) { 725 this.species = species; 726 } 727 728 @Schema(description = "Name usage key of the kingdom.") 729 @Override 730 public Integer getKingdomKey() { 731 return kingdomKey; 732 } 733 734 @Override 735 public void setKingdomKey(Integer kingdomKey) { 736 this.kingdomKey = kingdomKey; 737 } 738 739 @Schema(description = "Name usage key of the phylum.") 740 @Override 741 public Integer getPhylumKey() { 742 return phylumKey; 743 } 744 745 @Override 746 public void setPhylumKey(Integer phylumKey) { 747 this.phylumKey = phylumKey; 748 } 749 750 @Schema(description = "Name usage key of the class.") 751 @Override 752 public Integer getClassKey() { 753 return classKey; 754 } 755 756 @Override 757 public void setClassKey(Integer classKey) { 758 this.classKey = classKey; 759 } 760 761 @Schema(description = "Name usage key of the order.") 762 @Override 763 public Integer getOrderKey() { 764 return orderKey; 765 } 766 767 @Override 768 public void setOrderKey(Integer orderKey) { 769 this.orderKey = orderKey; 770 } 771 772 @Schema(description = "Name usage key of the family.") 773 @Override 774 public Integer getFamilyKey() { 775 return familyKey; 776 } 777 778 @Override 779 public void setFamilyKey(Integer familyKey) { 780 this.familyKey = familyKey; 781 } 782 783 @Schema(description = "Name usage key of the genus.") 784 @Override 785 public Integer getGenusKey() { 786 return genusKey; 787 } 788 789 @Override 790 public void setGenusKey(Integer genusKey) { 791 this.genusKey = genusKey; 792 } 793 794 @Schema(description = "Name usage key of the subgenus.") 795 @Override 796 public Integer getSubgenusKey() { 797 return subgenusKey; 798 } 799 800 @Override 801 public void setSubgenusKey(Integer subgenusKey) { 802 this.subgenusKey = subgenusKey; 803 } 804 805 @Schema(description = "Name usage key of the species.") 806 @Override 807 public Integer getSpeciesKey() { 808 return speciesKey; 809 } 810 811 @Override 812 public void setSpeciesKey(Integer speciesKey) { 813 this.speciesKey = speciesKey; 814 } 815 816 @Schema(description = "Remarks on the name usage.") 817 public String getRemarks() { 818 return remarks; 819 } 820 821 public void setRemarks(String remarks) { 822 this.remarks = remarks; 823 } 824 825 /** 826 * @return the canonicalName or scientific name in case its null 827 */ 828 @Nullable 829 @JsonIgnore 830 public String getCanonicalOrScientificName() { 831 return canonicalName == null ? scientificName : canonicalName; 832 } 833 834 /** 835 * A URI link or reference to the source of this record, the record's "homepage". 836 * <blockquote> 837 * <p> 838 * <i>Example:</i> https://www.catalogueoflife.org/data/taxon/4R5YN 839 * </p> 840 * </blockquote> 841 * 842 * @return the link 843 */ 844 @Schema(description = "A URI link or reference to the source of the record, the record's “homepage”.") 845 @Nullable 846 public URI getReferences() { 847 return references; 848 } 849 850 public void setReferences(URI references) { 851 this.references = references; 852 } 853 854 @Override 855 public String getHigherRank(Rank rank) { 856 return ClassificationUtils.getHigherRank(this, rank); 857 } 858 859 @Override 860 public Integer getHigherRankKey(Rank rank) { 861 return ClassificationUtils.getHigherRankKey(this, rank); 862 } 863 864 /** 865 * An ordered map with entries for all higher Linnean ranks down to the actual direct parent of this usage. 866 * The map starts with the highest rank, e.g. the kingdom and maps the name usage key to its canonical name. 867 * The name usage itself is never included, even though a higher rank might point to the usage itself. 868 * 869 * @return map of higher ranks 870 */ 871 @NotNull 872 @JsonIgnore 873 public LinkedHashMap<Integer, String> getHigherClassificationMap() { 874 return ClassificationUtils.getHigherClassificationMap(this, key, parentKey, parent); 875 } 876 877 /** 878 * The original taxonID of the name usage as found in the source. 879 * For backbone taxa and name usages with an origin different to SOURCE this is null. 880 */ 881 @Schema(description = "The original taxonID of the name usage as found in the source.\n\n" + 882 "For backbone taxa and name usages with an origin different to SOURCE this is null.") 883 @Nullable 884 public String getTaxonID() { 885 return taxonID; 886 } 887 888 /** 889 * The taxonomic status of the name usage. 890 * Can be null, but for all synonyms with an accepted name usage it is guaranteed to exist. 891 * 892 * @return the taxonomicStatus, can be null 893 */ 894 @Schema(description = "The taxonomic status of the name usage.\n\n" + 895 "Can be null, but for all synonyms with an accepted name usage it is guaranteed to exist.") 896 @Nullable 897 public TaxonomicStatus getTaxonomicStatus() { 898 return taxonomicStatus; 899 } 900 901 /** 902 * @param taxonomicStatus the taxonomicStatus to set 903 */ 904 public void setTaxonomicStatus(TaxonomicStatus taxonomicStatus) { 905 this.taxonomicStatus = taxonomicStatus; 906 } 907 908 /** 909 * The interpreted dc:modified from the verbatim source data. 910 * Ideally indicating when a record was last modified in the source. 911 */ 912 @Schema(description = "The interpreted dc:modified from the verbatim source data, ideally indicating when a record " + 913 "was last modified in the source.") 914 @Nullable 915 public Date getModified() { 916 return modified; 917 } 918 919 public void setModified(Date modified) { 920 this.modified = modified; 921 } 922 923 /** 924 * The date this record was deleted. 925 * Logical deletions only occur for backbone usages! 926 */ 927 @Schema(description = "The date this record was deleted.\n\n" + 928 "*Only backbone name usages are soft-deleted.*") 929 @Nullable 930 public Date getDeleted() { 931 return deleted; 932 } 933 934 public void setDeleted(Date deleted) { 935 this.deleted = deleted; 936 } 937 938 /** 939 * The date this record was last crawled during clb indexing. 940 */ 941 @Schema(description = "The date this record was last crawled (downloaded from the source) during Checklistbank indexing.") 942 @Nullable 943 public Date getLastCrawled() { 944 return lastCrawled; 945 } 946 947 public void setLastCrawled(Date lastCrawled) { 948 this.lastCrawled = lastCrawled; 949 } 950 951 /** 952 * The date this record was last interpreted during indexing. 953 * This includes matching to the backbone. 954 */ 955 @Schema(description = "The date this record was last interpreted during indexing. This includes matching to the backbone.") 956 @Nullable 957 public Date getLastInterpreted() { 958 return lastInterpreted; 959 } 960 961 public void setLastInterpreted(Date lastInterpreted) { 962 this.lastInterpreted = lastInterpreted; 963 } 964 965 @Schema(description = "Data quality issues found during Checklistbank interpretation.") 966 @NotNull 967 public Set<NameUsageIssue> getIssues() { 968 return issues; 969 } 970 971 public void setIssues(Set<NameUsageIssue> issues) { 972 Objects.requireNonNull(issues, "Issues cannot be null"); 973 this.issues = issues.isEmpty() ? EnumSet.noneOf(NameUsageIssue.class) : EnumSet.copyOf(issues); 974 } 975 976 public void addIssue(NameUsageIssue issue) { 977 Objects.requireNonNull(issue, "Issue needs to be specified"); 978 this.issues.add(issue); 979 } 980 981 @JsonIgnore 982 public boolean isNub() { 983 return datasetKey.equals(Constants.NUB_DATASET_KEY); 984 } 985 986 /** 987 * True for pro parte synonyms with multiple accepted usages. 988 * 989 * @return true if proParte, false otherwise 990 */ 991 @JsonIgnore 992 public boolean isProParte() { 993 return proParteKey != null; 994 } 995 996 /** 997 * Convenience method using the taxonomicStatus field. 998 * @return true if it's a synonym 999 */ 1000 @JsonIgnore 1001 public boolean isSynonym() { 1002 return taxonomicStatus != null && taxonomicStatus.isSynonym(); 1003 } 1004 1005 public void setTaxonID(String taxonID) { 1006 this.taxonID = taxonID; 1007 } 1008 1009 @Override 1010 public boolean equals(Object o) { 1011 if (this == o) { 1012 return true; 1013 } 1014 if (o == null || getClass() != o.getClass()) { 1015 return false; 1016 } 1017 NameUsage nameUsage = (NameUsage) o; 1018 return numDescendants == nameUsage.numDescendants && 1019 Objects.equals(key, nameUsage.key) && 1020 Objects.equals(nubKey, nameUsage.nubKey) && 1021 Objects.equals(nameKey, nameUsage.nameKey) && 1022 Objects.equals(taxonID, nameUsage.taxonID) && 1023 Objects.equals(kingdom, nameUsage.kingdom) && 1024 Objects.equals(phylum, nameUsage.phylum) && 1025 Objects.equals(clazz, nameUsage.clazz) && 1026 Objects.equals(order, nameUsage.order) && 1027 Objects.equals(family, nameUsage.family) && 1028 Objects.equals(genus, nameUsage.genus) && 1029 Objects.equals(subgenus, nameUsage.subgenus) && 1030 Objects.equals(species, nameUsage.species) && 1031 Objects.equals(kingdomKey, nameUsage.kingdomKey) && 1032 Objects.equals(phylumKey, nameUsage.phylumKey) && 1033 Objects.equals(classKey, nameUsage.classKey) && 1034 Objects.equals(orderKey, nameUsage.orderKey) && 1035 Objects.equals(familyKey, nameUsage.familyKey) && 1036 Objects.equals(genusKey, nameUsage.genusKey) && 1037 Objects.equals(subgenusKey, nameUsage.subgenusKey) && 1038 Objects.equals(speciesKey, nameUsage.speciesKey) && 1039 Objects.equals(datasetKey, nameUsage.datasetKey) && 1040 Objects.equals(constituentKey, nameUsage.constituentKey) && 1041 Objects.equals(parentKey, nameUsage.parentKey) && 1042 Objects.equals(parent, nameUsage.parent) && 1043 Objects.equals(proParteKey, nameUsage.proParteKey) && 1044 Objects.equals(acceptedKey, nameUsage.acceptedKey) && 1045 Objects.equals(accepted, nameUsage.accepted) && 1046 Objects.equals(basionymKey, nameUsage.basionymKey) && 1047 Objects.equals(basionym, nameUsage.basionym) && 1048 Objects.equals(scientificName, nameUsage.scientificName) && 1049 Objects.equals(canonicalName, nameUsage.canonicalName) && 1050 Objects.equals(vernacularName, nameUsage.vernacularName) && 1051 Objects.equals(authorship, nameUsage.authorship) && 1052 nameType == nameUsage.nameType && 1053 rank == nameUsage.rank && 1054 origin == nameUsage.origin && 1055 taxonomicStatus == nameUsage.taxonomicStatus && 1056 Objects.equals(nomenclaturalStatus, nameUsage.nomenclaturalStatus) && 1057 Objects.equals(remarks, nameUsage.remarks) && 1058 Objects.equals(publishedIn, nameUsage.publishedIn) && 1059 Objects.equals(accordingTo, nameUsage.accordingTo) && 1060 Objects.equals(references, nameUsage.references) && 1061 Objects.equals(modified, nameUsage.modified) && 1062 Objects.equals(deleted, nameUsage.deleted) && 1063 Objects.equals(lastCrawled, nameUsage.lastCrawled) && 1064 Objects.equals(lastInterpreted, nameUsage.lastInterpreted) && 1065 Objects.equals(issues, nameUsage.issues); 1066 } 1067 1068 @Override 1069 public int hashCode() { 1070 return Objects 1071 .hash(key, nubKey, nameKey, taxonID, kingdom, phylum, clazz, order, family, genus, subgenus, 1072 species, kingdomKey, phylumKey, classKey, orderKey, familyKey, genusKey, subgenusKey, 1073 speciesKey, datasetKey, constituentKey, parentKey, parent, proParteKey, acceptedKey, 1074 accepted, basionymKey, basionym, scientificName, canonicalName, vernacularName, 1075 authorship, nameType, rank, origin, taxonomicStatus, nomenclaturalStatus, remarks, 1076 publishedIn, accordingTo, numDescendants, references, modified, deleted, lastCrawled, 1077 lastInterpreted, issues); 1078 } 1079 1080 @Override 1081 public String toString() { 1082 return new StringJoiner(", ", NameUsage.class.getSimpleName() + "[", "]") 1083 .add("key=" + key) 1084 .add("nubKey=" + nubKey) 1085 .add("nameKey=" + nameKey) 1086 .add("taxonID='" + taxonID + "'") 1087 .add("sourceTaxonKey=" + sourceTaxonKey) 1088 .add("kingdom='" + kingdom + "'") 1089 .add("phylum='" + phylum + "'") 1090 .add("clazz='" + clazz + "'") 1091 .add("order='" + order + "'") 1092 .add("family='" + family + "'") 1093 .add("genus='" + genus + "'") 1094 .add("subgenus='" + subgenus + "'") 1095 .add("species='" + species + "'") 1096 .add("kingdomKey=" + kingdomKey) 1097 .add("phylumKey=" + phylumKey) 1098 .add("classKey=" + classKey) 1099 .add("orderKey=" + orderKey) 1100 .add("familyKey=" + familyKey) 1101 .add("genusKey=" + genusKey) 1102 .add("subgenusKey=" + subgenusKey) 1103 .add("speciesKey=" + speciesKey) 1104 .add("datasetKey=" + datasetKey) 1105 .add("constituentKey=" + constituentKey) 1106 .add("parentKey=" + parentKey) 1107 .add("parent='" + parent + "'") 1108 .add("proParteKey=" + proParteKey) 1109 .add("acceptedKey=" + acceptedKey) 1110 .add("accepted='" + accepted + "'") 1111 .add("basionymKey=" + basionymKey) 1112 .add("basionym='" + basionym + "'") 1113 .add("scientificName='" + scientificName + "'") 1114 .add("canonicalName='" + canonicalName + "'") 1115 .add("vernacularName='" + vernacularName + "'") 1116 .add("authorship='" + authorship + "'") 1117 .add("nameType=" + nameType) 1118 .add("rank=" + rank) 1119 .add("origin=" + origin) 1120 .add("taxonomicStatus=" + taxonomicStatus) 1121 .add("nomenclaturalStatus=" + nomenclaturalStatus) 1122 .add("remarks='" + remarks + "'") 1123 .add("publishedIn='" + publishedIn + "'") 1124 .add("accordingTo='" + accordingTo + "'") 1125 .add("numDescendants=" + numDescendants) 1126 .add("references=" + references) 1127 .add("modified=" + modified) 1128 .add("deleted=" + deleted) 1129 .add("lastCrawled=" + lastCrawled) 1130 .add("lastInterpreted=" + lastInterpreted) 1131 .add("issues=" + issues) 1132 .toString(); 1133 } 1134}