001/*
002 * Copyright 2020 Global Biodiversity Information Facility (GBIF)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.gbif.api.model.registry;
017
018import org.gbif.api.util.HttpURI;
019import org.gbif.api.vocabulary.Continent;
020import org.gbif.api.vocabulary.Country;
021import org.gbif.api.vocabulary.GbifRegion;
022import org.gbif.api.vocabulary.NodeType;
023import org.gbif.api.vocabulary.ParticipationStatus;
024
025import java.net.URI;
026import java.util.ArrayList;
027import java.util.Date;
028import java.util.List;
029import java.util.Objects;
030import java.util.StringJoiner;
031import java.util.UUID;
032
033import javax.annotation.Nullable;
034import javax.validation.constraints.NotNull;
035import javax.validation.constraints.Size;
036
037import io.swagger.v3.oas.annotations.ExternalDocumentation;
038import io.swagger.v3.oas.annotations.Hidden;
039import io.swagger.v3.oas.annotations.media.Schema;
040
041/**
042 * A GBIF participant node.
043 *
044 * Primarily managed in the GBIF Directory.
045 */
046@Schema(
047  description = "A participant node, a country and its nominated organization " +
048    "or an international organization which is a member of GBIF.\n\n" +
049    "Note that participant nodes are managed in an internal GBIF system," +
050    "so methods to create, update and delete participant nodes are generally " +
051    "used only to synchronize the two systems.",
052  externalDocs = @ExternalDocumentation(
053    description = "Further information on GBIF's governance structure",
054    url = "https://www.gbif.org/what-is-gbif"
055  )
056)
057@SuppressWarnings("unused")
058public class Node
059    implements NetworkEntity,
060    Contactable,
061    Taggable,
062    MachineTaggable,
063    Commentable,
064    Identifiable,
065    Endpointable,
066    LenientEquals<Node>,
067    Address {
068
069  @Schema(
070    description = "Unique GBIF key for the participant node.",
071    accessMode = Schema.AccessMode.READ_ONLY
072  )
073  private UUID key;
074
075  @Schema(
076    description = "The membership type of the participant node.",
077    accessMode = Schema.AccessMode.READ_ONLY
078  )
079  private NodeType type;
080
081  @Schema(
082    description = "The membership status of the participant node.",
083    accessMode = Schema.AccessMode.READ_ONLY
084  )
085  private ParticipationStatus participationStatus;
086
087  @Schema(
088    description = "The year the participant node first joined GBIF.",
089    minimum = "2001",
090    accessMode = Schema.AccessMode.READ_ONLY
091  )
092  private Integer participantSince;
093
094  @Schema(
095    description = "The date the participant node signed the GBIF Memorandum " +
096      "of Understanding.",
097    minimum = "2001-01-18",
098    accessMode = Schema.AccessMode.READ_ONLY
099  )
100  private Date dateSignedMOU;
101
102  @Schema(
103    description = "The political region, based on " +
104      "[IPBES regions](https://www.nature.com/articles/sdata20167/figures/1), " +
105      "to which this participant node belongs.",
106    accessMode = Schema.AccessMode.READ_ONLY
107  )
108  private GbifRegion gbifRegion;
109
110  @Schema(
111    description = "Not used"
112  )
113  @Hidden
114  private Continent continent;
115
116  @Schema(
117    description = "The name of the participant node.",
118    example = "U.S. Geological Survey",
119    accessMode = Schema.AccessMode.READ_ONLY
120  )
121  private String title;
122
123  @Schema(
124    description = "The name of the participant.",
125    example = "United States of America",
126    accessMode = Schema.AccessMode.READ_ONLY
127  )
128  private String participantTitle;
129
130  @Schema(
131    description = "The abbreviation for the participant node.",
132    example = "USGS",
133    accessMode = Schema.AccessMode.READ_ONLY
134  )
135  private String abbreviation;
136
137  @Schema(
138    description = "The description of the participant node.",
139    accessMode = Schema.AccessMode.READ_ONLY
140  )
141  private String description;
142
143  @Schema(
144    description = "Email addresses associated with this participant node.",
145    accessMode = Schema.AccessMode.READ_ONLY
146  )
147  private List<String> email = new ArrayList<>();
148
149  @Schema(
150    description = "Telephone numbers associated with this participant node.",
151    accessMode = Schema.AccessMode.READ_ONLY
152  )
153  private List<String> phone = new ArrayList<>();
154
155  @Schema(
156    description = "Homepages with further details on the participant node.",
157    accessMode = Schema.AccessMode.READ_ONLY
158  )
159  private List<URI> homepage = new ArrayList<>();
160
161  @Schema(
162    description = "A logo for the participant node, accessible over HTTP.",
163    accessMode = Schema.AccessMode.READ_ONLY
164  )
165  private URI logoUrl;
166
167  @Schema(
168    description = "Not used"
169  )
170  @Hidden
171  private String organization;
172
173  @Schema(
174    description = "Address lines other than the city, province, country and" +
175      "postal code, which have their own fields."
176  )
177  private List<String> address = new ArrayList<>();
178
179  @Schema(
180    description = "The city or similar line of the participant node's address."
181  )
182  private String city;
183
184  @Schema(
185    description = "The province or similar line of the participant node's address."
186  )
187  private String province;
188
189  @Schema(
190    description = "The country or other region of the participant node's address."
191  )
192  private Country country;
193
194  @Schema(
195    description = "The postal code or similar line of the participant node's address."
196  )
197  private String postalCode;
198
199  @Schema(
200    description = "The GBIF username of the creator of the participant node.",
201    accessMode = Schema.AccessMode.READ_ONLY
202  )
203  private String createdBy;
204
205  @Schema(
206    description = "The GBIF username of the last user to modify the participant node.",
207    accessMode = Schema.AccessMode.READ_ONLY
208  )
209  private String modifiedBy;
210
211  @Schema(
212    description = "Timestamp of when the participant node was created *in " +
213      "the Registry*.  See also, `participantSince`.",
214    accessMode = Schema.AccessMode.READ_ONLY
215  )
216  private Date created;
217
218  @Schema(
219    description = "Timestamp of when the participant node was modified.",
220    accessMode = Schema.AccessMode.READ_ONLY
221  )
222  private Date modified;
223
224  @Schema(
225    description = "If present, the participant node was deleted at this time. " +
226      "It is possible for it to be restored in the future.",
227    accessMode = Schema.AccessMode.READ_ONLY
228  )
229  private Date deleted;
230
231  @Schema(
232    description = "A list of contacts associated with this participant node.",
233    accessMode = Schema.AccessMode.READ_ONLY
234  )
235  private List<Contact> contacts = new ArrayList<>();
236
237  @Schema(
238    description = "A list of endpoints associated with this participant node.",
239    accessMode = Schema.AccessMode.READ_ONLY
240  )
241  private List<Endpoint> endpoints = new ArrayList<>();
242
243  @Schema(
244    description = "A list of machine tags associated with this participant node.",
245    accessMode = Schema.AccessMode.READ_ONLY
246  )
247  private List<MachineTag> machineTags = new ArrayList<>();
248
249  @Schema(
250    description = "A list of tags associated with this participant node.",
251    accessMode = Schema.AccessMode.READ_ONLY
252  )
253  private List<Tag> tags = new ArrayList<>();
254
255  @Schema(
256    description = "A list of identifiers associated with this participant node.",
257    accessMode = Schema.AccessMode.READ_ONLY
258  )
259  private List<Identifier> identifiers = new ArrayList<>();
260
261  @Schema(
262    description = "A list of comments associated with this participant node.",
263    accessMode = Schema.AccessMode.READ_ONLY
264  )
265  private List<Comment> comments = new ArrayList<>();
266
267  @Override
268  public UUID getKey() {
269    return key;
270  }
271
272  @Override
273  public void setKey(UUID key) {
274    this.key = key;
275  }
276
277  @Override
278  public String getTitle() {
279    return title;
280  }
281
282  @Override
283  public void setTitle(String title) {
284    this.title = title;
285  }
286
287  public String getParticipantTitle() {
288    return participantTitle;
289  }
290
291  public void setParticipantTitle(String participantTitle) {
292    this.participantTitle = participantTitle;
293  }
294
295  @Nullable
296  @Size(min = 1, max = 10)
297  public String getAbbreviation() {
298    return abbreviation;
299  }
300
301  public void setAbbreviation(String abbreviation) {
302    this.abbreviation = abbreviation;
303  }
304
305  @Override
306  public String getDescription() {
307    return description;
308  }
309
310  @Override
311  public void setDescription(String description) {
312    this.description = description;
313  }
314
315  @Override
316  public Date getCreated() {
317    return created;
318  }
319
320  @Override
321  public void setCreated(Date created) {
322    this.created = created;
323  }
324
325  @Override
326  public Date getModified() {
327    return modified;
328  }
329
330  @Override
331  public void setModified(Date modified) {
332    this.modified = modified;
333  }
334
335  @Override
336  public Date getDeleted() {
337    return deleted;
338  }
339
340  @Override
341  public void setDeleted(Date deleted) {
342    this.deleted = deleted;
343  }
344
345  @NotNull
346  public NodeType getType() {
347    return type;
348  }
349
350  public void setType(NodeType type) {
351    this.type = type;
352  }
353
354  @NotNull
355  public ParticipationStatus getParticipationStatus() {
356    return participationStatus;
357  }
358
359  public void setParticipationStatus(ParticipationStatus participationStatus) {
360    this.participationStatus = participationStatus;
361  }
362
363  /**
364   * 4 digit year since the node participant first joined GBIF.
365   */
366  @Nullable
367  public Integer getParticipantSince() {
368    return participantSince;
369  }
370
371  public void setParticipantSince(Integer participantSince) {
372    this.participantSince = participantSince;
373  }
374
375  public Date getDateSignedMOU() {
376    return dateSignedMOU;
377  }
378
379  public void setDateSignedMOU(Date dateSignedMOU) {
380    this.dateSignedMOU = dateSignedMOU;
381  }
382
383  @Nullable
384  public GbifRegion getGbifRegion() {
385    return gbifRegion;
386  }
387
388  public void setGbifRegion(GbifRegion gbifRegion) {
389    this.gbifRegion = gbifRegion;
390  }
391
392  @Nullable
393  public Continent getContinent() {
394    return continent;
395  }
396
397  public void setContinent(Continent continent) {
398    this.continent = continent;
399  }
400
401  @Nullable
402  @Override
403  public List<String> getEmail() {
404    return email;
405  }
406
407  @Override
408  public void setEmail(List<String> email) {
409    this.email = email;
410  }
411
412  @Nullable
413  @Override
414  public List<String> getPhone() {
415    return phone;
416  }
417
418  @Override
419  public void setPhone(List<String> phone) {
420    this.phone = phone;
421  }
422
423  @Nullable
424  @Override
425  public List<URI> getHomepage() {
426    return homepage;
427  }
428
429  @Override
430  public void setHomepage(List<URI> homepage) {
431    this.homepage = homepage;
432  }
433
434  @HttpURI
435  @Nullable
436  public URI getLogoUrl() {
437    return logoUrl;
438  }
439
440  public void setLogoUrl(URI logoUrl) {
441    this.logoUrl = logoUrl;
442  }
443
444  @Nullable
445  @Override
446  public String getOrganization() {
447    return organization;
448  }
449
450  @Override
451  public void setOrganization(String organization) {
452    this.organization = organization;
453  }
454
455  @Nullable
456  @Override
457  public List<String> getAddress() {
458    return address;
459  }
460
461  @Override
462  public void setAddress(List<String> address) {
463    this.address = address;
464  }
465
466  @Nullable
467  @Size(min = 1)
468  @Override
469  public String getCity() {
470    return city;
471  }
472
473  @Override
474  public void setCity(String city) {
475    this.city = city;
476  }
477
478  @Nullable
479  @Size(min = 1)
480  @Override
481  public String getProvince() {
482    return province;
483  }
484
485  @Override
486  public void setProvince(String province) {
487    this.province = province;
488  }
489
490  @Nullable
491  @Override
492  public Country getCountry() {
493    return country;
494  }
495
496  @Override
497  public void setCountry(Country country) {
498    this.country = country;
499  }
500
501  @Nullable
502  @Size(min = 1)
503  @Override
504  public String getPostalCode() {
505    return postalCode;
506  }
507
508  @Override
509  public void setPostalCode(String postalCode) {
510    this.postalCode = postalCode;
511  }
512
513  @Override
514  public String getCreatedBy() {
515    return createdBy;
516  }
517
518  @Override
519  public void setCreatedBy(String createdBy) {
520    this.createdBy = createdBy;
521  }
522
523  @Override
524  public String getModifiedBy() {
525    return modifiedBy;
526  }
527
528  @Override
529  public void setModifiedBy(String modifiedBy) {
530    this.modifiedBy = modifiedBy;
531  }
532
533  @Override
534  public List<Contact> getContacts() {
535    return contacts;
536  }
537
538  @Override
539  public void setContacts(List<Contact> contacts) {
540    this.contacts = contacts;
541  }
542
543  @Override
544  public List<Endpoint> getEndpoints() {
545    return endpoints;
546  }
547
548  @Override
549  public void setEndpoints(List<Endpoint> endpoints) {
550    this.endpoints = endpoints;
551  }
552
553  @Override
554  public void addEndpoint(Endpoint endpoint) {
555    endpoints.add(endpoint);
556  }
557
558  @Override
559  public List<MachineTag> getMachineTags() {
560    return machineTags;
561  }
562
563  @Override
564  public void setMachineTags(List<MachineTag> machineTags) {
565    this.machineTags = machineTags;
566  }
567
568  @Override
569  public void addMachineTag(MachineTag machineTag) {
570    machineTags.add(machineTag);
571  }
572
573  @Override
574  public List<Tag> getTags() {
575    return tags;
576  }
577
578  @Override
579  public void setTags(List<Tag> tags) {
580    this.tags = tags;
581  }
582
583  @Override
584  public List<Identifier> getIdentifiers() {
585    return identifiers;
586  }
587
588  @Override
589  public void setIdentifiers(List<Identifier> identifiers) {
590    this.identifiers = identifiers;
591  }
592
593  @Override
594  public List<Comment> getComments() {
595    return comments;
596  }
597
598  @Override
599  public void setComments(List<Comment> comments) {
600    this.comments = comments;
601  }
602
603  @Override
604  public boolean equals(Object o) {
605    if (this == o) {
606      return true;
607    }
608    if (o == null || getClass() != o.getClass()) {
609      return false;
610    }
611    Node node = (Node) o;
612    return Objects.equals(key, node.key)
613        && type == node.type
614        && participationStatus == node.participationStatus
615        && Objects.equals(participantSince, node.participantSince)
616        && Objects.equals(dateSignedMOU, node.dateSignedMOU)
617        && gbifRegion == node.gbifRegion
618        && continent == node.continent
619        && Objects.equals(title, node.title)
620        && Objects.equals(participantTitle, node.participantTitle)
621        && Objects.equals(abbreviation, node.abbreviation)
622        && Objects.equals(description, node.description)
623        && Objects.equals(email, node.email)
624        && Objects.equals(phone, node.phone)
625        && Objects.equals(homepage, node.homepage)
626        && Objects.equals(logoUrl, node.logoUrl)
627        && Objects.equals(organization, node.organization)
628        && Objects.equals(address, node.address)
629        && Objects.equals(city, node.city)
630        && Objects.equals(province, node.province)
631        && country == node.country
632        && Objects.equals(postalCode, node.postalCode)
633        && Objects.equals(createdBy, node.createdBy)
634        && Objects.equals(modifiedBy, node.modifiedBy)
635        && Objects.equals(created, node.created)
636        && Objects.equals(modified, node.modified)
637        && Objects.equals(deleted, node.deleted)
638        && Objects.equals(contacts, node.contacts)
639        && Objects.equals(endpoints, node.endpoints)
640        && Objects.equals(machineTags, node.machineTags)
641        && Objects.equals(tags, node.tags)
642        && Objects.equals(identifiers, node.identifiers)
643        && Objects.equals(comments, node.comments);
644  }
645
646  @Override
647  public int hashCode() {
648    return Objects.hash(
649        key,
650        type,
651        participationStatus,
652        participantSince,
653        dateSignedMOU,
654        gbifRegion,
655        continent,
656        title,
657        participantTitle,
658        abbreviation,
659        description,
660        email,
661        phone,
662        homepage,
663        logoUrl,
664        organization,
665        address,
666        city,
667        province,
668        country,
669        postalCode,
670        createdBy,
671        modifiedBy,
672        created,
673        modified,
674        deleted,
675        contacts,
676        endpoints,
677        machineTags,
678        tags,
679        identifiers,
680        comments);
681  }
682
683  @Override
684  public String toString() {
685    return new StringJoiner(", ", Node.class.getSimpleName() + "[", "]")
686        .add("key=" + key)
687        .add("type=" + type)
688        .add("participationStatus=" + participationStatus)
689        .add("participantSince=" + participantSince)
690        .add("dateSignedMOU=" + dateSignedMOU)
691        .add("gbifRegion=" + gbifRegion)
692        .add("continent=" + continent)
693        .add("title='" + title + "'")
694        .add("participantTitle='" + participantTitle + "'")
695        .add("abbreviation='" + abbreviation + "'")
696        .add("description='" + description + "'")
697        .add("email=" + email)
698        .add("phone=" + phone)
699        .add("homepage=" + homepage)
700        .add("logoUrl=" + logoUrl)
701        .add("organization='" + organization + "'")
702        .add("address=" + address)
703        .add("city='" + city + "'")
704        .add("province='" + province + "'")
705        .add("country=" + country)
706        .add("postalCode='" + postalCode + "'")
707        .add("createdBy='" + createdBy + "'")
708        .add("modifiedBy='" + modifiedBy + "'")
709        .add("created=" + created)
710        .add("modified=" + modified)
711        .add("deleted=" + deleted)
712        .add("contacts=" + contacts)
713        .add("endpoints=" + endpoints)
714        .add("machineTags=" + machineTags)
715        .add("tags=" + tags)
716        .add("identifiers=" + identifiers)
717        .add("comments=" + comments)
718        .toString();
719  }
720
721  /**
722   * Compares the entities for business logic equality using a very lenient approach.
723   * Only fields persisted in the registry database are used in the comparison, excluding any nested properties and
724   * supplementary information which can be added from external databases such as the IMS. Server controlled values such
725   * as key and createdBy are omitted.
726   */
727  @Override
728  public boolean lenientEquals(Node other) {
729    if (this == other) {
730      return true;
731    }
732    return Objects.equals(this.type, other.type)
733        && Objects.equals(this.participationStatus, other.participationStatus)
734        && Objects.equals(this.gbifRegion, other.gbifRegion)
735        && Objects.equals(this.continent, other.continent)
736        && Objects.equals(this.title, other.title)
737        && Objects.equals(this.participantTitle, other.participantTitle)
738        && Objects.equals(this.country, other.country);
739  }
740}