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