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; 017import io.swagger.v3.oas.annotations.Hidden; 018import io.swagger.v3.oas.annotations.media.Schema; 019import java.net.URI; 020import java.util.ArrayList; 021import java.util.Date; 022import java.util.List; 023import java.util.Objects; 024import java.util.UUID; 025import javax.annotation.Nullable; 026import javax.validation.Valid; 027import javax.validation.constraints.NotNull; 028import javax.validation.constraints.Size; 029import lombok.EqualsAndHashCode; 030import lombok.Getter; 031import lombok.Setter; 032import lombok.ToString; 033import org.gbif.api.model.common.DOI; 034import org.gbif.api.model.registry.Comment; 035import org.gbif.api.model.registry.Identifier; 036import org.gbif.api.model.registry.LenientEquals; 037import org.gbif.api.model.registry.MachineTag; 038import org.gbif.api.model.registry.PrePersist; 039import org.gbif.api.model.registry.Tag; 040import org.gbif.api.util.HttpURI; 041import org.gbif.api.util.LenientEqualsUtils; 042import org.gbif.api.util.validators.email.ValidEmail; 043import org.gbif.api.vocabulary.License; 044import org.gbif.api.vocabulary.collections.MasterSourceType; 045 046/** 047 * A group of specimens or other natural history objects. Types of collections can be: specimens, 048 * original artwork, archives, observations, library materials, datasets, photographs or mixed 049 * collections such as those that result from expeditions and voyages of discovery. 050 */ 051@SuppressWarnings("unused") 052@ToString 053@EqualsAndHashCode 054public class Collection implements CollectionEntity, LenientEquals<Collection> { 055 056 @Schema( 057 description = "Unique GBIF key for the collection.", 058 accessMode = Schema.AccessMode.READ_ONLY) 059 private UUID key; 060 061 @Schema( 062 description = 063 "Code of the collection — identifies a collection at the " 064 + "owner's location.\n\n" 065 + "*(NB Not required for updates.)*") 066 @Sourceable(masterSources = MasterSourceType.IH) 067 private String code; 068 069 @Schema( 070 description = "Descriptive name of the collection.\n\n" + "*(NB Not required for updates.)*") 071 @Sourceable(masterSources = MasterSourceType.GBIF_REGISTRY) 072 @Sourceable(masterSources = MasterSourceType.IH, overridable = true) 073 private String name; 074 075 @Schema(description = "Description or summary of the contents of the collection.") 076 @Sourceable(masterSources = MasterSourceType.GBIF_REGISTRY) 077 private String description; 078 079 /** 080 * Content type of the elements found in a collection. 081 */ 082 @Getter 083 @Setter 084 @Schema(description = "Content type of the elements found in the collection.") 085 private List<String> contentTypes = new ArrayList<>(); 086 087 @Schema(description = "Whether the collection is active/maintained.") 088 @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH}) 089 private boolean active; 090 091 /** 092 * Does this collections belong to an individual?. 093 */ 094 @Setter 095 @Getter 096 @Schema(description = "Whether this collection belongs to an individual.") 097 private boolean personalCollection; 098 099 /** 100 * Digital Object Identifier assigned to this collection. 101 */ 102 @Setter 103 @Getter 104 @Schema( 105 description = "A Digital Object Identifier for the collection.", 106 implementation = String.class, 107 pattern = "(10(?:\\.[0-9]+)+)" + "/(.+)") 108 private DOI doi; 109 110 @Schema(description = "Email addresses associated with the collection.") 111 @Sourceable(masterSources = MasterSourceType.IH) 112 private List<@ValidEmail String> email = new ArrayList<>(); 113 114 @Schema(description = "Telephone numbers associated with the collection.") 115 @Sourceable(masterSources = MasterSourceType.IH) 116 private List<String> phone = new ArrayList<>(); 117 118 @Setter 119 @Schema(description = "The collection's WWW homepage.") 120 @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH}) 121 private URI homepage; 122 123 @Setter 124 @Schema(description = "URLs for interactive catalogues of the collection.") 125 private List<@HttpURI URI> catalogUrls = new ArrayList<>(); 126 127 @Setter 128 @Schema(description = "URLs for machine-readable APIs for the collection catalogue.") 129 private List<@HttpURI URI> apiUrls = new ArrayList<>(); 130 131 /** 132 * Types of preservation mechanisms used for these collections. 133 */ 134 @Setter 135 @Getter 136 @Schema(description = "The preservation mechanisms used for this collection.") 137 @Sourceable(masterSources = MasterSourceType.GBIF_REGISTRY) 138 private List<String> preservationTypes = new ArrayList<>(); 139 140 /** 141 * Defines how a collection as been added or joined. 142 */ 143 @Setter 144 @Getter 145 @Schema(description = "How the collection was added or joined.") 146 private String accessionStatus; 147 148 /** 149 * Institution that owns or hosts this collection. 150 */ 151 @Setter 152 @Getter 153 @Schema(description = "The key of the institution owning or hosting the collection.") 154 private UUID institutionKey; 155 156 @Schema(description = "The postal address of the collection.") 157 @Sourceable(masterSources = MasterSourceType.IH) 158 private Address mailingAddress; 159 160 @Schema(description = "The address of the location of the collection.") 161 @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH}) 162 private Address address; 163 164 @Schema( 165 description = 166 "The GBIF username of the creator of the collection entity in " + "the GBIF registry.", 167 accessMode = Schema.AccessMode.READ_ONLY) 168 private String createdBy; 169 170 @Schema( 171 description = 172 "The GBIF username of the last user to modify the collection " 173 + "entity in the GBIF registry.", 174 accessMode = Schema.AccessMode.READ_ONLY) 175 private String modifiedBy; 176 177 @Schema( 178 description = 179 "Timestamp of when the collection entity was created in the GBIF " + "registry.", 180 accessMode = Schema.AccessMode.READ_ONLY) 181 private Date created; 182 183 @Schema( 184 description = 185 "Timestamp of when the collection entity was modified in the GBIF " + "registry.", 186 accessMode = Schema.AccessMode.READ_ONLY) 187 private Date modified; 188 189 @Schema( 190 description = 191 "If present, the collection was deleted at this time. " 192 + "It is possible for it to be restored in the future.", 193 accessMode = Schema.AccessMode.READ_ONLY) 194 private Date deleted; 195 196 @Schema( 197 description = "A list of tags associated with this collection.", 198 accessMode = Schema.AccessMode.READ_ONLY) 199 private List<Tag> tags = new ArrayList<>(); 200 201 @Schema( 202 description = "A list of identifiers associated with this collection.", 203 accessMode = Schema.AccessMode.READ_ONLY) 204 @Sourceable(masterSources = MasterSourceType.IH, sourceableParts = "IH_IRN") 205 @Sourceable(masterSources = MasterSourceType.GBIF_REGISTRY, sourceableParts = "DOI") 206 private List<Identifier> identifiers = new ArrayList<>(); 207 208 @Schema( 209 description = "A list of contact people for this collection.", 210 accessMode = Schema.AccessMode.READ_ONLY) 211 @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH}) 212 private List<Contact> contactPersons = new ArrayList<>(); 213 214 @Setter 215 @Getter 216 @Schema(description = "The number of specimens contained in this collection.") 217 @Sourceable(masterSources = MasterSourceType.IH) 218 private Integer numberSpecimens; 219 220 @Schema( 221 description = "A list of machine tags associated with this collection.", 222 accessMode = Schema.AccessMode.READ_ONLY) 223 private List<MachineTag> machineTags = new ArrayList<>(); 224 225 @Setter 226 @Getter 227 @Schema(description = "The taxonomic coverage of this collection.") 228 @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH}) 229 private String taxonomicCoverage; 230 231 @Setter 232 @Getter 233 @Schema(description = "The geographic coverage of this collection.") 234 @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH}) 235 private String geographicCoverage; 236 237 @Setter 238 @Getter 239 @Schema(description = "Notes on the collection.") 240 @Sourceable(masterSources = MasterSourceType.IH) 241 private String notes; 242 243 @Setter 244 @Getter 245 @Schema(description = "Other collections incorporated into this collection.") 246 @Sourceable(masterSources = {MasterSourceType.GBIF_REGISTRY, MasterSourceType.IH}) 247 private List<String> incorporatedCollections = new ArrayList<>(); 248 249 @Schema(description = "Alternative codes for this collection.") 250 private List<AlternativeCode> alternativeCodes = new ArrayList<>(); 251 252 @Schema( 253 description = "A list of comments associated with this collection.", 254 accessMode = Schema.AccessMode.READ_ONLY) 255 private List<Comment> comments = new ArrayList<>(); 256 257 @Schema( 258 description = "Mapping of a GRSciColl collection to occurrence records", 259 accessMode = Schema.AccessMode.READ_ONLY) 260 private List<OccurrenceMapping> occurrenceMappings = new ArrayList<>(); 261 262 @Schema(description = "A collection record that replaces this collection.") 263 private UUID replacedBy; 264 265 @Schema(description = "The primary source of this collection record.") 266 private MasterSourceType masterSource; 267 268 @Schema( 269 description = 270 "Information to assist the synchronization of the master source " 271 + "record with the record in the GBIF registry.") 272 private MasterSourceMetadata masterSourceMetadata; 273 274 @Setter 275 @Getter 276 @Schema(description = "An organizational department managing the collection.") 277 @Sourceable(masterSources = MasterSourceType.IH) 278 private String department; 279 280 @Setter 281 @Getter 282 @Schema(description = "An organizational division managing the collection.") 283 @Sourceable(masterSources = MasterSourceType.IH) 284 private String division; 285 286 @Schema(description = "Whether the collection is shown on the NHC portal.") 287 private Boolean displayOnNHCPortal; 288 289 @Setter 290 @Getter 291 @Schema(description = "An estimate of the number of occurrences linked to the institution.") 292 private Integer occurrenceCount; 293 294 @Setter 295 @Getter 296 @Schema(description = "An estimate of the number of type specimens linked to the institution.") 297 private Integer typeSpecimenCount; 298 299 @Schema( 300 description = 301 "URI to the image to be featured on the collection page, this image should be associated with a license.") 302 private URI featuredImageUrl; 303 304 @Schema( 305 description = "The license associated with the image to be featured on the collection page.") 306 private License featuredImageLicense; 307 308 @Setter 309 @Schema( 310 description = 311 "Temporal scope or focus of the collection. This free text field can be used to describe both the collection " 312 + "date ranges as well as the geological time group(s) of the collection objects in the context of " 313 + "paleontological collections.") 314 @Sourceable(masterSources = MasterSourceType.GBIF_REGISTRY) 315 private String temporalCoverage; 316 317 @Schema( 318 description = " Information about ownership, attribution, etc. of the featured image. This value with " 319 + "be used to generate a suggested citation of the image." 320 ) 321 private String featuredImageAttribution; 322 323 /** List of alternative identifiers: UUIDs, external system identifiers, LSIDs, etc.. */ 324 @Override 325 public List<Identifier> getIdentifiers() { 326 return identifiers; 327 } 328 329 @Override 330 public void setIdentifiers(List<Identifier> identifiers) { 331 this.identifiers = identifiers; 332 } 333 334 /** (Meta)Tags or labels. */ 335 @Valid 336 @Override 337 public List<Tag> getTags() { 338 return tags; 339 } 340 341 @Override 342 public void setTags(List<Tag> tags) { 343 this.tags = tags; 344 } 345 346 /** GBIF Unique identifier of this collection. */ 347 @Override 348 public UUID getKey() { 349 return key; 350 } 351 352 @Override 353 public void setKey(UUID key) { 354 this.key = key; 355 } 356 357 /** Collection code: identifies a collection at the owner's location. */ 358 @NotNull(groups = PrePersist.class) 359 @Override 360 public String getCode() { 361 return code; 362 } 363 364 @Override 365 public void setCode(String code) { 366 this.code = code; 367 } 368 369 /** Descriptive name of a collection. */ 370 @NotNull 371 @Override 372 public String getName() { 373 return name; 374 } 375 376 @Override 377 public void setName(String name) { 378 this.name = name; 379 } 380 381 /** Textual description/summary of the contents of a collection. */ 382 @Size(min = 1) 383 @Override 384 public String getDescription() { 385 return description; 386 } 387 388 @Override 389 public void setDescription(String description) { 390 this.description = description; 391 } 392 393 /** Is this collection currently active/maintained. */ 394 @Override 395 public boolean isActive() { 396 return active; 397 } 398 399 @Override 400 public void setActive(boolean active) { 401 this.active = active; 402 } 403 404 @Override 405 public List<String> getEmail() { 406 return email; 407 } 408 409 @Override 410 public void setEmail(List<String> email) { 411 this.email = email; 412 } 413 414 @Override 415 public List<String> getPhone() { 416 return phone; 417 } 418 419 @Override 420 public void setPhone(List<String> phone) { 421 this.phone = phone; 422 } 423 424 /** URL containing information about a collection. */ 425 @HttpURI 426 @Nullable 427 public URI getHomepage() { 428 return homepage; 429 } 430 431 /** URI that exposes data about the catalog associated to a collection. */ 432 @Nullable 433 public List<URI> getCatalogUrls() { 434 return catalogUrls; 435 } 436 437 /** Machine consumable endpoint of information about a collection. */ 438 @Nullable 439 public List<URI> getApiUrls() { 440 return apiUrls; 441 } 442 443 /** Address used to send/receive physical mail. */ 444 @Nullable 445 @Valid 446 @Override 447 public Address getMailingAddress() { 448 return mailingAddress; 449 } 450 451 @Override 452 public void setMailingAddress(Address mailingAddress) { 453 this.mailingAddress = mailingAddress; 454 } 455 456 /** Address where this collection is situated. */ 457 @Nullable 458 @Valid 459 @Override 460 public Address getAddress() { 461 return address; 462 } 463 464 @Override 465 public void setAddress(Address address) { 466 this.address = address; 467 } 468 469 @Override 470 public String getCreatedBy() { 471 return createdBy; 472 } 473 474 @Override 475 public void setCreatedBy(String createdBy) { 476 this.createdBy = createdBy; 477 } 478 479 @Override 480 public String getModifiedBy() { 481 return modifiedBy; 482 } 483 484 @Override 485 public void setModifiedBy(String modifiedBy) { 486 this.modifiedBy = modifiedBy; 487 } 488 489 @Override 490 public Date getCreated() { 491 return created; 492 } 493 494 @Override 495 public void setCreated(Date created) { 496 this.created = created; 497 } 498 499 @Override 500 public Date getModified() { 501 return modified; 502 } 503 504 @Override 505 public void setModified(Date modified) { 506 this.modified = modified; 507 } 508 509 @Override 510 public Date getDeleted() { 511 return deleted; 512 } 513 514 @Override 515 public void setDeleted(Date deleted) { 516 this.deleted = deleted; 517 } 518 519 @Valid 520 @Override 521 public List<Contact> getContactPersons() { 522 return contactPersons; 523 } 524 525 @Override 526 public void setContactPersons(List<Contact> contactPersons) { 527 this.contactPersons = contactPersons; 528 } 529 530 @Valid 531 @Override 532 public @NotNull List<MachineTag> getMachineTags() { 533 return machineTags; 534 } 535 536 @Override 537 public void setMachineTags(List<MachineTag> machineTags) { 538 this.machineTags = machineTags; 539 } 540 541 @Override 542 public void addMachineTag(MachineTag machineTag) { 543 machineTags.add(machineTag); 544 } 545 546 /** Alternative codes for a collection. */ 547 @Override 548 public List<AlternativeCode> getAlternativeCodes() { 549 return alternativeCodes; 550 } 551 552 @Override 553 public void setAlternativeCodes(List<AlternativeCode> alternativeCodes) { 554 this.alternativeCodes = alternativeCodes; 555 } 556 557 @Override 558 public @NotNull List<Comment> getComments() { 559 return comments; 560 } 561 562 @Override 563 public void setComments(List<Comment> comments) { 564 this.comments = comments; 565 } 566 567 @Override 568 public @NotNull List<OccurrenceMapping> getOccurrenceMappings() { 569 return occurrenceMappings; 570 } 571 572 @Override 573 public void setOccurrenceMappings(List<OccurrenceMapping> occurrenceMappings) { 574 this.occurrenceMappings = occurrenceMappings; 575 } 576 577 @Override 578 public UUID getReplacedBy() { 579 return replacedBy; 580 } 581 582 @Override 583 public void setReplacedBy(UUID replacedBy) { 584 this.replacedBy = replacedBy; 585 } 586 587 @Override 588 public MasterSourceType getMasterSource() { 589 return masterSource; 590 } 591 592 @Override 593 public void setMasterSource(MasterSourceType masterSource) { 594 this.masterSource = masterSource; 595 } 596 597 @Override 598 public MasterSourceMetadata getMasterSourceMetadata() { 599 return masterSourceMetadata; 600 } 601 602 @Override 603 public void setMasterSourceMetadata(MasterSourceMetadata masterSourceMetadata) { 604 this.masterSourceMetadata = masterSourceMetadata; 605 } 606 607 @Override 608 public Boolean getDisplayOnNHCPortal() { 609 return displayOnNHCPortal; 610 } 611 612 @Override 613 public void setDisplayOnNHCPortal(Boolean displayOnNHCPortal) { 614 this.displayOnNHCPortal = displayOnNHCPortal; 615 } 616 617 @HttpURI 618 @Nullable 619 @Override 620 public URI getFeaturedImageUrl() { 621 return featuredImageUrl; 622 } 623 624 @Override 625 public void setFeaturedImageUrl(URI featuredImageUrl) { 626 this.featuredImageUrl = featuredImageUrl; 627 } 628 629 @Nullable 630 @Override 631 public License getFeaturedImageLicense() { 632 return featuredImageLicense; 633 } 634 635 @Override 636 public void setFeaturedImageLicense(License featuredImageLicense) { 637 this.featuredImageLicense = featuredImageLicense; 638 } 639 640 @Nullable 641 public String getTemporalCoverage() { 642 return temporalCoverage; 643 } 644 645 @Hidden 646 @JsonIgnore 647 public String getCountry() { 648 if (address != null && address.getCountry() != null) { 649 return address.getCountry().getIso2LetterCode(); 650 } else if (mailingAddress != null && mailingAddress.getCountry() != null) { 651 return mailingAddress.getCountry().getIso2LetterCode(); 652 } 653 return null; 654 } 655 656 @Hidden 657 @JsonIgnore 658 public String getCity() { 659 if (address != null && address.getCity() != null) { 660 return address.getCity(); 661 } else if (mailingAddress != null && mailingAddress.getCity() != null) { 662 return mailingAddress.getCity(); 663 } 664 return null; 665 } 666 667 @Hidden 668 @JsonIgnore 669 public String getProvince() { 670 if (address != null && address.getProvince() != null) { 671 return address.getProvince(); 672 } else if (mailingAddress != null && mailingAddress.getProvince() != null) { 673 return mailingAddress.getProvince(); 674 } 675 return null; 676 } 677 678 @Nullable 679 @Override 680 public String getFeaturedImageAttribution() { 681 return featuredImageAttribution; 682 } 683 684 @Override 685 public void setFeaturedImageAttribution(String featuredImageAttribution) { 686 this.featuredImageAttribution = featuredImageAttribution; 687 } 688 689 @Override 690 public boolean lenientEquals(Collection other) { 691 if (this == other) { 692 return true; 693 } 694 return active == other.active 695 && personalCollection == other.personalCollection 696 && Objects.equals(key, other.key) 697 && Objects.equals(code, other.code) 698 && Objects.equals(name, other.name) 699 && Objects.equals(description, other.description) 700 && Objects.equals(contentTypes, other.contentTypes) 701 && Objects.equals(doi, other.doi) 702 && Objects.equals(email, other.email) 703 && Objects.equals(phone, other.phone) 704 && Objects.equals(homepage, other.homepage) 705 && Objects.equals(catalogUrls, other.catalogUrls) 706 && Objects.equals(apiUrls, other.apiUrls) 707 && Objects.equals(preservationTypes, other.preservationTypes) 708 && Objects.equals(accessionStatus, other.accessionStatus) 709 && Objects.equals(institutionKey, other.institutionKey) 710 && LenientEqualsUtils.lenientEquals(mailingAddress, other.mailingAddress) 711 && LenientEqualsUtils.lenientEquals(address, other.address) 712 && Objects.equals(deleted, other.deleted) 713 && Objects.equals(numberSpecimens, other.numberSpecimens) 714 && Objects.equals(taxonomicCoverage, other.taxonomicCoverage) 715 && Objects.equals(geographicCoverage, other.geographicCoverage) 716 && Objects.equals(notes, other.notes) 717 && Objects.equals(incorporatedCollections, other.incorporatedCollections) 718 && Objects.equals(alternativeCodes, other.alternativeCodes) 719 && Objects.equals(comments, other.comments) 720 && Objects.equals(occurrenceMappings, other.occurrenceMappings) 721 && Objects.equals(replacedBy, other.replacedBy) 722 && Objects.equals(masterSource, other.masterSource) 723 && Objects.equals(masterSourceMetadata, other.masterSourceMetadata) 724 && Objects.equals(division, other.division) 725 && Objects.equals(department, other.department) 726 && Objects.equals(displayOnNHCPortal, other.displayOnNHCPortal) 727 && Objects.equals(featuredImageUrl, other.featuredImageUrl) 728 && Objects.equals(featuredImageLicense, other.featuredImageLicense) 729 && Objects.equals(temporalCoverage, other.temporalCoverage) 730 && Objects.equals(featuredImageAttribution, other.featuredImageAttribution); 731 } 732}