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  @Nullable
485  public String getVersion() {
486    return version;
487  }
488
489  public void setVersion(String version) {
490    this.version = version;
491  }
492
493  @Override
494  public String getTitle() {
495    return title;
496  }
497
498  /**
499   * Persisted in the database table.
500   */
501  @Override
502  public void setTitle(String title) {
503    this.title = title;
504  }
505
506  @Override
507  @Nullable
508  public String getDescription() {
509    return description;
510  }
511
512  /**
513   * Persisted in the database table.
514   */
515  @Override
516  public void setDescription(String description) {
517    this.description = description;
518  }
519
520  @Override
521  public Date getCreated() {
522    return created;
523  }
524
525  /**
526   * Autoassigned in the database table, ignored when persisted.
527   */
528  @Override
529  public void setCreated(Date created) {
530    this.created = created;
531  }
532
533  @Override
534  public Date getModified() {
535    return modified;
536  }
537
538  /**
539   * Persisted in the database table.
540   */
541  @Override
542  public void setModified(Date modified) {
543    this.modified = modified;
544  }
545
546  @Override
547  @Nullable
548  public Date getDeleted() {
549    return deleted;
550  }
551
552  /**
553   * Persisted in the database table.
554   */
555  @Override
556  public void setDeleted(Date deleted) {
557    this.deleted = deleted;
558  }
559
560  @Nullable
561  public UUID getParentDatasetKey() {
562    return parentDatasetKey;
563  }
564
565  /**
566   * Persisted in the database table.
567   */
568  public void setParentDatasetKey(UUID parentDatasetKey) {
569    this.parentDatasetKey = parentDatasetKey;
570  }
571
572  /**
573   * If a dataset is registered with GBIF through more than one place we'll mark all but one as a duplicate by pointing
574   * it to the canonical dataset. That is done using this field. If it is {@code null} then this is not a known
575   * duplicate.
576   */
577  @Nullable
578  public UUID getDuplicateOfDatasetKey() {
579    return duplicateOfDatasetKey;
580  }
581
582  /**
583   * Persisted in the database table.
584   */
585  public void setDuplicateOfDatasetKey(UUID duplicateOfDatasetKey) {
586    this.duplicateOfDatasetKey = duplicateOfDatasetKey;
587  }
588
589  @NotNull
590  public UUID getInstallationKey() {
591    return installationKey;
592  }
593
594  /**
595   * Persisted in the database table.
596   */
597  public void setInstallationKey(UUID installationKey) {
598    this.installationKey = installationKey;
599  }
600
601  @NotNull
602  public UUID getPublishingOrganizationKey() {
603    return publishingOrganizationKey;
604  }
605
606  /**
607   * Persisted in the database table.
608   */
609  public void setPublishingOrganizationKey(UUID publishingOrganizationKey) {
610    this.publishingOrganizationKey = publishingOrganizationKey;
611  }
612
613  @Nullable
614  public String getPublishingOrganizationName() {
615    return publishingOrganizationName;
616  }
617
618  public void setPublishingOrganizationName(String publishingOrganizationName) {
619    this.publishingOrganizationName = publishingOrganizationName;
620  }
621
622  /**
623   * Networks in which this dataset is a constituent.
624   */
625  public List<UUID> getNetworkKeys() {
626    return networkKeys;
627  }
628
629  public void setNetworkKeys(List<UUID> networkKeys) {
630    this.networkKeys = networkKeys;
631  }
632
633  /**
634   * Persisted in the database table.
635   *
636   * @return the frequency with which changes are made to the dataset
637   */
638  @Nullable
639  public MaintenanceUpdateFrequency getMaintenanceUpdateFrequency() {
640    return maintenanceUpdateFrequency;
641  }
642  /**
643   * Persisted in the database table.
644   */
645  public void setMaintenanceUpdateFrequency(MaintenanceUpdateFrequency maintenanceUpdateFrequency) {
646    this.maintenanceUpdateFrequency = maintenanceUpdateFrequency;
647  }
648
649  /**
650   * A description of the maintenance frequency of this resource.
651   *
652   * @return the description of the maintenance frequency of this resource
653   */
654  public String getMaintenanceDescription() {
655    return maintenanceDescription;
656  }
657
658  public void setMaintenanceDescription(String maintenanceDescription) {
659    this.maintenanceDescription = maintenanceDescription;
660  }
661
662  /**
663   * Persisted in the database table.
664   * </br>
665   * Note for backwards compatibility, we cannot apply @NotNull to license. Otherwise existing users of our API
666   * would have to ensure Dataset objects always populate license.
667   * </br>
668   * In the Registry DB, Dataset.license defaults to CC-BY 4.0. Therefore license must be excluded from lenientEquals
669   * method.
670   *
671   * @return the License applied to the dataset
672   *
673   * @see <a href="http://dev.gbif.org/issues/browse/POR-3133">POR-3133</a>
674   */
675  public License getLicense() {
676    return license;
677  }
678
679  /**
680   * Persisted in the database table. Can be interpreted from EML.intellectualRights using machine readable format:
681   * <pre>
682   * {@code
683   * <intellectualRights>
684   *   <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>
685   * </intellectualRights>
686   * }
687   * </pre>
688   */
689  public void setLicense(License license) {
690    this.license = license;
691  }
692
693  /**
694   * @return the primary DOI for this dataset regardless if issued by GBIF or publisher
695   */
696  public DOI getDoi() {
697    return doi;
698  }
699
700  public void setDoi(DOI doi) {
701    this.doi = doi;
702  }
703
704  public boolean isExternal() {
705    return external;
706  }
707
708  /**
709   * Persisted in the database table.
710   */
711  public void setExternal(boolean external) {
712    this.external = external;
713  }
714
715  @Min(0)
716  public int getNumConstituents() {
717    return numConstituents;
718  }
719
720  /**
721   * Not persisted in the database table, but calculated on the fly.
722   */
723  public void setNumConstituents(int numConstituents) {
724    this.numConstituents = numConstituents;
725  }
726
727  @NotNull
728  public DatasetType getType() {
729    return type;
730  }
731
732  /**
733   * Persisted in the database table.
734   */
735  public void setType(DatasetType type) {
736    this.type = type;
737  }
738
739  @Nullable
740  public DatasetSubtype getSubtype() {
741    return subtype;
742  }
743
744  /**
745   * Persisted in the database table.
746   */
747  public void setSubtype(DatasetSubtype subtype) {
748    this.subtype = subtype;
749  }
750
751  @Nullable
752  public String getShortName() {
753    return shortName;
754  }
755
756  public void setShortName(String shortName) {
757    this.shortName = shortName;
758  }
759
760  /**
761   * TODO: Document what this is
762   */
763  @Nullable
764  @Size(min = 2, max = 50)
765  public String getAlias() {
766    return alias;
767  }
768
769  /**
770   * Persisted in the database table.
771   */
772  public void setAlias(String alias) {
773    this.alias = alias;
774  }
775
776  /**
777   * TODO: Document what this is
778   * TODO: are both alias & abbreviation needed?
779   */
780  @Nullable
781  @Size(min = 1, max = 50)
782  public String getAbbreviation() {
783    return abbreviation;
784  }
785
786  /**
787   * Persisted in the database table.
788   */
789  public void setAbbreviation(String abbreviation) {
790    this.abbreviation = abbreviation;
791  }
792
793  @NotNull
794  public Language getLanguage() {
795    return language;
796  }
797
798  /**
799   * Persisted in the database table.
800   */
801  public void setLanguage(Language language) {
802    this.language = language;
803  }
804
805  @HttpURI
806  @Nullable
807  public URI getHomepage() {
808    return homepage;
809  }
810
811  /**
812   * Persisted in the database table.
813   */
814  public void setHomepage(URI homepage) {
815    this.homepage = homepage;
816  }
817
818  @HttpURI
819  @Nullable
820  public URI getLogoUrl() {
821    return logoUrl;
822  }
823
824  /**
825   * Persisted in the database table.
826   */
827  public void setLogoUrl(URI logoUrl) {
828    this.logoUrl = logoUrl;
829  }
830
831  /**
832   * The exact form of how to cite this dataset.
833   */
834  @Nullable
835  @Valid
836  public Citation getCitation() {
837    return citation;
838  }
839
840  /**
841   * Persisted in the database table.
842   */
843  public void setCitation(Citation citation) {
844    this.citation = citation;
845  }
846
847  /**
848   * A generated list of contacts used in the citation text when it is generated by the GBIF API.
849   */
850  @Nullable
851  public List<CitationContact> getContactsCitation() {
852    return contactsCitation;
853  }
854
855  public void setContactsCitation(List<CitationContact> contactsCitation) {
856    this.contactsCitation = contactsCitation;
857  }
858
859  /**
860   * Any kind of (copy)rights/IPR statements that apply to the datasets data.
861   */
862  @Nullable
863  @Size(min = 1)
864  public String getRights() {
865    return rights;
866  }
867
868  /**
869   * Persisted in the database table.
870   */
871  public void setRights(String rights) {
872    this.rights = rights;
873  }
874
875  public boolean isLockedForAutoUpdate() {
876    return lockedForAutoUpdate;
877  }
878
879  /**
880   * Persisted in the database table.
881   */
882  public void setLockedForAutoUpdate(boolean lockedForAutoUpdate) {
883    this.lockedForAutoUpdate = lockedForAutoUpdate;
884  }
885
886  @Override
887  public String getCreatedBy() {
888    return createdBy;
889  }
890
891  /**
892   * Persisted in the database table.
893   */
894  @Override
895  public void setCreatedBy(String createdBy) {
896    this.createdBy = createdBy;
897  }
898
899  @Override
900  public String getModifiedBy() {
901    return modifiedBy;
902  }
903
904  /**
905   * Persisted in the database table.
906   */
907  @Override
908  public void setModifiedBy(String modifiedBy) {
909    this.modifiedBy = modifiedBy;
910  }
911
912  @Override
913  public List<Contact> getContacts() {
914    return contacts;
915  }
916
917  @Override
918  public void setContacts(List<Contact> contacts) {
919    this.contacts = contacts;
920  }
921
922  @Override
923  public List<Endpoint> getEndpoints() {
924    return endpoints;
925  }
926
927  @Override
928  public void setEndpoints(List<Endpoint> endpoints) {
929    this.endpoints = endpoints;
930  }
931
932  @Override
933  public void addEndpoint(Endpoint endpoint) {
934    endpoints.add(endpoint);
935  }
936
937  @Override
938  public List<MachineTag> getMachineTags() {
939    return machineTags;
940  }
941
942  @Override
943  public void setMachineTags(List<MachineTag> machineTags) {
944    this.machineTags = machineTags;
945  }
946
947  @Override
948  public void addMachineTag(MachineTag machineTag) {
949    machineTags.add(machineTag);
950  }
951
952  @Override
953  public List<Tag> getTags() {
954    return tags;
955  }
956
957  @Override
958  public void setTags(List<Tag> tags) {
959    this.tags = tags;
960  }
961
962  @Override
963  public List<Identifier> getIdentifiers() {
964    return identifiers;
965  }
966
967  @Override
968  public void setIdentifiers(List<Identifier> identifiers) {
969    this.identifiers = identifiers;
970  }
971
972  @Override
973  public List<Comment> getComments() {
974    return comments;
975  }
976
977  @Override
978  public void setComments(List<Comment> comments) {
979    this.comments = comments;
980  }
981
982  public List<Citation> getBibliographicCitations() {
983    return bibliographicCitations;
984  }
985
986  public void setBibliographicCitations(List<Citation> bibliographicCitations) {
987    this.bibliographicCitations = bibliographicCitations;
988  }
989
990  public List<CuratorialUnitComposite> getCuratorialUnits() {
991    return curatorialUnits;
992  }
993
994  public void setCuratorialUnits(List<CuratorialUnitComposite> curatorialUnits) {
995    this.curatorialUnits = curatorialUnits;
996  }
997
998  public List<TaxonomicCoverages> getTaxonomicCoverages() {
999    return taxonomicCoverages;
1000  }
1001
1002  public void setTaxonomicCoverages(List<TaxonomicCoverages> taxonomicCoverages) {
1003    this.taxonomicCoverages = taxonomicCoverages;
1004  }
1005
1006  public String getGeographicCoverageDescription() {
1007    return geographicCoverageDescription;
1008  }
1009
1010  public void setGeographicCoverageDescription(String geographicCoverageDescription) {
1011    this.geographicCoverageDescription = geographicCoverageDescription;
1012  }
1013
1014  public List<GeospatialCoverage> getGeographicCoverages() {
1015    return geographicCoverages;
1016  }
1017
1018  public void setGeographicCoverages(List<GeospatialCoverage> geographicCoverages) {
1019    this.geographicCoverages = geographicCoverages;
1020  }
1021
1022  public List<TemporalCoverage> getTemporalCoverages() {
1023    return temporalCoverages;
1024  }
1025
1026  public void setTemporalCoverages(List<TemporalCoverage> temporalCoverages) {
1027    this.temporalCoverages = temporalCoverages;
1028  }
1029
1030  public List<KeywordCollection> getKeywordCollections() {
1031    return keywordCollections;
1032  }
1033
1034  public void setKeywordCollections(List<KeywordCollection> keywordCollections) {
1035    this.keywordCollections = keywordCollections;
1036  }
1037
1038  public Project getProject() {
1039    return project;
1040  }
1041
1042  public void setProject(Project project) {
1043    this.project = project;
1044  }
1045
1046  public SamplingDescription getSamplingDescription() {
1047    return samplingDescription;
1048  }
1049
1050  public void setSamplingDescription(SamplingDescription samplingDescription) {
1051    this.samplingDescription = samplingDescription;
1052  }
1053
1054  public Set<Country> getCountryCoverage() {
1055    return countryCoverage;
1056  }
1057
1058  public void setCountryCoverage(Set<Country> countryCoverage) {
1059    this.countryCoverage = countryCoverage;
1060  }
1061
1062  public List<Collection> getCollections() {
1063    return collections;
1064  }
1065
1066  public void setCollections(List<Collection> collections) {
1067    this.collections = collections;
1068  }
1069
1070  public List<DataDescription> getDataDescriptions() {
1071    return dataDescriptions;
1072  }
1073
1074  public void setDataDescriptions(List<DataDescription> dataDescriptions) {
1075    this.dataDescriptions = dataDescriptions;
1076  }
1077
1078  public Language getDataLanguage() {
1079    return dataLanguage;
1080  }
1081
1082  public void setDataLanguage(Language dataLanguage) {
1083    this.dataLanguage = dataLanguage;
1084  }
1085
1086  public String getPurpose() {
1087    return purpose;
1088  }
1089
1090  public void setPurpose(String purpose) {
1091    this.purpose = purpose;
1092  }
1093
1094  @Nullable
1095  public String getIntroduction() {
1096    return introduction;
1097  }
1098
1099  public void setIntroduction(String introduction) {
1100    this.introduction = introduction;
1101  }
1102
1103  @Nullable
1104  public String getGettingStarted() {
1105    return gettingStarted;
1106  }
1107
1108  public void setGettingStarted(String gettingStarted) {
1109    this.gettingStarted = gettingStarted;
1110  }
1111
1112  @Nullable
1113  public String getAcknowledgements() {
1114    return acknowledgements;
1115  }
1116
1117  public void setAcknowledgements(String acknowledgements) {
1118    this.acknowledgements = acknowledgements;
1119  }
1120
1121  public String getAdditionalInfo() {
1122    return additionalInfo;
1123  }
1124
1125  public void setAdditionalInfo(String additionalInfo) {
1126    this.additionalInfo = additionalInfo;
1127  }
1128
1129  public Date getPubDate() {
1130    return pubDate;
1131  }
1132
1133  public void setPubDate(Date pubDate) {
1134    this.pubDate = pubDate;
1135  }
1136
1137  @Nullable
1138  @Valid
1139  public DwcA getDwca() {
1140    return dwca;
1141  }
1142
1143  public void setDwca(DwcA dwca) {
1144    this.dwca = dwca;
1145  }
1146
1147  public Set<String> getCategory() {
1148    return category;
1149  }
1150
1151  public void setCategory(Set<String> category) {
1152    this.category = category;
1153  }
1154
1155  @Override
1156  public boolean equals(Object o) {
1157    if (this == o) {
1158      return true;
1159    }
1160    if (o == null || getClass() != o.getClass()) {
1161      return false;
1162    }
1163    Dataset dataset = (Dataset) o;
1164    return external == dataset.external
1165        && numConstituents == dataset.numConstituents
1166        && lockedForAutoUpdate == dataset.lockedForAutoUpdate
1167        && Objects.equals(key, dataset.key)
1168        && Objects.equals(parentDatasetKey, dataset.parentDatasetKey)
1169        && Objects.equals(duplicateOfDatasetKey, dataset.duplicateOfDatasetKey)
1170        && Objects.equals(installationKey, dataset.installationKey)
1171        && Objects.equals(publishingOrganizationKey, dataset.publishingOrganizationKey)
1172        && Objects.equals(publishingOrganizationName, dataset.publishingOrganizationName)
1173        && Objects.equals(networkKeys, dataset.networkKeys)
1174        && Objects.equals(doi, dataset.doi)
1175        && Objects.equals(version, dataset.version)
1176        && type == dataset.type
1177        && subtype == dataset.subtype
1178        && Objects.equals(shortName, dataset.shortName)
1179        && Objects.equals(title, dataset.title)
1180        && Objects.equals(alias, dataset.alias)
1181        && Objects.equals(abbreviation, dataset.abbreviation)
1182        && Objects.equals(description, dataset.description)
1183        && language == dataset.language
1184        && Objects.equals(homepage, dataset.homepage)
1185        && Objects.equals(logoUrl, dataset.logoUrl)
1186        && Objects.equals(citation, dataset.citation)
1187        && Objects.equals(contactsCitation, dataset.contactsCitation)
1188        && Objects.equals(rights, dataset.rights)
1189        && Objects.equals(createdBy, dataset.createdBy)
1190        && Objects.equals(modifiedBy, dataset.modifiedBy)
1191        && Objects.equals(created, dataset.created)
1192        && Objects.equals(modified, dataset.modified)
1193        && Objects.equals(deleted, dataset.deleted)
1194        && Objects.equals(contacts, dataset.contacts)
1195        && Objects.equals(endpoints, dataset.endpoints)
1196        && Objects.equals(machineTags, dataset.machineTags)
1197        && Objects.equals(tags, dataset.tags)
1198        && Objects.equals(identifiers, dataset.identifiers)
1199        && Objects.equals(comments, dataset.comments)
1200        && Objects.equals(bibliographicCitations, dataset.bibliographicCitations)
1201        && Objects.equals(curatorialUnits, dataset.curatorialUnits)
1202        && Objects.equals(taxonomicCoverages, dataset.taxonomicCoverages)
1203        && Objects.equals(geographicCoverageDescription, dataset.geographicCoverageDescription)
1204        && Objects.equals(geographicCoverages, dataset.geographicCoverages)
1205        && Objects.equals(temporalCoverages, dataset.temporalCoverages)
1206        && Objects.equals(keywordCollections, dataset.keywordCollections)
1207        && Objects.equals(project, dataset.project)
1208        && Objects.equals(samplingDescription, dataset.samplingDescription)
1209        && Objects.equals(countryCoverage, dataset.countryCoverage)
1210        && Objects.equals(collections, dataset.collections)
1211        && Objects.equals(dataDescriptions, dataset.dataDescriptions)
1212        && dataLanguage == dataset.dataLanguage
1213        && Objects.equals(purpose, dataset.purpose)
1214        && Objects.equals(introduction, dataset.introduction)
1215        && Objects.equals(gettingStarted, dataset.gettingStarted)
1216        && Objects.equals(acknowledgements, dataset.acknowledgements)
1217        && Objects.equals(additionalInfo, dataset.additionalInfo)
1218        && Objects.equals(pubDate, dataset.pubDate)
1219        && maintenanceUpdateFrequency == dataset.maintenanceUpdateFrequency
1220        && Objects.equals(maintenanceDescription, dataset.maintenanceDescription)
1221        && license == dataset.license
1222        && Objects.equals(dwca, dataset.dwca)
1223        && Objects.equals(category, dataset.category);
1224  }
1225
1226  @Override
1227  public int hashCode() {
1228    return Objects.hash(
1229        key,
1230        parentDatasetKey,
1231        duplicateOfDatasetKey,
1232        installationKey,
1233        publishingOrganizationKey,
1234        publishingOrganizationName,
1235        networkKeys,
1236        doi,
1237        version,
1238        external,
1239        numConstituents,
1240        type,
1241        subtype,
1242        shortName,
1243        title,
1244        alias,
1245        abbreviation,
1246        description,
1247        language,
1248        homepage,
1249        logoUrl,
1250        citation,
1251        contactsCitation,
1252        rights,
1253        lockedForAutoUpdate,
1254        createdBy,
1255        modifiedBy,
1256        created,
1257        modified,
1258        deleted,
1259        contacts,
1260        endpoints,
1261        machineTags,
1262        tags,
1263        identifiers,
1264        comments,
1265        bibliographicCitations,
1266        curatorialUnits,
1267        taxonomicCoverages,
1268        geographicCoverageDescription,
1269        geographicCoverages,
1270        temporalCoverages,
1271        keywordCollections,
1272        project,
1273        samplingDescription,
1274        countryCoverage,
1275        collections,
1276        dataDescriptions,
1277        dataLanguage,
1278        purpose,
1279        introduction,
1280        gettingStarted,
1281        acknowledgements,
1282        additionalInfo,
1283        pubDate,
1284        maintenanceUpdateFrequency,
1285        maintenanceDescription,
1286        license,
1287        dwca,
1288        category);
1289  }
1290
1291  @Override
1292  public String toString() {
1293    return new StringJoiner(", ", Dataset.class.getSimpleName() + "[", "]")
1294        .add("key=" + key)
1295        .add("parentDatasetKey=" + parentDatasetKey)
1296        .add("duplicateOfDatasetKey=" + duplicateOfDatasetKey)
1297        .add("installationKey=" + installationKey)
1298        .add("publishingOrganizationKey=" + publishingOrganizationKey)
1299        .add("publishingOrganizationName=" + publishingOrganizationName)
1300        .add("networkKeys=" + networkKeys)
1301        .add("doi=" + doi)
1302        .add("version='" + version + "'")
1303        .add("external=" + external)
1304        .add("numConstituents=" + numConstituents)
1305        .add("type=" + type)
1306        .add("subtype=" + subtype)
1307        .add("shortName='" + shortName + "'")
1308        .add("title='" + title + "'")
1309        .add("alias='" + alias + "'")
1310        .add("abbreviation='" + abbreviation + "'")
1311        .add("description='" + description + "'")
1312        .add("language=" + language)
1313        .add("homepage=" + homepage)
1314        .add("logoUrl=" + logoUrl)
1315        .add("citation=" + citation)
1316        .add("contactsCitation=" + contactsCitation)
1317        .add("rights='" + rights + "'")
1318        .add("lockedForAutoUpdate=" + lockedForAutoUpdate)
1319        .add("createdBy='" + createdBy + "'")
1320        .add("modifiedBy='" + modifiedBy + "'")
1321        .add("created=" + created)
1322        .add("modified=" + modified)
1323        .add("deleted=" + deleted)
1324        .add("contacts=" + contacts)
1325        .add("endpoints=" + endpoints)
1326        .add("machineTags=" + machineTags)
1327        .add("tags=" + tags)
1328        .add("identifiers=" + identifiers)
1329        .add("comments=" + comments)
1330        .add("bibliographicCitations=" + bibliographicCitations)
1331        .add("curatorialUnits=" + curatorialUnits)
1332        .add("taxonomicCoverages=" + taxonomicCoverages)
1333        .add("geographicCoverageDescription='" + geographicCoverageDescription + "'")
1334        .add("geographicCoverages=" + geographicCoverages)
1335        .add("temporalCoverages=" + temporalCoverages)
1336        .add("keywordCollections=" + keywordCollections)
1337        .add("project=" + project)
1338        .add("samplingDescription=" + samplingDescription)
1339        .add("countryCoverage=" + countryCoverage)
1340        .add("collections=" + collections)
1341        .add("dataDescriptions=" + dataDescriptions)
1342        .add("dataLanguage=" + dataLanguage)
1343        .add("purpose='" + purpose + "'")
1344        .add("introduction='" + introduction + "'")
1345        .add("gettingStarted='" + gettingStarted + "'")
1346        .add("acknowledgements='" + acknowledgements + "'")
1347        .add("additionalInfo='" + additionalInfo + "'")
1348        .add("pubDate=" + pubDate)
1349        .add("maintenanceUpdateFrequency=" + maintenanceUpdateFrequency)
1350        .add("maintenanceDescription='" + maintenanceDescription + "'")
1351        .add("license=" + license)
1352        .add("dwca=" + dwca)
1353        .add("category=" + category)
1354        .toString();
1355  }
1356
1357  /**
1358   * Only checks the persisted properties, excluding the server controlled fields (key, created, license etc).
1359   * Does not include the nested properties.
1360   */
1361  @Override
1362  public boolean lenientEquals(Dataset other) {
1363    if (this == other) {
1364      return true;
1365    }
1366    if (other == null) return false;
1367    return Objects.equals(this.parentDatasetKey, other.parentDatasetKey)
1368        && Objects.equals(this.duplicateOfDatasetKey, other.duplicateOfDatasetKey)
1369        && Objects.equals(this.installationKey, other.installationKey)
1370        && Objects.equals(this.publishingOrganizationKey, other.publishingOrganizationKey)
1371        && Objects.equals(this.publishingOrganizationName, other.publishingOrganizationName)
1372        && Objects.equals(this.doi, other.doi)
1373        && Objects.equals(this.external, other.external)
1374        && Objects.equals(this.type, other.type)
1375        && Objects.equals(this.subtype, other.subtype)
1376        && Objects.equals(this.title, other.title)
1377        && Objects.equals(this.alias, other.alias)
1378        && Objects.equals(this.abbreviation, other.abbreviation)
1379        && Objects.equals(this.description, other.description)
1380        && Objects.equals(this.language, other.language)
1381        && Objects.equals(this.homepage, other.homepage)
1382        && Objects.equals(this.logoUrl, other.logoUrl)
1383        && Objects.equals(this.citation, other.citation)
1384        && Objects.equals(this.rights, other.rights)
1385        && Objects.equals(this.lockedForAutoUpdate, other.lockedForAutoUpdate)
1386        && Objects.equals(this.deleted, other.deleted)
1387        && Objects.equals(this.maintenanceUpdateFrequency, other.maintenanceUpdateFrequency)
1388        && Objects.equals(this.maintenanceDescription, other.maintenanceDescription)
1389        && Objects.equals(this.dwca, other.dwca)
1390        && Objects.equals(this.category, other.category);
1391  }
1392
1393  /**
1394   * Metadata of dataset that has been published as a Darwin Core Archive (DwC-A).
1395   */
1396  @NoArgsConstructor
1397  @Data
1398  public static class DwcA {
1399    @Schema(
1400      description = "This attribute, within the <core>, indicates the specific " +
1401                    "type of data being represented in the core data file.**."
1402    )
1403    private String coreType;
1404
1405    @Schema(
1406      description = "This attribute, within the <extensions>, indicates the specific " +
1407                    "type of data being represented in the associated extension data file.**."
1408    )
1409    private List<String> extensions;
1410
1411    @Schema(
1412      description = "Timestamp of when the dataset DwcA metadata was modified.",
1413      accessMode = Schema.AccessMode.READ_ONLY
1414    )
1415    @Null(groups = {PrePersist.class})
1416    private Date modified;
1417
1418
1419    public String getCoreType() {
1420      return coreType;
1421    }
1422
1423    public void setCoreType(String coreType) {
1424      this.coreType = coreType;
1425    }
1426
1427    @Nullable
1428    public List<String> getExtensions() {
1429      return extensions;
1430    }
1431
1432    public void setExtensions(List<String> extensions) {
1433      this.extensions = extensions;
1434    }
1435
1436    @Nullable
1437    public Date getModified() {
1438      return modified;
1439    }
1440
1441    public void setModified(Date modified) {
1442      this.modified = modified;
1443    }
1444  }
1445
1446  /**
1447   * Metadata of dataset that has been published as a <a href="https://specs.frictionlessdata.io/data-package/">DataPackage</a>.
1448   */
1449  @NoArgsConstructor
1450  @Data
1451  @Experimental
1452  public static class DataPackage {
1453
1454    /**
1455     * Custom deserializer to capture raw JSON string.
1456     */
1457    public static class RawJsonDeserializer extends JsonDeserializer<String> {
1458
1459      public RawJsonDeserializer() {
1460        // Jackson requires this
1461      }
1462
1463      @Override
1464      public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
1465        JsonNode node = p.readValueAsTree();
1466        return node.toString(); // store the raw JSON as a string
1467      }
1468    }
1469
1470    @Schema(
1471      description = "The  content of the <a href=\"https://specs.frictionlessdata.io/schemas/data-package.json\">datapackage.json</a> file."
1472    )
1473    @JsonDeserialize(using = RawJsonDeserializer.class)
1474    @JsonRawValue
1475    private String metadata;
1476
1477    @Schema(
1478      description = "Timestamp of when the dataset datapackage metadata was modified.",
1479      accessMode = Schema.AccessMode.READ_ONLY
1480    )
1481    @Null(groups = {PrePersist.class})
1482    private Date modified;
1483
1484
1485    @Schema(
1486      description = "Unique GBIF key for the dataset.",
1487      accessMode = Schema.AccessMode.READ_ONLY
1488    )
1489    private UUID datasetKey;
1490
1491    @Schema(
1492      description = "Unique GBIF key for the dataset endpoint.",
1493      accessMode = Schema.AccessMode.READ_ONLY
1494    )
1495    private Integer endpointKey;
1496
1497    @NotNull
1498    public String getMetadata() {
1499      return metadata;
1500    }
1501
1502    public void setMetadata(String metadata) {
1503      this.metadata = metadata;
1504    }
1505
1506    @Nullable
1507    public Date getModified() {
1508      return modified;
1509    }
1510
1511    public void setModified(Date modified) {
1512      this.modified = modified;
1513    }
1514
1515    @NotNull
1516    public UUID getDatasetKey() {
1517      return datasetKey;
1518    }
1519
1520    public void setDatasetKey(UUID datasetKey) {
1521      this.datasetKey = datasetKey;
1522    }
1523
1524    @NotNull
1525    public Integer getEndpointKey() {
1526      return endpointKey;
1527    }
1528
1529    public void setEndpointKey(Integer endpointKey) {
1530      this.endpointKey = endpointKey;
1531    }
1532  }
1533}