001/*
002 * Copyright 2013 Global Biodiversity Information Facility (GBIF)
003 * Licensed under the Apache License, Version 2.0 (the "License");
004 * you may not use this file except in compliance with the License.
005 * You may obtain a copy of the License at
006 * http://www.apache.org/licenses/LICENSE-2.0
007 * Unless required by applicable law or agreed to in writing, software
008 * distributed under the License is distributed on an "AS IS" BASIS,
009 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
010 * See the License for the specific language governing permissions and
011 * limitations under the License.
012 */
013package org.gbif.api.model.registry;
014
015import org.gbif.api.model.common.DOI;
016import org.gbif.api.model.registry.eml.Collection;
017import org.gbif.api.model.registry.eml.DataDescription;
018import org.gbif.api.model.registry.eml.KeywordCollection;
019import org.gbif.api.model.registry.eml.Project;
020import org.gbif.api.model.registry.eml.SamplingDescription;
021import org.gbif.api.model.registry.eml.TaxonomicCoverages;
022import org.gbif.api.model.registry.eml.curatorial.CuratorialUnitComposite;
023import org.gbif.api.model.registry.eml.geospatial.GeospatialCoverage;
024import org.gbif.api.model.registry.eml.temporal.TemporalCoverage;
025import org.gbif.api.util.HttpURI;
026import org.gbif.api.vocabulary.Country;
027import org.gbif.api.vocabulary.DatasetSubtype;
028import org.gbif.api.vocabulary.DatasetType;
029import org.gbif.api.vocabulary.Language;
030import org.gbif.api.vocabulary.License;
031import org.gbif.api.vocabulary.MaintenanceUpdateFrequency;
032
033import java.net.URI;
034import java.util.Date;
035import java.util.List;
036import java.util.Set;
037import java.util.UUID;
038import javax.annotation.Nullable;
039import javax.validation.Valid;
040import javax.validation.constraints.Min;
041import javax.validation.constraints.NotNull;
042import javax.validation.constraints.Size;
043
044import com.google.common.base.Objects;
045import com.google.common.collect.Lists;
046import com.google.common.collect.Sets;
047
048/**
049 * A GBIF dataset which provides occurrence data, checklist data, sampling event data or metadata.
050 * This Dataset class is covering all of the GBIF metadata profile v1.1, but only few properties are kept in the
051 * database table:
052 * <ul>
053 * <li>key</li>
054 * <li>parentDatasetKey</li>
055 * <li>duplicateOfDatasetKey</li>
056 * <li>version</li>
057 * <li>installationKey</li>
058 * <li>publishingOrganizationKey</li>
059 * <li>license</li>
060 * <li>maintenanceUpdateFrequency</li>
061 * <li>external</li>
062 * <li>numConstituents</li>
063 * <li>type</li>
064 * <li>subtype</li>
065 * <li>title</li>
066 * <li>alias</li>
067 * <li>abbreviation</li>
068 * <li>description</li>
069 * <li>language</li>
070 * <li>homepage</li>
071 * <li>logoUrl</li>
072 * <li>citation</li>
073 * <li>rights</li>
074 * <li>lockedForAutoUpdate</li>
075 * <li>createdBy</li>
076 * <li>modifiedBy</li>
077 * <li>created</li>
078 * <li>modified</li>
079 * <li>deleted</li>
080 * </ul>
081 *
082 * @see <a href="http://rs.gbif.org/schema/eml-gbif-profile/dev/eml.xsd">GBIF EML Profile XML Schema</a>
083 */
084public class Dataset
085  implements NetworkEntity, Contactable, Endpointable, MachineTaggable, Taggable, Identifiable, Commentable,
086  LenientEquals<Dataset> {
087
088  private UUID key;
089  private UUID parentDatasetKey;
090  private UUID duplicateOfDatasetKey;
091  private UUID installationKey;
092  private UUID publishingOrganizationKey;
093  private DOI doi;
094  private String version;
095  private boolean external;
096  private int numConstituents;
097  private DatasetType type;
098  private DatasetSubtype subtype;
099  private String title;
100  private String alias;
101  private String abbreviation;
102  private String description;
103  private Language language = Language.ENGLISH; // sensible default as it is not null
104  private URI homepage;
105  private URI logoUrl;
106
107  private Citation citation = new Citation();
108  private String rights;
109  private boolean lockedForAutoUpdate;
110  private String createdBy;
111  private String modifiedBy;
112  private Date created;
113  private Date modified;
114  private Date deleted;
115  private List<Contact> contacts = Lists.newArrayList();
116  private List<Endpoint> endpoints = Lists.newArrayList();
117  private List<MachineTag> machineTags = Lists.newArrayList();
118  private List<Tag> tags = Lists.newArrayList();
119  private List<Identifier> identifiers = Lists.newArrayList();
120  private List<Comment> comments = Lists.newArrayList();
121  // EML specific properties which are not persisted on the dataset table!
122  private List<Citation> bibliographicCitations = Lists.newArrayList();
123  private List<CuratorialUnitComposite> curatorialUnits = Lists.newArrayList();
124  private List<TaxonomicCoverages> taxonomicCoverages = Lists.newArrayList();
125  private String geographicCoverageDescription;
126  private List<GeospatialCoverage> geographicCoverages = Lists.newArrayList();
127  private List<TemporalCoverage> temporalCoverages = Lists.newArrayList();
128  private List<KeywordCollection> keywordCollections = Lists.newArrayList();
129  private Project project;
130  private SamplingDescription samplingDescription;
131  private Set<Country> countryCoverage = Sets.newHashSet();
132  private List<Collection> collections = Lists.newArrayList();
133  private List<DataDescription> dataDescriptions = Lists.newArrayList();
134  private Language dataLanguage;
135  private String purpose;
136  private String additionalInfo;
137  private Date pubDate;
138  private MaintenanceUpdateFrequency maintenanceUpdateFrequency;
139  private String maintenanceDescription;
140  private License license;
141
142  @Override
143  public UUID getKey() {
144    return key;
145  }
146
147  @Override
148  /**
149   * Persisted in the database table.
150   */
151  public void setKey(UUID key) {
152    this.key = key;
153  }
154
155  @Nullable
156  public String getVersion() {
157    return version;
158  }
159
160  public void setVersion(String version) {
161    this.version = version;
162  }
163
164  @Override
165  public String getTitle() {
166    return title;
167  }
168
169  @Override
170  /**
171   * Persisted in the database table.
172   */
173  public void setTitle(String title) {
174    this.title = title;
175  }
176
177  @Override
178  @Nullable
179  public String getDescription() {
180    return description;
181  }
182
183  /**
184   * Persisted in the database table.
185   */
186  @Override
187  public void setDescription(String description) {
188    this.description = description;
189  }
190
191  @Override
192  public Date getCreated() {
193    return created;
194  }
195
196  @Override
197  /**
198   * Autoassigned in the database table, ignored when persisted.
199   */
200  public void setCreated(Date created) {
201    this.created = created;
202  }
203
204  @Override
205  public Date getModified() {
206    return modified;
207  }
208
209  @Override
210  /**
211   * Persisted in the database table.
212   */
213  public void setModified(Date modified) {
214    this.modified = modified;
215  }
216
217  @Override
218  @Nullable
219  public Date getDeleted() {
220    return deleted;
221  }
222
223  /**
224   * Persisted in the database table.
225   */
226  @Override
227  public void setDeleted(Date deleted) {
228    this.deleted = deleted;
229  }
230
231  @Nullable
232  public UUID getParentDatasetKey() {
233    return parentDatasetKey;
234  }
235
236  /**
237   * Persisted in the database table.
238   */
239  public void setParentDatasetKey(UUID parentDatasetKey) {
240    this.parentDatasetKey = parentDatasetKey;
241  }
242
243  /**
244   * If a dataset is registered with GBIF through more than one place we'll mark all but one as a duplicate by pointing
245   * it to the canonical dataset. That is done using this field. If it is {@code null} then this is not a known
246   * duplicate.
247   */
248  @Nullable
249  public UUID getDuplicateOfDatasetKey() {
250    return duplicateOfDatasetKey;
251  }
252
253  /**
254   * Persisted in the database table.
255   */
256  public void setDuplicateOfDatasetKey(UUID duplicateOfDatasetKey) {
257    this.duplicateOfDatasetKey = duplicateOfDatasetKey;
258  }
259
260  @NotNull
261  public UUID getInstallationKey() {
262    return installationKey;
263  }
264
265  /**
266   * Persisted in the database table.
267   */
268  public void setInstallationKey(UUID installationKey) {
269    this.installationKey = installationKey;
270  }
271
272  @NotNull
273  public UUID getPublishingOrganizationKey() {
274    return publishingOrganizationKey;
275  }
276
277  /**
278   * Persisted in the database table.
279   */
280  public void setPublishingOrganizationKey(UUID publishingOrganizationKey) {
281    this.publishingOrganizationKey = publishingOrganizationKey;
282  }
283
284  /**
285   * Persisted in the database table.
286   *
287   * @return the frequency with which changes are made to the dataset
288   */
289  @Nullable
290  public MaintenanceUpdateFrequency getMaintenanceUpdateFrequency() {
291    return maintenanceUpdateFrequency;
292  }
293  /**
294   * Persisted in the database table.
295   */
296  public void setMaintenanceUpdateFrequency(MaintenanceUpdateFrequency maintenanceUpdateFrequency) {
297    this.maintenanceUpdateFrequency = maintenanceUpdateFrequency;
298  }
299
300  /**
301   * A description of the maintenance frequency of this resource.
302   *
303   * @return the description of the maintenance frequency of this resource
304   */
305  public String getMaintenanceDescription() {
306    return maintenanceDescription;
307  }
308
309  public void setMaintenanceDescription(String maintenanceDescription) {
310    this.maintenanceDescription = maintenanceDescription;
311  }
312
313  /**
314   * Persisted in the database table.
315   * </br>
316   * Note for backwards compatibility, we cannot apply @NotNull to license. Otherwise existing users of our API
317   * would have to ensure Dataset objects always populate license.
318   * </br>
319   * In the Registry DB, Dataset.license defaults to CC-BY 4.0. Therefore license must be excluded from lenientEquals
320   * method.
321   *
322   * @return the License applied to the dataset
323   *
324   * @see <a href="http://dev.gbif.org/issues/browse/POR-3133">POR-3133</a>
325   */
326  public License getLicense() {
327    return license;
328  }
329
330  /**
331   * Persisted in the database table. Can be interpreted from EML.intellectualRights using machine readable format:
332   * <pre>
333   * {@code
334   * <intellectualRights>
335   *   <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>
336   * </intellectualRights>
337   * }
338   * </pre>
339   */
340  public void setLicense(License license) {
341    this.license = license;
342  }
343
344  /**
345   * @return the primary DOI for this dataset regardless if issued by GBIF or publisher
346   */
347  public DOI getDoi() {
348    return doi;
349  }
350
351  public void setDoi(DOI doi) {
352    this.doi = doi;
353  }
354
355  public boolean isExternal() {
356    return external;
357  }
358
359  /**
360   * Persisted in the database table.
361   */
362  public void setExternal(boolean external) {
363    this.external = external;
364  }
365
366  @Min(0)
367  public int getNumConstituents() {
368    return numConstituents;
369  }
370
371  /**
372   * Not persisted in the database table, but calculated on the fly.
373   */
374  public void setNumConstituents(int numConstituents) {
375    this.numConstituents = numConstituents;
376  }
377
378  @NotNull
379  public DatasetType getType() {
380    return type;
381  }
382
383  /**
384   * Persisted in the database table.
385   */
386  public void setType(DatasetType type) {
387    this.type = type;
388  }
389
390  @Nullable
391  public DatasetSubtype getSubtype() {
392    return subtype;
393  }
394
395  /**
396   * Persisted in the database table.
397   */
398  public void setSubtype(DatasetSubtype subtype) {
399    this.subtype = subtype;
400  }
401
402  /**
403   * TODO: Document what this is
404   */
405  @Nullable
406  @Size(min = 2, max = 50)
407  public String getAlias() {
408    return alias;
409  }
410
411  /**
412   * Persisted in the database table.
413   */
414  public void setAlias(String alias) {
415    this.alias = alias;
416  }
417
418  /**
419   * TODO: Document what this is
420   * TODO: are both alias & abbreviation needed?
421   */
422  @Nullable
423  @Size(min = 1, max = 50)
424  public String getAbbreviation() {
425    return abbreviation;
426  }
427
428  /**
429   * Persisted in the database table.
430   */
431  public void setAbbreviation(String abbreviation) {
432    this.abbreviation = abbreviation;
433  }
434
435  @NotNull
436  public Language getLanguage() {
437    return language;
438  }
439
440  /**
441   * Persisted in the database table.
442   */
443  public void setLanguage(Language language) {
444    this.language = language;
445  }
446
447  @HttpURI
448  @Nullable
449  public URI getHomepage() {
450    return homepage;
451  }
452
453  /**
454   * Persisted in the database table.
455   */
456  public void setHomepage(URI homepage) {
457    this.homepage = homepage;
458  }
459
460  @HttpURI
461  @Nullable
462  public URI getLogoUrl() {
463    return logoUrl;
464  }
465
466  /**
467   * Persisted in the database table.
468   */
469  public void setLogoUrl(URI logoUrl) {
470    this.logoUrl = logoUrl;
471  }
472
473  /**
474   * The exact form of how to cite this dataset.
475   */
476  @Nullable
477  @Valid
478  public Citation getCitation() {
479    return citation;
480  }
481
482  /**
483   * Persisted in the database table.
484   */
485  public void setCitation(Citation citation) {
486    this.citation = citation;
487  }
488  
489  /**
490   * Any kind of (copy)rights/IPR statements that apply to the datasets data.
491   */
492  @Nullable
493  @Size(min = 1)
494  public String getRights() {
495    return rights;
496  }
497
498  /**
499   * Persisted in the database table.
500   */
501  public void setRights(String rights) {
502    this.rights = rights;
503  }
504
505  public boolean isLockedForAutoUpdate() {
506    return lockedForAutoUpdate;
507  }
508
509  /**
510   * Persisted in the database table.
511   */
512  public void setLockedForAutoUpdate(boolean lockedForAutoUpdate) {
513    this.lockedForAutoUpdate = lockedForAutoUpdate;
514  }
515
516  @Override
517  public String getCreatedBy() {
518    return createdBy;
519  }
520
521  /**
522   * Persisted in the database table.
523   */
524  @Override
525  public void setCreatedBy(String createdBy) {
526    this.createdBy = createdBy;
527  }
528
529  @Override
530  public String getModifiedBy() {
531    return modifiedBy;
532  }
533
534  /**
535   * Persisted in the database table.
536   */
537  @Override
538  public void setModifiedBy(String modifiedBy) {
539    this.modifiedBy = modifiedBy;
540  }
541
542  @Override
543  public List<Contact> getContacts() {
544    return contacts;
545  }
546
547  @Override
548  public void setContacts(List<Contact> contacts) {
549    this.contacts = contacts;
550  }
551
552  @Override
553  public List<Endpoint> getEndpoints() {
554    return endpoints;
555  }
556
557  @Override
558  public void setEndpoints(List<Endpoint> endpoints) {
559    this.endpoints = endpoints;
560  }
561
562  @Override
563  public void addEndpoint(Endpoint endpoint) {
564    endpoints.add(endpoint);
565  }
566
567  @Override
568  public List<MachineTag> getMachineTags() {
569    return machineTags;
570  }
571
572  @Override
573  public void setMachineTags(List<MachineTag> machineTags) {
574    this.machineTags = machineTags;
575  }
576
577  @Override
578  public void addMachineTag(MachineTag machineTag) {
579    machineTags.add(machineTag);
580  }
581
582  @Override
583  public List<Tag> getTags() {
584    return tags;
585  }
586
587  @Override
588  public void setTags(List<Tag> tags) {
589    this.tags = tags;
590  }
591
592  @Override
593  public List<Identifier> getIdentifiers() {
594    return identifiers;
595  }
596
597  @Override
598  public void setIdentifiers(List<Identifier> identifiers) {
599    this.identifiers = identifiers;
600  }
601
602  @Override
603  public List<Comment> getComments() {
604    return comments;
605  }
606
607  @Override
608  public void setComments(List<Comment> comments) {
609    this.comments = comments;
610  }
611
612  public List<Citation> getBibliographicCitations() {
613    return bibliographicCitations;
614  }
615
616  public void setBibliographicCitations(List<Citation> bibliographicCitations) {
617    this.bibliographicCitations = bibliographicCitations;
618  }
619
620  public List<CuratorialUnitComposite> getCuratorialUnits() {
621    return curatorialUnits;
622  }
623
624  public void setCuratorialUnits(List<CuratorialUnitComposite> curatorialUnits) {
625    this.curatorialUnits = curatorialUnits;
626  }
627
628  public List<TaxonomicCoverages> getTaxonomicCoverages() {
629    return taxonomicCoverages;
630  }
631
632  public void setTaxonomicCoverages(List<TaxonomicCoverages> taxonomicCoverages) {
633    this.taxonomicCoverages = taxonomicCoverages;
634  }
635
636  public String getGeographicCoverageDescription() {
637    return geographicCoverageDescription;
638  }
639
640  public void setGeographicCoverageDescription(String geographicCoverageDescription) {
641    this.geographicCoverageDescription = geographicCoverageDescription;
642  }
643
644  public List<GeospatialCoverage> getGeographicCoverages() {
645    return geographicCoverages;
646  }
647
648  public void setGeographicCoverages(List<GeospatialCoverage> geographicCoverages) {
649    this.geographicCoverages = geographicCoverages;
650  }
651
652  public List<TemporalCoverage> getTemporalCoverages() {
653    return temporalCoverages;
654  }
655
656  public void setTemporalCoverages(List<TemporalCoverage> temporalCoverages) {
657    this.temporalCoverages = temporalCoverages;
658  }
659
660  public List<KeywordCollection> getKeywordCollections() {
661    return keywordCollections;
662  }
663
664  public void setKeywordCollections(List<KeywordCollection> keywordCollections) {
665    this.keywordCollections = keywordCollections;
666  }
667
668  public Project getProject() {
669    return project;
670  }
671
672  public void setProject(Project project) {
673    this.project = project;
674  }
675
676  public SamplingDescription getSamplingDescription() {
677    return samplingDescription;
678  }
679
680  public void setSamplingDescription(SamplingDescription samplingDescription) {
681    this.samplingDescription = samplingDescription;
682  }
683
684  public Set<Country> getCountryCoverage() {
685    return countryCoverage;
686  }
687
688  public void setCountryCoverage(Set<Country> countryCoverage) {
689    this.countryCoverage = countryCoverage;
690  }
691
692  public List<Collection> getCollections() {
693    return collections;
694  }
695
696  public void setCollections(List<Collection> collections) {
697    this.collections = collections;
698  }
699
700  public List<DataDescription> getDataDescriptions() {
701    return dataDescriptions;
702  }
703
704  public void setDataDescriptions(List<DataDescription> dataDescriptions) {
705    this.dataDescriptions = dataDescriptions;
706  }
707
708  public Language getDataLanguage() {
709    return dataLanguage;
710  }
711
712  public void setDataLanguage(Language dataLanguage) {
713    this.dataLanguage = dataLanguage;
714  }
715
716  public String getPurpose() {
717    return purpose;
718  }
719
720  public void setPurpose(String purpose) {
721    this.purpose = purpose;
722  }
723
724  public String getAdditionalInfo() {
725    return additionalInfo;
726  }
727
728  public void setAdditionalInfo(String additionalInfo) {
729    this.additionalInfo = additionalInfo;
730  }
731
732  public Date getPubDate() {
733    return pubDate;
734  }
735
736  public void setPubDate(Date pubDate) {
737    this.pubDate = pubDate;
738  }
739
740  @Override
741  public int hashCode() {
742    return Objects
743      .hashCode(key, doi, version, parentDatasetKey, duplicateOfDatasetKey, installationKey, publishingOrganizationKey, external,
744        numConstituents, type, subtype, title, alias, abbreviation, description, language, homepage, logoUrl, citation,
745        rights, lockedForAutoUpdate, createdBy, modifiedBy, created, modified, deleted, contacts, endpoints,
746        machineTags, tags, identifiers, comments, bibliographicCitations, curatorialUnits, taxonomicCoverages,
747        geographicCoverageDescription, geographicCoverages, temporalCoverages, keywordCollections, project,
748        samplingDescription, countryCoverage, collections, dataDescriptions, dataLanguage, purpose, additionalInfo,
749        pubDate, maintenanceUpdateFrequency, maintenanceDescription, license);
750  }
751
752  @Override
753  public boolean equals(Object object) {
754    if (object instanceof Dataset) {
755      Dataset that = (Dataset) object;
756      return Objects.equal(this.key, that.key)
757             && Objects.equal(this.doi, that.doi)
758             && Objects.equal(this.parentDatasetKey, that.parentDatasetKey)
759             && Objects.equal(this.duplicateOfDatasetKey, that.duplicateOfDatasetKey)
760             && Objects.equal(this.version, that.version)
761             && Objects.equal(this.installationKey, that.installationKey)
762             && Objects.equal(this.publishingOrganizationKey, that.publishingOrganizationKey)
763             && Objects.equal(this.external, that.external)
764             && Objects.equal(this.numConstituents, that.numConstituents)
765             && Objects.equal(this.type, that.type)
766             && Objects.equal(this.subtype, that.subtype)
767             && Objects.equal(this.title, that.title)
768             && Objects.equal(this.alias, that.alias)
769             && Objects.equal(this.abbreviation, that.abbreviation)
770             && Objects.equal(this.description, that.description)
771             && Objects.equal(this.language, that.language)
772             && Objects.equal(this.homepage, that.homepage)
773             && Objects.equal(this.logoUrl, that.logoUrl)
774             && Objects.equal(this.citation, that.citation)
775             && Objects.equal(this.rights, that.rights)
776             && Objects.equal(this.lockedForAutoUpdate, that.lockedForAutoUpdate)
777             && Objects.equal(this.createdBy, that.createdBy)
778             && Objects.equal(this.modifiedBy, that.modifiedBy)
779             && Objects.equal(this.created, that.created)
780             && Objects.equal(this.modified, that.modified)
781             && Objects.equal(this.deleted, that.deleted)
782             && Objects.equal(this.contacts, that.contacts)
783             && Objects.equal(this.endpoints, that.endpoints)
784             && Objects.equal(this.machineTags, that.machineTags)
785             && Objects.equal(this.tags, that.tags)
786             && Objects.equal(this.identifiers, that.identifiers)
787             && Objects.equal(this.comments, that.comments)
788             && Objects.equal(this.bibliographicCitations, that.bibliographicCitations)
789             && Objects.equal(this.curatorialUnits, that.curatorialUnits)
790             && Objects.equal(this.taxonomicCoverages, that.taxonomicCoverages)
791             && Objects.equal(this.geographicCoverageDescription, that.geographicCoverageDescription)
792             && Objects.equal(this.geographicCoverages, that.geographicCoverages)
793             && Objects.equal(this.temporalCoverages, that.temporalCoverages)
794             && Objects.equal(this.keywordCollections, that.keywordCollections)
795             && Objects.equal(this.project, that.project)
796             && Objects.equal(this.samplingDescription, that.samplingDescription)
797             && Objects.equal(this.countryCoverage, that.countryCoverage)
798             && Objects.equal(this.collections, that.collections)
799             && Objects.equal(this.dataDescriptions, that.dataDescriptions)
800             && Objects.equal(this.dataLanguage, that.dataLanguage)
801             && Objects.equal(this.purpose, that.purpose)
802             && Objects.equal(this.additionalInfo, that.additionalInfo)
803             && Objects.equal(this.pubDate, that.pubDate)
804             && Objects.equal(this.maintenanceUpdateFrequency, that.maintenanceUpdateFrequency)
805             && Objects.equal(this.maintenanceDescription, that.maintenanceDescription)
806             && Objects.equal(this.license, that.license);
807    }
808    return false;
809  }
810
811  @Override
812  public String toString() {
813    return Objects.toStringHelper(this)
814      .add("key", key).add("doi", doi).add("version", version).add("parentDatasetKey", parentDatasetKey)
815      .add("duplicateOfDatasetKey", duplicateOfDatasetKey).add("installationKey", installationKey)
816      .add("publishingOrganizationKey", publishingOrganizationKey).add("numConstituents", numConstituents).add("type",
817        type)
818      .add("subtype", subtype).add("title", title).add("alias", alias).add("abbreviation", abbreviation)
819      .add("description", description).add("language", language).add("homepage", homepage).add("logoUrl", logoUrl)
820      .add("citation", citation).add("rights", rights).add("lockedForAutoUpdate", lockedForAutoUpdate)
821      .add("createdBy", createdBy).add("modifiedBy", modifiedBy).add("created", created).add("modified", modified)
822      .add("deleted", deleted).add("contacts", contacts).add("endpoints", endpoints).add("machineTags", machineTags)
823      .add("tags", tags).add("identifiers", identifiers).add("comments", comments)
824      .add("bibliographicCitations", bibliographicCitations).add("curatorialUnits", curatorialUnits)
825      .add("taxonomicCoverages", taxonomicCoverages)
826      .add("geographicCoverageDescription", geographicCoverageDescription)
827      .add("geographicCoverages", geographicCoverages).add("temporalCoverages", temporalCoverages)
828      .add("keywordCollections", keywordCollections).add("project", project)
829      .add("samplingDescription", samplingDescription).add("countryCoverage", countryCoverage)
830      .add("collections", collections).add("dataDescriptions", dataDescriptions).add("dataLanguage", dataLanguage)
831      .add("purpose", purpose).add("additionalInfo", additionalInfo).add("pubDate", pubDate)
832      .add("maintenanceUpdateFrequency", maintenanceUpdateFrequency)
833      .add("maintenanceDescription", maintenanceDescription).add("license", license).toString();
834  }
835
836  /**
837   * Only checks the persisted properties, excluding the server controlled fields (key, created, license etc).
838   * Does not include the nested properties.
839   */
840  @Override
841  public boolean lenientEquals(Dataset other) {
842    if (this == other) {
843      return true;
844    }
845    return Objects.equal(this.parentDatasetKey, other.parentDatasetKey)
846      && Objects.equal(this.duplicateOfDatasetKey, other.duplicateOfDatasetKey)
847      && Objects.equal(this.installationKey, other.installationKey)
848      && Objects.equal(this.publishingOrganizationKey, other.publishingOrganizationKey)
849      && Objects.equal(this.doi, other.doi)
850      && Objects.equal(this.external, other.external)
851      && Objects.equal(this.type, other.type)
852      && Objects.equal(this.subtype, other.subtype)
853      && Objects.equal(this.title, other.title)
854      && Objects.equal(this.alias, other.alias)
855      && Objects.equal(this.abbreviation, other.abbreviation)
856      && Objects.equal(this.description, other.description)
857      && Objects.equal(this.language, other.language)
858      && Objects.equal(this.homepage, other.homepage)
859      && Objects.equal(this.logoUrl, other.logoUrl)
860      && Objects.equal(this.citation, other.citation)
861      && Objects.equal(this.rights, other.rights)
862      && Objects.equal(this.lockedForAutoUpdate, other.lockedForAutoUpdate)
863      && Objects.equal(this.deleted, other.deleted)
864      && Objects.equal(this.maintenanceUpdateFrequency, other.maintenanceUpdateFrequency)
865      && Objects.equal(this.maintenanceDescription, other.maintenanceDescription);
866  }
867}