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