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.collections;
015
016import com.fasterxml.jackson.annotation.JsonIgnore;
017
018import lombok.EqualsAndHashCode;
019import lombok.ToString;
020
021import org.gbif.api.model.registry.Comment;
022import org.gbif.api.model.registry.Identifier;
023import org.gbif.api.model.registry.LenientEquals;
024import org.gbif.api.model.registry.MachineTag;
025import org.gbif.api.model.registry.PrePersist;
026import org.gbif.api.model.registry.Tag;
027import org.gbif.api.util.HttpURI;
028import org.gbif.api.util.LenientEqualsUtils;
029import org.gbif.api.util.validators.email.ValidEmail;
030import org.gbif.api.vocabulary.collections.Discipline;
031import org.gbif.api.vocabulary.collections.InstitutionGovernance;
032import org.gbif.api.vocabulary.collections.InstitutionType;
033import org.gbif.api.vocabulary.collections.MasterSourceType;
034
035import java.math.BigDecimal;
036import java.net.URI;
037import java.util.ArrayList;
038import java.util.Date;
039import java.util.List;
040import java.util.Objects;
041import java.util.StringJoiner;
042import java.util.UUID;
043
044import javax.annotation.Nullable;
045import javax.validation.Valid;
046import javax.validation.constraints.NotNull;
047import javax.validation.constraints.Size;
048
049import io.swagger.v3.oas.annotations.Hidden;
050import io.swagger.v3.oas.annotations.media.Schema;
051
052/**
053 * The owner or location of collection. Usually an established organization or foundation,
054 * especially one dedicated to education, public service, or culture.
055 */
056@SuppressWarnings("unused")
057@ToString
058@EqualsAndHashCode
059public class Institution implements CollectionEntity, LenientEquals<Institution> {
060
061  @Schema(
062      description = "Unique GBIF key for the institution.",
063      accessMode = Schema.AccessMode.READ_ONLY)
064  private UUID key;
065
066  @Schema(
067      description =
068          "Code used to identify the institution.\n\n" + "*(NB Not required for updates.)*")
069  @Sourceable(masterSources = MasterSourceType.IH)
070  private String code;
071
072  @Schema(
073      description = "Name or title of the institution.\n\n" + "*(NB Not required for updates.)*")
074  @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH})
075  private String name;
076
077  @Schema(description = "Description of the institution.")
078  @Sourceable(masterSources = MasterSourceType.GBIF_REGISTRY)
079  private String description;
080
081  @Schema(description = "Type of the institution, describing its main activity.")
082  private InstitutionType type;
083
084  @Schema(description = "Whether the institution is active or operational.")
085  @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH})
086  private boolean active;
087
088  @Schema(description = "Email addresses associated with the institution.")
089  @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH})
090  private List<@ValidEmail String> email = new ArrayList<>();
091
092  @Schema(description = "Telephone numbers associated with the instutiton.")
093  @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH})
094  private List<String> phone = new ArrayList<>();
095
096  @Schema(description = "The institution's WWW homepage.")
097  @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH})
098  private URI homepage;
099
100  @Schema(description = "A URL for the main interactive catalogue of the institution.")
101  private URI catalogUrl;
102
103  @Schema(description = "A URL for a machine-readable API for the institution catalogue.")
104  private URI apiUrl;
105
106  @Schema(
107      description =
108          "The mechanisms, processes and relations by which an "
109              + "institution is controlled and directed.")
110  private InstitutionGovernance institutionalGovernance;
111
112  @Schema(
113      description =
114          "The academic or research disciplines to which an " + "institution is dedicated.")
115  private List<Discipline> disciplines = new ArrayList<>();
116
117  @Schema(description = "The latitude of the institution.")
118  @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH})
119  private BigDecimal latitude;
120
121  @Schema(description = "The longitude of the institution.")
122  @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH})
123  private BigDecimal longitude;
124
125  @Schema(description = "The postal address of the institution.")
126  @Sourceable(masterSources = MasterSourceType.IH)
127  private Address mailingAddress;
128
129  @Schema(description = "The address of the location of the institution.")
130  @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH})
131  private Address address;
132
133  @Schema(description = "Additional names by which the institution is known.")
134  private List<String> additionalNames = new ArrayList<>();
135
136  @Schema(description = "The date the institution was founded or established.")
137  @Sourceable(masterSources = MasterSourceType.IH)
138  private Integer foundingDate;
139
140  @Schema(
141      description =
142          "A description of the geographic range of the activities "
143              + "performed by the institution.")
144  private String geographicDescription;
145
146  @Schema(
147      description =
148          "A description of the taxonomic range of the activities "
149              + "performed by the institution.")
150  private String taxonomicDescription;
151
152  @Schema(description = "An estimate of the number of specimens hosted by the institution.")
153  private Integer numberSpecimens;
154
155  @Schema(description = "Whether this institution record was imported from *Index Herbariorum*.")
156  @Sourceable(masterSources = MasterSourceType.IH)
157  private boolean indexHerbariorumRecord;
158
159  @Schema(description = "A URL to a logo for the institution.")
160  @Sourceable(masterSources = MasterSourceType.GBIF_REGISTRY)
161  private URI logoUrl;
162
163  @Schema(description = "The CITES permit number for the institution.")
164  private String citesPermitNumber;
165
166  @Schema(
167      description =
168          "The GBIF username of the creator of the institution entity in " + "the GBIF registry.",
169      accessMode = Schema.AccessMode.READ_ONLY)
170  private String createdBy;
171
172  @Schema(
173      description =
174          "The GBIF username of the last user to modify the institution "
175              + "entity in the GBIF registry.",
176      accessMode = Schema.AccessMode.READ_ONLY)
177  private String modifiedBy;
178
179  @Schema(
180      description =
181          "Timestamp of when the institution entity was created in the GBIF " + "registry.",
182      accessMode = Schema.AccessMode.READ_ONLY)
183  private Date created;
184
185  @Schema(
186      description =
187          "Timestamp of when the institution entity was modified in the GBIF " + "registry.",
188      accessMode = Schema.AccessMode.READ_ONLY)
189  private Date modified;
190
191  @Schema(
192      description =
193          "If present, the institution was deleted at this time. "
194              + "It is possible for it to be restored in the future.",
195      accessMode = Schema.AccessMode.READ_ONLY)
196  private Date deleted;
197
198  @Schema(
199      description = "A list of tags associated with this institution.",
200      accessMode = Schema.AccessMode.READ_ONLY)
201  private List<Tag> tags = new ArrayList<>();
202
203  @Schema(
204      description = "A list of identifiers associated with this institution.",
205      accessMode = Schema.AccessMode.READ_ONLY)
206  @Sourceable(masterSources = MasterSourceType.IH, sourceableParts = "IH_IRN")
207  private List<Identifier> identifiers = new ArrayList<>();
208
209  @Schema(description = "A list of contact people for this institution.")
210  @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH})
211  private List<Contact> contactPersons = new ArrayList<>();
212
213  @Schema(
214      description = "A list of machine tags associated with this institution.",
215      accessMode = Schema.AccessMode.READ_ONLY)
216  private List<MachineTag> machineTags = new ArrayList<>();
217
218  @Schema(description = "Alternative codes for this institution.")
219  private List<AlternativeCode> alternativeCodes = new ArrayList<>();
220
221  @Schema(
222      description = "A list of comments associated with this institution.",
223      accessMode = Schema.AccessMode.READ_ONLY)
224  private List<Comment> comments = new ArrayList<>();
225
226  @Schema(
227      description = "Mapping of a GRSciColl institution to occurrence records",
228      accessMode = Schema.AccessMode.READ_ONLY)
229  private List<OccurrenceMapping> occurrenceMappings = new ArrayList<>();
230
231  @Schema(description = "A collection record that replaces this collection.")
232  private UUID replacedBy;
233
234  @Schema(
235      description =
236          "Indicates if the institution was converted to a collection and specifies the UUID key of that collection")
237  private UUID convertedToCollection;
238
239  @Schema(description = "The primary source of this institution record.")
240  private MasterSourceType masterSource;
241
242  @Schema(
243      description =
244          "Information to assist the synchronization of the master source "
245              + "record with the record in the GBIF registry.")
246  private MasterSourceMetadata masterSourceMetadata;
247
248  @Schema(description = "Whether the institution is shown on the NHC portal.")
249  private Boolean displayOnNHCPortal;
250
251  @Schema(description = "An estimate of the number of occurrences linked to the institution.")
252  private Integer occurrenceCount;
253
254  @Schema(description = "An estimate of the number of type specimens linked to the institution.")
255  private Integer typeSpecimenCount;
256
257  /** GBIF unique identifier. */
258  @Override
259  public UUID getKey() {
260    return key;
261  }
262
263  @Override
264  public void setKey(UUID key) {
265    this.key = key;
266  }
267
268  /** Code used to identify the collection. */
269  @NotNull(groups = PrePersist.class)
270  @Override
271  public String getCode() {
272    return code;
273  }
274
275  @Override
276  public void setCode(String code) {
277    this.code = code;
278  }
279
280  /** Name or title of an institution. */
281  @NotNull
282  @Override
283  public String getName() {
284    return name;
285  }
286
287  @Override
288  public void setName(String name) {
289    this.name = name;
290  }
291
292  /** Textual description of institution. */
293  @Nullable
294  @Size(min = 1)
295  @Override
296  public String getDescription() {
297    return description;
298  }
299
300  @Override
301  public void setDescription(String description) {
302    this.description = description;
303  }
304
305  /** Describes the main activity of an institution. */
306  public InstitutionType getType() {
307    return type;
308  }
309
310  public void setType(InstitutionType type) {
311    this.type = type;
312  }
313
314  /** Is the institution active/operational?. */
315  @Override
316  public boolean isActive() {
317    return active;
318  }
319
320  @Override
321  public void setActive(boolean active) {
322    this.active = active;
323  }
324
325  @Override
326  public List<String> getEmail() {
327    return email;
328  }
329
330  @Override
331  public void setEmail(List<String> email) {
332    this.email = email;
333  }
334
335  @Override
336  public List<String> getPhone() {
337    return phone;
338  }
339
340  @Override
341  public void setPhone(List<String> phone) {
342    this.phone = phone;
343  }
344
345  /** URL to the home page of an institution. */
346  @HttpURI
347  @Nullable
348  public URI getHomepage() {
349    return homepage;
350  }
351
352  public void setHomepage(URI homepage) {
353    this.homepage = homepage;
354  }
355
356  /** URL to the main catalogue of an institution. */
357  @HttpURI
358  @Nullable
359  public URI getCatalogUrl() {
360    return catalogUrl;
361  }
362
363  public void setCatalogUrl(URI catalogUrl) {
364    this.catalogUrl = catalogUrl;
365  }
366
367  /** Machine-consumable endpoint of an institution and probably its collections. */
368  @HttpURI
369  @Nullable
370  public URI getApiUrl() {
371    return apiUrl;
372  }
373
374  public void setApiUrl(URI apiUrl) {
375    this.apiUrl = apiUrl;
376  }
377
378  /** Governance nature of an institution. */
379  public InstitutionGovernance getInstitutionalGovernance() {
380    return institutionalGovernance;
381  }
382
383  public void setInstitutionalGovernance(InstitutionGovernance institutionalGovernance) {
384    this.institutionalGovernance = institutionalGovernance;
385  }
386
387  /** Activities to which an institution is dedicated. */
388  public List<Discipline> getDisciplines() {
389    return disciplines;
390  }
391
392  public void setDisciplines(List<Discipline> disciplines) {
393    this.disciplines = disciplines;
394  }
395
396  /** Decimal latitude of where this institution is located. */
397  public BigDecimal getLatitude() {
398    return latitude;
399  }
400
401  public void setLatitude(BigDecimal latitude) {
402    this.latitude = latitude;
403  }
404
405  /** Decimal longitude of where this institution is located. */
406  public BigDecimal getLongitude() {
407    return longitude;
408  }
409
410  public void setLongitude(BigDecimal longitude) {
411    this.longitude = longitude;
412  }
413
414  @Nullable
415  @Valid
416  @Override
417  public Address getMailingAddress() {
418    return mailingAddress;
419  }
420
421  @Override
422  public void setMailingAddress(Address mailingAddress) {
423    this.mailingAddress = mailingAddress;
424  }
425
426  @Nullable
427  @Valid
428  @Override
429  public Address getAddress() {
430    return address;
431  }
432
433  @Override
434  public void setAddress(Address address) {
435    this.address = address;
436  }
437
438  /** Alternative names of institution. */
439  public List<String> getAdditionalNames() {
440    return additionalNames;
441  }
442
443  public void setAdditionalNames(List<String> additionalNames) {
444    this.additionalNames = additionalNames;
445  }
446
447  /** Date when the institution was founded or established. */
448  public Integer getFoundingDate() {
449    return foundingDate;
450  }
451
452  public void setFoundingDate(Integer foundingDate) {
453    this.foundingDate = foundingDate;
454  }
455
456  /** Geographical coverage of the activities performed by an institution. */
457  public String getGeographicDescription() {
458    return geographicDescription;
459  }
460
461  public void setGeographicDescription(String geographicDescription) {
462    this.geographicDescription = geographicDescription;
463  }
464
465  /** Taxonomic description of the collections maintained by an institution. */
466  public String getTaxonomicDescription() {
467    return taxonomicDescription;
468  }
469
470  public void setTaxonomicDescription(String taxonomicDescription) {
471    this.taxonomicDescription = taxonomicDescription;
472  }
473
474  /** Estimated number of specimens hosted by an institution. */
475  public Integer getNumberSpecimens() {
476    return numberSpecimens;
477  }
478
479  public void setNumberSpecimens(Integer numberSpecimens) {
480    this.numberSpecimens = numberSpecimens;
481  }
482
483  /** Was the institution record imported form Index Herbariorum. */
484  public boolean isIndexHerbariorumRecord() {
485    return indexHerbariorumRecord;
486  }
487
488  public void setIndexHerbariorumRecord(boolean indexHerbariorumRecord) {
489    this.indexHerbariorumRecord = indexHerbariorumRecord;
490  }
491
492  /** Logo/Image that identifies the institution. */
493  @HttpURI
494  @Nullable
495  public URI getLogoUrl() {
496    return logoUrl;
497  }
498
499  public void setLogoUrl(URI logoUrl) {
500    this.logoUrl = logoUrl;
501  }
502
503  /**
504   * CITES (see http://ec.europa.eu/environment/cites/info_permits_en.htm) licence given for this
505   * collection.
506   */
507  public String getCitesPermitNumber() {
508    return citesPermitNumber;
509  }
510
511  public void setCitesPermitNumber(String citesPermitNumber) {
512    this.citesPermitNumber = citesPermitNumber;
513  }
514
515  @Override
516  public String getCreatedBy() {
517    return createdBy;
518  }
519
520  @Override
521  public void setCreatedBy(String createdBy) {
522    this.createdBy = createdBy;
523  }
524
525  @Override
526  public String getModifiedBy() {
527    return modifiedBy;
528  }
529
530  @Override
531  public void setModifiedBy(String modifiedBy) {
532    this.modifiedBy = modifiedBy;
533  }
534
535  @Override
536  public Date getCreated() {
537    return created;
538  }
539
540  @Override
541  public void setCreated(Date created) {
542    this.created = created;
543  }
544
545  @Override
546  public Date getModified() {
547    return modified;
548  }
549
550  @Override
551  public void setModified(Date modified) {
552    this.modified = modified;
553  }
554
555  @Override
556  public Date getDeleted() {
557    return deleted;
558  }
559
560  @Override
561  public void setDeleted(Date deleted) {
562    this.deleted = deleted;
563  }
564
565  @Override
566  public List<Identifier> getIdentifiers() {
567    return identifiers;
568  }
569
570  @Override
571  public void setIdentifiers(List<Identifier> identifiers) {
572    this.identifiers = identifiers;
573  }
574
575  @Override
576  public List<Tag> getTags() {
577    return tags;
578  }
579
580  @Override
581  public void setTags(List<Tag> tags) {
582    this.tags = tags;
583  }
584
585  @Valid
586  @Override
587  public List<Contact> getContactPersons() {
588    return contactPersons;
589  }
590
591  @Override
592  public void setContactPersons(List<Contact> contactPersons) {
593    this.contactPersons = contactPersons;
594  }
595
596  @Override
597  public @NotNull List<MachineTag> getMachineTags() {
598    return machineTags;
599  }
600
601  @Override
602  public void setMachineTags(List<MachineTag> machineTags) {
603    this.machineTags = machineTags;
604  }
605
606  @Override
607  public void addMachineTag(MachineTag machineTag) {
608    this.machineTags.add(machineTag);
609  }
610
611  /** Alternative codes for an institution. */
612  @Override
613  public List<AlternativeCode> getAlternativeCodes() {
614    return alternativeCodes;
615  }
616
617  @Override
618  public void setAlternativeCodes(List<AlternativeCode> alternativeCodes) {
619    this.alternativeCodes = alternativeCodes;
620  }
621
622  @Override
623  public List<Comment> getComments() {
624    return comments;
625  }
626
627  @Override
628  public void setComments(List<Comment> comments) {
629    this.comments = comments;
630  }
631
632  @Override
633  public List<OccurrenceMapping> getOccurrenceMappings() {
634    return occurrenceMappings;
635  }
636
637  @Override
638  public void setOccurrenceMappings(List<OccurrenceMapping> occurrenceMappings) {
639    this.occurrenceMappings = occurrenceMappings;
640  }
641
642  @Override
643  public UUID getReplacedBy() {
644    return replacedBy;
645  }
646
647  @Override
648  public void setReplacedBy(UUID replacedBy) {
649    this.replacedBy = replacedBy;
650  }
651
652  public UUID getConvertedToCollection() {
653    return convertedToCollection;
654  }
655
656  public void setConvertedToCollection(UUID convertedToCollection) {
657    this.convertedToCollection = convertedToCollection;
658  }
659
660  @Override
661  public MasterSourceType getMasterSource() {
662    return masterSource;
663  }
664
665  @Override
666  public void setMasterSource(MasterSourceType masterSource) {
667    this.masterSource = masterSource;
668  }
669
670  @Override
671  public MasterSourceMetadata getMasterSourceMetadata() {
672    return masterSourceMetadata;
673  }
674
675  @Override
676  public void setMasterSourceMetadata(MasterSourceMetadata masterSourceMetadata) {
677    this.masterSourceMetadata = masterSourceMetadata;
678  }
679
680  @Override
681  public Boolean getDisplayOnNHCPortal() {
682    return displayOnNHCPortal;
683  }
684
685  @Override
686  public void setDisplayOnNHCPortal(Boolean displayOnNHCPortal) {
687    this.displayOnNHCPortal = displayOnNHCPortal;
688  }
689
690  public Integer getOccurrenceCount() {
691    return occurrenceCount;
692  }
693
694  public void setOccurrenceCount(Integer occurrenceCount) {
695    this.occurrenceCount = occurrenceCount;
696  }
697
698  public Integer getTypeSpecimenCount() {
699    return typeSpecimenCount;
700  }
701
702  public void setTypeSpecimenCount(Integer typeSpecimenCount) {
703    this.typeSpecimenCount = typeSpecimenCount;
704  }
705
706  @Hidden
707  @JsonIgnore
708  public String getCountry() {
709    if (address != null && address.getCountry() != null) {
710      return address.getCountry().getIso2LetterCode();
711    } else if (mailingAddress != null && mailingAddress.getCountry() != null) {
712      return mailingAddress.getCountry().getIso2LetterCode();
713    }
714    return null;
715  }
716
717  @Hidden
718  @JsonIgnore
719  public String getCity() {
720    if (address != null && address.getCity() != null) {
721      return address.getCity();
722    } else if (mailingAddress != null && mailingAddress.getCity() != null) {
723      return mailingAddress.getCity();
724    }
725    return null;
726  }
727
728  @Hidden
729  @JsonIgnore
730  public String getProvince() {
731    if (address != null && address.getProvince() != null) {
732      return address.getProvince();
733    } else if (mailingAddress != null && mailingAddress.getProvince() != null) {
734      return mailingAddress.getProvince();
735    }
736    return null;
737  }
738
739  @Override
740  public boolean lenientEquals(Institution other) {
741    if (this == other) {
742      return true;
743    }
744    return active == other.active
745        && indexHerbariorumRecord == other.indexHerbariorumRecord
746        && Objects.equals(key, other.key)
747        && Objects.equals(code, other.code)
748        && Objects.equals(name, other.name)
749        && Objects.equals(description, other.description)
750        && type == other.type
751        && Objects.equals(email, other.email)
752        && Objects.equals(phone, other.phone)
753        && Objects.equals(homepage, other.homepage)
754        && Objects.equals(catalogUrl, other.catalogUrl)
755        && Objects.equals(apiUrl, other.apiUrl)
756        && institutionalGovernance == other.institutionalGovernance
757        && Objects.equals(disciplines, other.disciplines)
758        && Objects.equals(latitude, other.latitude)
759        && Objects.equals(longitude, other.longitude)
760        && LenientEqualsUtils.lenientEquals(mailingAddress, other.mailingAddress)
761        && LenientEqualsUtils.lenientEquals(address, other.address)
762        && Objects.equals(additionalNames, other.additionalNames)
763        && Objects.equals(foundingDate, other.foundingDate)
764        && Objects.equals(geographicDescription, other.geographicDescription)
765        && Objects.equals(numberSpecimens, other.numberSpecimens)
766        && Objects.equals(taxonomicDescription, other.taxonomicDescription)
767        && Objects.equals(logoUrl, other.logoUrl)
768        && Objects.equals(citesPermitNumber, other.citesPermitNumber)
769        && Objects.equals(deleted, other.deleted)
770        && Objects.equals(alternativeCodes, other.alternativeCodes)
771        && Objects.equals(comments, other.comments)
772        && Objects.equals(occurrenceMappings, other.occurrenceMappings)
773        && Objects.equals(replacedBy, other.replacedBy)
774        && Objects.equals(convertedToCollection, other.convertedToCollection)
775        && Objects.equals(masterSource, other.masterSource)
776        && Objects.equals(masterSourceMetadata, other.masterSourceMetadata)
777        && Objects.equals(displayOnNHCPortal, other.displayOnNHCPortal);
778  }
779}