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.registry; 015 016import com.fasterxml.jackson.annotation.JsonRawValue; 017 018import com.fasterxml.jackson.core.JsonParser; 019import com.fasterxml.jackson.databind.DeserializationContext; 020import com.fasterxml.jackson.databind.JsonDeserializer; 021 022import com.fasterxml.jackson.databind.JsonNode; 023 024import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 025 026import lombok.Data; 027import lombok.Getter; 028import lombok.NoArgsConstructor; 029import lombok.Setter; 030 031import org.gbif.api.annotation.Experimental; 032import org.gbif.api.model.common.DOI; 033import org.gbif.api.model.registry.eml.Collection; 034import org.gbif.api.model.registry.eml.DataDescription; 035import org.gbif.api.model.registry.eml.KeywordCollection; 036import org.gbif.api.model.registry.eml.Project; 037import org.gbif.api.model.registry.eml.SamplingDescription; 038import org.gbif.api.model.registry.eml.TaxonomicCoverages; 039import org.gbif.api.model.registry.eml.curatorial.CuratorialUnitComposite; 040import org.gbif.api.model.registry.eml.geospatial.GeospatialCoverage; 041import org.gbif.api.model.registry.eml.temporal.TemporalCoverage; 042import org.gbif.api.util.HttpURI; 043import org.gbif.api.vocabulary.Country; 044import org.gbif.api.vocabulary.DatasetSubtype; 045import org.gbif.api.vocabulary.DatasetType; 046import org.gbif.api.vocabulary.Language; 047import org.gbif.api.vocabulary.License; 048import org.gbif.api.vocabulary.MaintenanceUpdateFrequency; 049 050import java.io.IOException; 051import java.net.URI; 052import java.util.ArrayList; 053import java.util.Date; 054import java.util.HashSet; 055import java.util.List; 056import java.util.Objects; 057import java.util.Set; 058import java.util.StringJoiner; 059import java.util.UUID; 060 061import jakarta.annotation.Nullable; 062import jakarta.validation.Valid; 063import jakarta.validation.constraints.Min; 064import jakarta.validation.constraints.NotNull; 065import jakarta.validation.constraints.Null; 066import jakarta.validation.constraints.Size; 067 068import io.swagger.v3.oas.annotations.media.Schema; 069 070/** 071 * A GBIF dataset which provides occurrence data, checklist data, sampling event data or metadata. 072 * This Dataset class is covering all the GBIF metadata profile v1.3, but only a few properties are kept in the 073 * database table: 074 * <ul> 075 * <li>key</li> 076 * <li>parentDatasetKey</li> 077 * <li>duplicateOfDatasetKey</li> 078 * <li>version</li> 079 * <li>installationKey</li> 080 * <li>publishingOrganizationKey</li> 081 * <li>publishingOrganizationName</li> 082 * <li>networkKeys</li> 083 * <li>license</li> 084 * <li>maintenanceUpdateFrequency</li> 085 * <li>external</li> 086 * <li>numConstituents</li> 087 * <li>type</li> 088 * <li>subtype</li> 089 * <li>title</li> 090 * <li>alias</li> 091 * <li>abbreviation</li> 092 * <li>description</li> 093 * <li>language</li> 094 * <li>homepage</li> 095 * <li>logoUrl</li> 096 * <li>citation</li> 097 * <li>rights</li> 098 * <li>lockedForAutoUpdate</li> 099 * <li>createdBy</li> 100 * <li>modifiedBy</li> 101 * <li>created</li> 102 * <li>modified</li> 103 * <li>deleted</li> 104 * </ul> 105 * 106 * @see <a href="http://rs.gbif.org/schema/eml-gbif-profile/dev/eml.xsd">GBIF EML Profile XML Schema</a> 107 */ 108@SuppressWarnings({"unused", "LombokSetterMayBeUsed", "LombokGetterMayBeUsed"}) 109public class Dataset 110 implements NetworkEntity, 111 Contactable, 112 Endpointable, 113 MachineTaggable, 114 Taggable, 115 Identifiable, 116 Commentable, 117 LenientEquals<Dataset> { 118 119 @Schema( 120 description = "Unique GBIF key for the dataset. This is used in the" + 121 "GBIF API, but outside GBIF it is best to refer to a dataset by its DOI.", 122 accessMode = Schema.AccessMode.READ_ONLY 123 ) 124 private UUID key; 125 126 @Schema( 127 description = "If set, this dataset is a sub-dataset of the parent." 128 ) 129 private UUID parentDatasetKey; 130 131 @Schema( 132 description = "A dataset of which this dataset is a duplicate. Typically, " + 133 "this means this dataset is an old version of the duplicated dataset, " + 134 "which has replaced this dataset. Therefore **this link is usually found " + 135 "on deleted datasets**." 136 ) 137 private UUID duplicateOfDatasetKey; 138 139 @Schema( 140 description = "The installation providing access to the source dataset.\n\n" + 141 "*(NB Not required for updates.)*" 142 ) 143 private UUID installationKey; 144 145 @Schema( 146 description = "The publishing organization publishing this dataset.\n\n" + 147 "*(NB Not required for updates.)*" 148 ) 149 private UUID publishingOrganizationKey; 150 151 @Schema( 152 description = "The publishing organization name.\n\n" + 153 "*(NB Not required for updates.)*" 154 ) 155 private String publishingOrganizationName; 156 157 @Schema( 158 description = "A list of GBIF Networks to which this dataset belongs." 159 ) 160 private List<UUID> networkKeys; 161 162 @Schema( 163 description = "The primary Digital Object Identifier (DOI) for this dataset.", 164 implementation = String.class, 165 pattern = "(10(?:\\.[0-9]+)+)" + "/(.+)" 166 ) 167 private DOI doi; 168 169 @Schema( 170 description = "The version of the published dataset." 171 ) 172 private String version; 173 174 @Schema( 175 description = "Not currently used." 176 ) 177 private boolean external; 178 179 @Schema( 180 description = "If set, the number of sub-datasets of this parent dataset." 181 ) 182 private int numConstituents; 183 184 @Schema( 185 description = "The primary type of the dataset.\n\n" + 186 "*(NB Not required for updates.)*" 187 ) 188 private DatasetType type; 189 190 @Schema( 191 description = "The sub-type of the dataset." 192 ) 193 private DatasetSubtype subtype; 194 195 @Schema( 196 description = "Concise name of the dataset." 197 ) 198 private String shortName; 199 200 @Schema( 201 description = "The title of the dataset.\n\n" + 202 "*(NB Not required for updates.)*" 203 ) 204 private String title; 205 206 @Schema( 207 description = "An alias for this dataset. Rarely used." 208 ) 209 private String alias; 210 211 @Schema( 212 description = "An abbreviation for this dataset. Rarely used." 213 ) 214 private String abbreviation; 215 216 @Schema( 217 description = "A description of the dataset." 218 ) 219 private String description; 220 221 @Schema( 222 description = "The language of the dataset metadata.\n\n" + 223 "*(NB Not required for updates.)*" 224 ) 225 private Language language = Language.ENGLISH; // sensible default as it is not null 226 227 @Schema( 228 description = "A homepage with further details on the dataset." 229 ) 230 private URI homepage; 231 232 @Schema( 233 description = "A logo for the dataset, accessible over HTTP." 234 ) 235 private URI logoUrl; 236 237 @Schema( 238 description = "The citation recommended by GBIF for use when citing this dataset." 239 ) 240 private Citation citation = new Citation(); 241 242 @Schema( 243 description = "Contacts use to generate a citation." 244 ) 245 private List<CitationContact> contactsCitation = new ArrayList<>(); 246 247 @Schema( 248 description = "Intellectual property rights applied to this dataset.\n\n" + 249 "*Rarely used, see `license` instead.*" 250 ) 251 private String rights; 252 253 @Schema( 254 description = "If true, any new or updated metadata is ignored.\n\n" + 255 "This is generally used when the publisher has technical problems or " + 256 "limitations with their publication system.", 257 accessMode = Schema.AccessMode.READ_ONLY 258 ) 259 private boolean lockedForAutoUpdate; 260 261 @Schema( 262 description = "The GBIF username of the creator of the dataset.", 263 accessMode = Schema.AccessMode.READ_ONLY 264 ) 265 private String createdBy; 266 267 @Schema( 268 description = "The GBIF username of the last user to modify the dataset.", 269 accessMode = Schema.AccessMode.READ_ONLY 270 ) 271 private String modifiedBy; 272 273 @Schema( 274 description = "Timestamp of when the dataset was created.", 275 accessMode = Schema.AccessMode.READ_ONLY 276 ) 277 private Date created; 278 279 @Schema( 280 description = "Timestamp of when the dataset was modified.", 281 accessMode = Schema.AccessMode.READ_ONLY 282 ) 283 private Date modified; 284 285 @Schema( 286 description = "If present, the dataset was deleted at this time. " + 287 "It is possible for it to be restored in the future.", 288 accessMode = Schema.AccessMode.READ_ONLY 289 ) 290 private Date deleted; 291 292 @Schema( 293 description = "A list of contacts associated with this dataset.", 294 accessMode = Schema.AccessMode.READ_ONLY 295 ) 296 private List<Contact> contacts = new ArrayList<>(); 297 298 @Schema( 299 description = "A list of endpoints associated with this dataset.", 300 accessMode = Schema.AccessMode.READ_ONLY 301 ) 302 private List<Endpoint> endpoints = new ArrayList<>(); 303 304 @Schema( 305 description = "A list of machine tags associated with this dataset.", 306 accessMode = Schema.AccessMode.READ_ONLY 307 ) 308 private List<MachineTag> machineTags = new ArrayList<>(); 309 310 @Schema( 311 description = "A list of tags associated with this dataset.", 312 accessMode = Schema.AccessMode.READ_ONLY 313 ) 314 private List<Tag> tags = new ArrayList<>(); 315 316 @Schema( 317 description = "A list of identifiers associated with this dataset.", 318 accessMode = Schema.AccessMode.READ_ONLY 319 ) 320 private List<Identifier> identifiers = new ArrayList<>(); 321 322 @Schema( 323 description = "A list of comments associated with this dataset.", 324 accessMode = Schema.AccessMode.READ_ONLY 325 ) 326 private List<Comment> comments = new ArrayList<>(); 327 328 // EML specific properties which are not persisted on the dataset table! 329 @Schema( 330 description = "Citations retrieved from this dataset's metadata documents.", 331 accessMode = Schema.AccessMode.READ_ONLY 332 ) 333 private List<Citation> bibliographicCitations = new ArrayList<>(); 334 335 @Schema( 336 description = "Curatorial unit information retrieved from this dataset's metadata documents.", 337 accessMode = Schema.AccessMode.READ_ONLY 338 ) 339 private List<CuratorialUnitComposite> curatorialUnits = new ArrayList<>(); 340 341 @Schema( 342 description = "Taxonomic coverage information retrieved from this dataset's metadata documents.", 343 accessMode = Schema.AccessMode.READ_ONLY 344 ) 345 private List<TaxonomicCoverages> taxonomicCoverages = new ArrayList<>(); 346 347 @Schema( 348 description = "Geographic coverage description retrieved from this dataset's metadata documents.", 349 accessMode = Schema.AccessMode.READ_ONLY 350 ) 351 private String geographicCoverageDescription; 352 353 @Schema( 354 description = "Geospatial coverage information retrieved from this dataset's metadata documents.", 355 accessMode = Schema.AccessMode.READ_ONLY 356 ) 357 private List<GeospatialCoverage> geographicCoverages = new ArrayList<>(); 358 359 @Schema( 360 description = "Temporal coverage information retrieved from this dataset's metadata documents.", 361 accessMode = Schema.AccessMode.READ_ONLY 362 ) 363 private List<TemporalCoverage> temporalCoverages = new ArrayList<>(); 364 365 @Schema( 366 description = "Keyword collections retrieved from this dataset's metadata documents.", 367 accessMode = Schema.AccessMode.READ_ONLY 368 ) 369 private List<KeywordCollection> keywordCollections = new ArrayList<>(); 370 371 @Schema( 372 description = "Project information retrieved from this dataset's metadata documents.", 373 accessMode = Schema.AccessMode.READ_ONLY 374 ) 375 private Project project; 376 377 @Schema( 378 description = "Sampling description retrieved from this dataset's metadata documents.", 379 accessMode = Schema.AccessMode.READ_ONLY 380 ) 381 private SamplingDescription samplingDescription; 382 383 @Schema( 384 description = "Country coverage information retrieved from this dataset's metadata documents.", 385 accessMode = Schema.AccessMode.READ_ONLY 386 ) 387 private Set<Country> countryCoverage = new HashSet<>(); 388 389 @Schema( 390 description = "Collection information retrieved from this dataset's metadata documents.", 391 accessMode = Schema.AccessMode.READ_ONLY 392 ) 393 private List<Collection> collections = new ArrayList<>(); 394 395 @Schema( 396 description = "Data description information retrieved from this dataset's metadata documents.", 397 accessMode = Schema.AccessMode.READ_ONLY 398 ) 399 private List<DataDescription> dataDescriptions = new ArrayList<>(); 400 401 @Schema( 402 description = "Data language information retrieved from this dataset's metadata documents.", 403 accessMode = Schema.AccessMode.READ_ONLY 404 ) 405 private Language dataLanguage; 406 407 @Schema( 408 description = "Purpose information retrieved from this dataset's metadata documents.", 409 accessMode = Schema.AccessMode.READ_ONLY 410 ) 411 private String purpose; 412 413 @Schema( 414 description = "An overview of the background and context for the dataset.", 415 accessMode = Schema.AccessMode.READ_ONLY 416 ) 417 private String introduction; 418 419 @Schema( 420 description = "A high level overview of interpretation, structure, and content of the dataset.", 421 accessMode = Schema.AccessMode.READ_ONLY 422 ) 423 private String gettingStarted; 424 425 @Schema( 426 description = "Text that acknowledges funders and other key contributors.", 427 accessMode = Schema.AccessMode.READ_ONLY 428 ) 429 private String acknowledgements; 430 431 @Schema( 432 description = "Additional information retrieved from this dataset's metadata documents.", 433 accessMode = Schema.AccessMode.READ_ONLY 434 ) 435 private String additionalInfo; 436 437 @Schema( 438 description = "The publication date retrieved from this dataset's metadata documents.", 439 accessMode = Schema.AccessMode.READ_ONLY 440 ) 441 private Date pubDate; 442 443 @Schema( 444 description = "The maintenance update frequency retrieved from this dataset's metadata documents.", 445 accessMode = Schema.AccessMode.READ_ONLY 446 ) 447 private MaintenanceUpdateFrequency maintenanceUpdateFrequency; 448 449 @Schema( 450 description = "The maintenance description retrieved from this dataset's metadata documents.", 451 accessMode = Schema.AccessMode.READ_ONLY 452 ) 453 private String maintenanceDescription; 454 455 @Schema( 456 description = "The data and metadata license retrieved from this dataset's metadata documents.", 457 accessMode = Schema.AccessMode.READ_ONLY 458 ) 459 private License license; 460 461 @Schema( 462 description = "Basic metadata of the Darwin Core Archive (DwC-A) associated with this dataset.", 463 accessMode = Schema.AccessMode.READ_ONLY 464 ) 465 private DwcA dwca; 466 467 @Schema( 468 description = "Category of this dataset.", 469 accessMode = Schema.AccessMode.READ_ONLY 470 ) 471 private Set<String> category; 472 473 @Schema( 474 description = 475 "Approximate record counts for this dataset, sourced from the dataset search index. " 476 + "Values are snapshots taken at index time and may lag live occurrence or checklist data. " 477 + "Useful to avoid extra calls for occurrence and name-usage totals; not authoritative.", 478 accessMode = Schema.AccessMode.READ_ONLY 479 ) 480 @Nullable 481 @Getter 482 @Setter 483 private ApproximateCounts approximateCounts; 484 485 @Override 486 public UUID getKey() { 487 return key; 488 } 489 490 /** 491 * Persisted in the database table. 492 */ 493 @Override 494 public void setKey(UUID key) { 495 this.key = key; 496 } 497 498 @Size(max = 10) 499 @Nullable 500 public String getVersion() { 501 return version; 502 } 503 504 public void setVersion(String version) { 505 this.version = version; 506 } 507 508 @Override 509 public String getTitle() { 510 return title; 511 } 512 513 /** 514 * Persisted in the database table. 515 */ 516 @Override 517 public void setTitle(String title) { 518 this.title = title; 519 } 520 521 @Override 522 @Nullable 523 public String getDescription() { 524 return description; 525 } 526 527 /** 528 * Persisted in the database table. 529 */ 530 @Override 531 public void setDescription(String description) { 532 this.description = description; 533 } 534 535 @Override 536 public Date getCreated() { 537 return created; 538 } 539 540 /** 541 * Autoassigned in the database table, ignored when persisted. 542 */ 543 @Override 544 public void setCreated(Date created) { 545 this.created = created; 546 } 547 548 @Override 549 public Date getModified() { 550 return modified; 551 } 552 553 /** 554 * Persisted in the database table. 555 */ 556 @Override 557 public void setModified(Date modified) { 558 this.modified = modified; 559 } 560 561 @Override 562 @Nullable 563 public Date getDeleted() { 564 return deleted; 565 } 566 567 /** 568 * Persisted in the database table. 569 */ 570 @Override 571 public void setDeleted(Date deleted) { 572 this.deleted = deleted; 573 } 574 575 @Nullable 576 public UUID getParentDatasetKey() { 577 return parentDatasetKey; 578 } 579 580 /** 581 * Persisted in the database table. 582 */ 583 public void setParentDatasetKey(UUID parentDatasetKey) { 584 this.parentDatasetKey = parentDatasetKey; 585 } 586 587 /** 588 * If a dataset is registered with GBIF through more than one place we'll mark all but one as a duplicate by pointing 589 * it to the canonical dataset. That is done using this field. If it is {@code null} then this is not a known 590 * duplicate. 591 */ 592 @Nullable 593 public UUID getDuplicateOfDatasetKey() { 594 return duplicateOfDatasetKey; 595 } 596 597 /** 598 * Persisted in the database table. 599 */ 600 public void setDuplicateOfDatasetKey(UUID duplicateOfDatasetKey) { 601 this.duplicateOfDatasetKey = duplicateOfDatasetKey; 602 } 603 604 @NotNull 605 public UUID getInstallationKey() { 606 return installationKey; 607 } 608 609 /** 610 * Persisted in the database table. 611 */ 612 public void setInstallationKey(UUID installationKey) { 613 this.installationKey = installationKey; 614 } 615 616 @NotNull 617 public UUID getPublishingOrganizationKey() { 618 return publishingOrganizationKey; 619 } 620 621 /** 622 * Persisted in the database table. 623 */ 624 public void setPublishingOrganizationKey(UUID publishingOrganizationKey) { 625 this.publishingOrganizationKey = publishingOrganizationKey; 626 } 627 628 @Nullable 629 public String getPublishingOrganizationName() { 630 return publishingOrganizationName; 631 } 632 633 public void setPublishingOrganizationName(String publishingOrganizationName) { 634 this.publishingOrganizationName = publishingOrganizationName; 635 } 636 637 /** 638 * Networks in which this dataset is a constituent. 639 */ 640 public List<UUID> getNetworkKeys() { 641 return networkKeys; 642 } 643 644 public void setNetworkKeys(List<UUID> networkKeys) { 645 this.networkKeys = networkKeys; 646 } 647 648 /** 649 * Persisted in the database table. 650 * 651 * @return the frequency with which changes are made to the dataset 652 */ 653 @Nullable 654 public MaintenanceUpdateFrequency getMaintenanceUpdateFrequency() { 655 return maintenanceUpdateFrequency; 656 } 657 /** 658 * Persisted in the database table. 659 */ 660 public void setMaintenanceUpdateFrequency(MaintenanceUpdateFrequency maintenanceUpdateFrequency) { 661 this.maintenanceUpdateFrequency = maintenanceUpdateFrequency; 662 } 663 664 /** 665 * A description of the maintenance frequency of this resource. 666 * 667 * @return the description of the maintenance frequency of this resource 668 */ 669 public String getMaintenanceDescription() { 670 return maintenanceDescription; 671 } 672 673 public void setMaintenanceDescription(String maintenanceDescription) { 674 this.maintenanceDescription = maintenanceDescription; 675 } 676 677 /** 678 * Persisted in the database table. 679 * </br> 680 * Note for backwards compatibility, we cannot apply @NotNull to license. Otherwise existing users of our API 681 * would have to ensure Dataset objects always populate license. 682 * </br> 683 * In the Registry DB, Dataset.license defaults to CC-BY 4.0. Therefore license must be excluded from lenientEquals 684 * method. 685 * 686 * @return the License applied to the dataset 687 * 688 * @see <a href="http://dev.gbif.org/issues/browse/POR-3133">POR-3133</a> 689 */ 690 public License getLicense() { 691 return license; 692 } 693 694 /** 695 * Persisted in the database table. Can be interpreted from EML.intellectualRights using machine readable format: 696 * <pre> 697 * {@code 698 * <intellectualRights> 699 * <para>This work is licensed under a <ulink url="http://creativecommons.org/licenses/by/4.0/legalcode"><citetitle>Creative Commons Attribution (CC-BY) 4.0 License</citetitle></ulink>.</para> 700 * </intellectualRights> 701 * } 702 * </pre> 703 */ 704 public void setLicense(License license) { 705 this.license = license; 706 } 707 708 /** 709 * @return the primary DOI for this dataset regardless if issued by GBIF or publisher 710 */ 711 public DOI getDoi() { 712 return doi; 713 } 714 715 public void setDoi(DOI doi) { 716 this.doi = doi; 717 } 718 719 public boolean isExternal() { 720 return external; 721 } 722 723 /** 724 * Persisted in the database table. 725 */ 726 public void setExternal(boolean external) { 727 this.external = external; 728 } 729 730 @Min(0) 731 public int getNumConstituents() { 732 return numConstituents; 733 } 734 735 /** 736 * Not persisted in the database table, but calculated on the fly. 737 */ 738 public void setNumConstituents(int numConstituents) { 739 this.numConstituents = numConstituents; 740 } 741 742 @NotNull 743 public DatasetType getType() { 744 return type; 745 } 746 747 /** 748 * Persisted in the database table. 749 */ 750 public void setType(DatasetType type) { 751 this.type = type; 752 } 753 754 @Nullable 755 public DatasetSubtype getSubtype() { 756 return subtype; 757 } 758 759 /** 760 * Persisted in the database table. 761 */ 762 public void setSubtype(DatasetSubtype subtype) { 763 this.subtype = subtype; 764 } 765 766 @Nullable 767 public String getShortName() { 768 return shortName; 769 } 770 771 public void setShortName(String shortName) { 772 this.shortName = shortName; 773 } 774 775 /** 776 * TODO: Document what this is 777 */ 778 @Nullable 779 @Size(min = 2, max = 50) 780 public String getAlias() { 781 return alias; 782 } 783 784 /** 785 * Persisted in the database table. 786 */ 787 public void setAlias(String alias) { 788 this.alias = alias; 789 } 790 791 /** 792 * TODO: Document what this is 793 * TODO: are both alias & abbreviation needed? 794 */ 795 @Nullable 796 @Size(min = 1, max = 50) 797 public String getAbbreviation() { 798 return abbreviation; 799 } 800 801 /** 802 * Persisted in the database table. 803 */ 804 public void setAbbreviation(String abbreviation) { 805 this.abbreviation = abbreviation; 806 } 807 808 @NotNull 809 public Language getLanguage() { 810 return language; 811 } 812 813 /** 814 * Persisted in the database table. 815 */ 816 public void setLanguage(Language language) { 817 this.language = language; 818 } 819 820 @HttpURI 821 @Nullable 822 public URI getHomepage() { 823 return homepage; 824 } 825 826 /** 827 * Persisted in the database table. 828 */ 829 public void setHomepage(URI homepage) { 830 this.homepage = homepage; 831 } 832 833 @HttpURI 834 @Nullable 835 public URI getLogoUrl() { 836 return logoUrl; 837 } 838 839 /** 840 * Persisted in the database table. 841 */ 842 public void setLogoUrl(URI logoUrl) { 843 this.logoUrl = logoUrl; 844 } 845 846 /** 847 * The exact form of how to cite this dataset. 848 */ 849 @Nullable 850 @Valid 851 public Citation getCitation() { 852 return citation; 853 } 854 855 /** 856 * Persisted in the database table. 857 */ 858 public void setCitation(Citation citation) { 859 this.citation = citation; 860 } 861 862 /** 863 * A generated list of contacts used in the citation text when it is generated by the GBIF API. 864 */ 865 @Nullable 866 public List<CitationContact> getContactsCitation() { 867 return contactsCitation; 868 } 869 870 public void setContactsCitation(List<CitationContact> contactsCitation) { 871 this.contactsCitation = contactsCitation; 872 } 873 874 /** 875 * Any kind of (copy)rights/IPR statements that apply to the datasets data. 876 */ 877 @Nullable 878 @Size(min = 1) 879 public String getRights() { 880 return rights; 881 } 882 883 /** 884 * Persisted in the database table. 885 */ 886 public void setRights(String rights) { 887 this.rights = rights; 888 } 889 890 public boolean isLockedForAutoUpdate() { 891 return lockedForAutoUpdate; 892 } 893 894 /** 895 * Persisted in the database table. 896 */ 897 public void setLockedForAutoUpdate(boolean lockedForAutoUpdate) { 898 this.lockedForAutoUpdate = lockedForAutoUpdate; 899 } 900 901 @Override 902 public String getCreatedBy() { 903 return createdBy; 904 } 905 906 /** 907 * Persisted in the database table. 908 */ 909 @Override 910 public void setCreatedBy(String createdBy) { 911 this.createdBy = createdBy; 912 } 913 914 @Override 915 public String getModifiedBy() { 916 return modifiedBy; 917 } 918 919 /** 920 * Persisted in the database table. 921 */ 922 @Override 923 public void setModifiedBy(String modifiedBy) { 924 this.modifiedBy = modifiedBy; 925 } 926 927 @Override 928 public List<Contact> getContacts() { 929 return contacts; 930 } 931 932 @Override 933 public void setContacts(List<Contact> contacts) { 934 this.contacts = contacts; 935 } 936 937 @Override 938 public List<Endpoint> getEndpoints() { 939 return endpoints; 940 } 941 942 @Override 943 public void setEndpoints(List<Endpoint> endpoints) { 944 this.endpoints = endpoints; 945 } 946 947 @Override 948 public void addEndpoint(Endpoint endpoint) { 949 endpoints.add(endpoint); 950 } 951 952 @Override 953 public List<MachineTag> getMachineTags() { 954 return machineTags; 955 } 956 957 @Override 958 public void setMachineTags(List<MachineTag> machineTags) { 959 this.machineTags = machineTags; 960 } 961 962 @Override 963 public void addMachineTag(MachineTag machineTag) { 964 machineTags.add(machineTag); 965 } 966 967 @Override 968 public List<Tag> getTags() { 969 return tags; 970 } 971 972 @Override 973 public void setTags(List<Tag> tags) { 974 this.tags = tags; 975 } 976 977 @Override 978 public List<Identifier> getIdentifiers() { 979 return identifiers; 980 } 981 982 @Override 983 public void setIdentifiers(List<Identifier> identifiers) { 984 this.identifiers = identifiers; 985 } 986 987 @Override 988 public List<Comment> getComments() { 989 return comments; 990 } 991 992 @Override 993 public void setComments(List<Comment> comments) { 994 this.comments = comments; 995 } 996 997 public List<Citation> getBibliographicCitations() { 998 return bibliographicCitations; 999 } 1000 1001 public void setBibliographicCitations(List<Citation> bibliographicCitations) { 1002 this.bibliographicCitations = bibliographicCitations; 1003 } 1004 1005 public List<CuratorialUnitComposite> getCuratorialUnits() { 1006 return curatorialUnits; 1007 } 1008 1009 public void setCuratorialUnits(List<CuratorialUnitComposite> curatorialUnits) { 1010 this.curatorialUnits = curatorialUnits; 1011 } 1012 1013 public List<TaxonomicCoverages> getTaxonomicCoverages() { 1014 return taxonomicCoverages; 1015 } 1016 1017 public void setTaxonomicCoverages(List<TaxonomicCoverages> taxonomicCoverages) { 1018 this.taxonomicCoverages = taxonomicCoverages; 1019 } 1020 1021 public String getGeographicCoverageDescription() { 1022 return geographicCoverageDescription; 1023 } 1024 1025 public void setGeographicCoverageDescription(String geographicCoverageDescription) { 1026 this.geographicCoverageDescription = geographicCoverageDescription; 1027 } 1028 1029 public List<GeospatialCoverage> getGeographicCoverages() { 1030 return geographicCoverages; 1031 } 1032 1033 public void setGeographicCoverages(List<GeospatialCoverage> geographicCoverages) { 1034 this.geographicCoverages = geographicCoverages; 1035 } 1036 1037 public List<TemporalCoverage> getTemporalCoverages() { 1038 return temporalCoverages; 1039 } 1040 1041 public void setTemporalCoverages(List<TemporalCoverage> temporalCoverages) { 1042 this.temporalCoverages = temporalCoverages; 1043 } 1044 1045 public List<KeywordCollection> getKeywordCollections() { 1046 return keywordCollections; 1047 } 1048 1049 public void setKeywordCollections(List<KeywordCollection> keywordCollections) { 1050 this.keywordCollections = keywordCollections; 1051 } 1052 1053 public Project getProject() { 1054 return project; 1055 } 1056 1057 public void setProject(Project project) { 1058 this.project = project; 1059 } 1060 1061 public SamplingDescription getSamplingDescription() { 1062 return samplingDescription; 1063 } 1064 1065 public void setSamplingDescription(SamplingDescription samplingDescription) { 1066 this.samplingDescription = samplingDescription; 1067 } 1068 1069 public Set<Country> getCountryCoverage() { 1070 return countryCoverage; 1071 } 1072 1073 public void setCountryCoverage(Set<Country> countryCoverage) { 1074 this.countryCoverage = countryCoverage; 1075 } 1076 1077 public List<Collection> getCollections() { 1078 return collections; 1079 } 1080 1081 public void setCollections(List<Collection> collections) { 1082 this.collections = collections; 1083 } 1084 1085 public List<DataDescription> getDataDescriptions() { 1086 return dataDescriptions; 1087 } 1088 1089 public void setDataDescriptions(List<DataDescription> dataDescriptions) { 1090 this.dataDescriptions = dataDescriptions; 1091 } 1092 1093 public Language getDataLanguage() { 1094 return dataLanguage; 1095 } 1096 1097 public void setDataLanguage(Language dataLanguage) { 1098 this.dataLanguage = dataLanguage; 1099 } 1100 1101 public String getPurpose() { 1102 return purpose; 1103 } 1104 1105 public void setPurpose(String purpose) { 1106 this.purpose = purpose; 1107 } 1108 1109 @Nullable 1110 public String getIntroduction() { 1111 return introduction; 1112 } 1113 1114 public void setIntroduction(String introduction) { 1115 this.introduction = introduction; 1116 } 1117 1118 @Nullable 1119 public String getGettingStarted() { 1120 return gettingStarted; 1121 } 1122 1123 public void setGettingStarted(String gettingStarted) { 1124 this.gettingStarted = gettingStarted; 1125 } 1126 1127 @Nullable 1128 public String getAcknowledgements() { 1129 return acknowledgements; 1130 } 1131 1132 public void setAcknowledgements(String acknowledgements) { 1133 this.acknowledgements = acknowledgements; 1134 } 1135 1136 public String getAdditionalInfo() { 1137 return additionalInfo; 1138 } 1139 1140 public void setAdditionalInfo(String additionalInfo) { 1141 this.additionalInfo = additionalInfo; 1142 } 1143 1144 public Date getPubDate() { 1145 return pubDate; 1146 } 1147 1148 public void setPubDate(Date pubDate) { 1149 this.pubDate = pubDate; 1150 } 1151 1152 @Nullable 1153 @Valid 1154 public DwcA getDwca() { 1155 return dwca; 1156 } 1157 1158 public void setDwca(DwcA dwca) { 1159 this.dwca = dwca; 1160 } 1161 1162 public Set<String> getCategory() { 1163 return category; 1164 } 1165 1166 public void setCategory(Set<String> category) { 1167 this.category = category; 1168 } 1169 1170 @Override 1171 public boolean equals(Object o) { 1172 if (this == o) { 1173 return true; 1174 } 1175 if (o == null || getClass() != o.getClass()) { 1176 return false; 1177 } 1178 Dataset dataset = (Dataset) o; 1179 return external == dataset.external 1180 && numConstituents == dataset.numConstituents 1181 && lockedForAutoUpdate == dataset.lockedForAutoUpdate 1182 && Objects.equals(key, dataset.key) 1183 && Objects.equals(parentDatasetKey, dataset.parentDatasetKey) 1184 && Objects.equals(duplicateOfDatasetKey, dataset.duplicateOfDatasetKey) 1185 && Objects.equals(installationKey, dataset.installationKey) 1186 && Objects.equals(publishingOrganizationKey, dataset.publishingOrganizationKey) 1187 && Objects.equals(publishingOrganizationName, dataset.publishingOrganizationName) 1188 && Objects.equals(networkKeys, dataset.networkKeys) 1189 && Objects.equals(doi, dataset.doi) 1190 && Objects.equals(version, dataset.version) 1191 && type == dataset.type 1192 && subtype == dataset.subtype 1193 && Objects.equals(shortName, dataset.shortName) 1194 && Objects.equals(title, dataset.title) 1195 && Objects.equals(alias, dataset.alias) 1196 && Objects.equals(abbreviation, dataset.abbreviation) 1197 && Objects.equals(description, dataset.description) 1198 && language == dataset.language 1199 && Objects.equals(homepage, dataset.homepage) 1200 && Objects.equals(logoUrl, dataset.logoUrl) 1201 && Objects.equals(citation, dataset.citation) 1202 && Objects.equals(contactsCitation, dataset.contactsCitation) 1203 && Objects.equals(rights, dataset.rights) 1204 && Objects.equals(createdBy, dataset.createdBy) 1205 && Objects.equals(modifiedBy, dataset.modifiedBy) 1206 && Objects.equals(created, dataset.created) 1207 && Objects.equals(modified, dataset.modified) 1208 && Objects.equals(deleted, dataset.deleted) 1209 && Objects.equals(contacts, dataset.contacts) 1210 && Objects.equals(endpoints, dataset.endpoints) 1211 && Objects.equals(machineTags, dataset.machineTags) 1212 && Objects.equals(tags, dataset.tags) 1213 && Objects.equals(identifiers, dataset.identifiers) 1214 && Objects.equals(comments, dataset.comments) 1215 && Objects.equals(bibliographicCitations, dataset.bibliographicCitations) 1216 && Objects.equals(curatorialUnits, dataset.curatorialUnits) 1217 && Objects.equals(taxonomicCoverages, dataset.taxonomicCoverages) 1218 && Objects.equals(geographicCoverageDescription, dataset.geographicCoverageDescription) 1219 && Objects.equals(geographicCoverages, dataset.geographicCoverages) 1220 && Objects.equals(temporalCoverages, dataset.temporalCoverages) 1221 && Objects.equals(keywordCollections, dataset.keywordCollections) 1222 && Objects.equals(project, dataset.project) 1223 && Objects.equals(samplingDescription, dataset.samplingDescription) 1224 && Objects.equals(countryCoverage, dataset.countryCoverage) 1225 && Objects.equals(collections, dataset.collections) 1226 && Objects.equals(dataDescriptions, dataset.dataDescriptions) 1227 && dataLanguage == dataset.dataLanguage 1228 && Objects.equals(purpose, dataset.purpose) 1229 && Objects.equals(introduction, dataset.introduction) 1230 && Objects.equals(gettingStarted, dataset.gettingStarted) 1231 && Objects.equals(acknowledgements, dataset.acknowledgements) 1232 && Objects.equals(additionalInfo, dataset.additionalInfo) 1233 && Objects.equals(pubDate, dataset.pubDate) 1234 && maintenanceUpdateFrequency == dataset.maintenanceUpdateFrequency 1235 && Objects.equals(maintenanceDescription, dataset.maintenanceDescription) 1236 && license == dataset.license 1237 && Objects.equals(dwca, dataset.dwca) 1238 && Objects.equals(category, dataset.category); 1239 } 1240 1241 @Override 1242 public int hashCode() { 1243 return Objects.hash( 1244 key, 1245 parentDatasetKey, 1246 duplicateOfDatasetKey, 1247 installationKey, 1248 publishingOrganizationKey, 1249 publishingOrganizationName, 1250 networkKeys, 1251 doi, 1252 version, 1253 external, 1254 numConstituents, 1255 type, 1256 subtype, 1257 shortName, 1258 title, 1259 alias, 1260 abbreviation, 1261 description, 1262 language, 1263 homepage, 1264 logoUrl, 1265 citation, 1266 contactsCitation, 1267 rights, 1268 lockedForAutoUpdate, 1269 createdBy, 1270 modifiedBy, 1271 created, 1272 modified, 1273 deleted, 1274 contacts, 1275 endpoints, 1276 machineTags, 1277 tags, 1278 identifiers, 1279 comments, 1280 bibliographicCitations, 1281 curatorialUnits, 1282 taxonomicCoverages, 1283 geographicCoverageDescription, 1284 geographicCoverages, 1285 temporalCoverages, 1286 keywordCollections, 1287 project, 1288 samplingDescription, 1289 countryCoverage, 1290 collections, 1291 dataDescriptions, 1292 dataLanguage, 1293 purpose, 1294 introduction, 1295 gettingStarted, 1296 acknowledgements, 1297 additionalInfo, 1298 pubDate, 1299 maintenanceUpdateFrequency, 1300 maintenanceDescription, 1301 license, 1302 dwca, 1303 category); 1304 } 1305 1306 @Override 1307 public String toString() { 1308 return new StringJoiner(", ", Dataset.class.getSimpleName() + "[", "]") 1309 .add("key=" + key) 1310 .add("parentDatasetKey=" + parentDatasetKey) 1311 .add("duplicateOfDatasetKey=" + duplicateOfDatasetKey) 1312 .add("installationKey=" + installationKey) 1313 .add("publishingOrganizationKey=" + publishingOrganizationKey) 1314 .add("publishingOrganizationName=" + publishingOrganizationName) 1315 .add("networkKeys=" + networkKeys) 1316 .add("doi=" + doi) 1317 .add("version='" + version + "'") 1318 .add("external=" + external) 1319 .add("numConstituents=" + numConstituents) 1320 .add("type=" + type) 1321 .add("subtype=" + subtype) 1322 .add("shortName='" + shortName + "'") 1323 .add("title='" + title + "'") 1324 .add("alias='" + alias + "'") 1325 .add("abbreviation='" + abbreviation + "'") 1326 .add("description='" + description + "'") 1327 .add("language=" + language) 1328 .add("homepage=" + homepage) 1329 .add("logoUrl=" + logoUrl) 1330 .add("citation=" + citation) 1331 .add("contactsCitation=" + contactsCitation) 1332 .add("rights='" + rights + "'") 1333 .add("lockedForAutoUpdate=" + lockedForAutoUpdate) 1334 .add("createdBy='" + createdBy + "'") 1335 .add("modifiedBy='" + modifiedBy + "'") 1336 .add("created=" + created) 1337 .add("modified=" + modified) 1338 .add("deleted=" + deleted) 1339 .add("contacts=" + contacts) 1340 .add("endpoints=" + endpoints) 1341 .add("machineTags=" + machineTags) 1342 .add("tags=" + tags) 1343 .add("identifiers=" + identifiers) 1344 .add("comments=" + comments) 1345 .add("bibliographicCitations=" + bibliographicCitations) 1346 .add("curatorialUnits=" + curatorialUnits) 1347 .add("taxonomicCoverages=" + taxonomicCoverages) 1348 .add("geographicCoverageDescription='" + geographicCoverageDescription + "'") 1349 .add("geographicCoverages=" + geographicCoverages) 1350 .add("temporalCoverages=" + temporalCoverages) 1351 .add("keywordCollections=" + keywordCollections) 1352 .add("project=" + project) 1353 .add("samplingDescription=" + samplingDescription) 1354 .add("countryCoverage=" + countryCoverage) 1355 .add("collections=" + collections) 1356 .add("dataDescriptions=" + dataDescriptions) 1357 .add("dataLanguage=" + dataLanguage) 1358 .add("purpose='" + purpose + "'") 1359 .add("introduction='" + introduction + "'") 1360 .add("gettingStarted='" + gettingStarted + "'") 1361 .add("acknowledgements='" + acknowledgements + "'") 1362 .add("additionalInfo='" + additionalInfo + "'") 1363 .add("pubDate=" + pubDate) 1364 .add("maintenanceUpdateFrequency=" + maintenanceUpdateFrequency) 1365 .add("maintenanceDescription='" + maintenanceDescription + "'") 1366 .add("license=" + license) 1367 .add("dwca=" + dwca) 1368 .add("category=" + category) 1369 .add("approximateCounts=" + approximateCounts) 1370 .toString(); 1371 } 1372 1373 /** 1374 * Only checks the persisted properties, excluding the server controlled fields (key, created, license etc). 1375 * Does not include the nested properties. 1376 */ 1377 @Override 1378 public boolean lenientEquals(Dataset other) { 1379 if (this == other) { 1380 return true; 1381 } 1382 if (other == null) return false; 1383 return Objects.equals(this.parentDatasetKey, other.parentDatasetKey) 1384 && Objects.equals(this.duplicateOfDatasetKey, other.duplicateOfDatasetKey) 1385 && Objects.equals(this.installationKey, other.installationKey) 1386 && Objects.equals(this.publishingOrganizationKey, other.publishingOrganizationKey) 1387 && Objects.equals(this.publishingOrganizationName, other.publishingOrganizationName) 1388 && Objects.equals(this.doi, other.doi) 1389 && Objects.equals(this.external, other.external) 1390 && Objects.equals(this.type, other.type) 1391 && Objects.equals(this.subtype, other.subtype) 1392 && Objects.equals(this.title, other.title) 1393 && Objects.equals(this.alias, other.alias) 1394 && Objects.equals(this.abbreviation, other.abbreviation) 1395 && Objects.equals(this.description, other.description) 1396 && Objects.equals(this.language, other.language) 1397 && Objects.equals(this.homepage, other.homepage) 1398 && Objects.equals(this.logoUrl, other.logoUrl) 1399 && Objects.equals(this.citation, other.citation) 1400 && Objects.equals(this.rights, other.rights) 1401 && Objects.equals(this.lockedForAutoUpdate, other.lockedForAutoUpdate) 1402 && Objects.equals(this.deleted, other.deleted) 1403 && Objects.equals(this.maintenanceUpdateFrequency, other.maintenanceUpdateFrequency) 1404 && Objects.equals(this.maintenanceDescription, other.maintenanceDescription) 1405 && Objects.equals(this.dwca, other.dwca) 1406 && Objects.equals(this.category, other.category); 1407 } 1408 1409 /** 1410 * Metadata of dataset that has been published as a Darwin Core Archive (DwC-A). 1411 */ 1412 @NoArgsConstructor 1413 @Data 1414 public static class DwcA { 1415 @Schema( 1416 description = "This attribute, within the <core>, indicates the specific " + 1417 "type of data being represented in the core data file.**." 1418 ) 1419 private String coreType; 1420 1421 @Schema( 1422 description = "This attribute, within the <extensions>, indicates the specific " + 1423 "type of data being represented in the associated extension data file.**." 1424 ) 1425 private List<String> extensions; 1426 1427 @Schema( 1428 description = "Timestamp of when the dataset DwcA metadata was modified.", 1429 accessMode = Schema.AccessMode.READ_ONLY 1430 ) 1431 @Null(groups = {PrePersist.class}) 1432 private Date modified; 1433 1434 1435 public String getCoreType() { 1436 return coreType; 1437 } 1438 1439 public void setCoreType(String coreType) { 1440 this.coreType = coreType; 1441 } 1442 1443 @Nullable 1444 public List<String> getExtensions() { 1445 return extensions; 1446 } 1447 1448 public void setExtensions(List<String> extensions) { 1449 this.extensions = extensions; 1450 } 1451 1452 @Nullable 1453 public Date getModified() { 1454 return modified; 1455 } 1456 1457 public void setModified(Date modified) { 1458 this.modified = modified; 1459 } 1460 } 1461 1462 /** 1463 * Metadata of dataset that has been published as a <a href="https://specs.frictionlessdata.io/data-package/">DataPackage</a>. 1464 */ 1465 @NoArgsConstructor 1466 @Data 1467 @Experimental 1468 public static class DataPackage { 1469 1470 /** 1471 * Custom deserializer to capture raw JSON string. 1472 */ 1473 public static class RawJsonDeserializer extends JsonDeserializer<String> { 1474 1475 public RawJsonDeserializer() { 1476 // Jackson requires this 1477 } 1478 1479 @Override 1480 public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { 1481 JsonNode node = p.readValueAsTree(); 1482 return node.toString(); // store the raw JSON as a string 1483 } 1484 } 1485 1486 @Schema( 1487 description = "The content of the <a href=\"https://specs.frictionlessdata.io/schemas/data-package.json\">datapackage.json</a> file." 1488 ) 1489 @JsonDeserialize(using = RawJsonDeserializer.class) 1490 @JsonRawValue 1491 private String metadata; 1492 1493 @Schema( 1494 description = "Timestamp of when the dataset datapackage metadata was modified.", 1495 accessMode = Schema.AccessMode.READ_ONLY 1496 ) 1497 @Null(groups = {PrePersist.class}) 1498 private Date modified; 1499 1500 1501 @Schema( 1502 description = "Unique GBIF key for the dataset.", 1503 accessMode = Schema.AccessMode.READ_ONLY 1504 ) 1505 private UUID datasetKey; 1506 1507 @Schema( 1508 description = "Unique GBIF key for the dataset endpoint.", 1509 accessMode = Schema.AccessMode.READ_ONLY 1510 ) 1511 private Integer endpointKey; 1512 1513 @NotNull 1514 public String getMetadata() { 1515 return metadata; 1516 } 1517 1518 public void setMetadata(String metadata) { 1519 this.metadata = metadata; 1520 } 1521 1522 @Nullable 1523 public Date getModified() { 1524 return modified; 1525 } 1526 1527 public void setModified(Date modified) { 1528 this.modified = modified; 1529 } 1530 1531 @NotNull 1532 public UUID getDatasetKey() { 1533 return datasetKey; 1534 } 1535 1536 public void setDatasetKey(UUID datasetKey) { 1537 this.datasetKey = datasetKey; 1538 } 1539 1540 @NotNull 1541 public Integer getEndpointKey() { 1542 return endpointKey; 1543 } 1544 1545 public void setEndpointKey(Integer endpointKey) { 1546 this.endpointKey = endpointKey; 1547 } 1548 } 1549}