TaxonomySpecies.java

  1. /*
  2.  * Copyright 2019 Global Crop Diversity Trust
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *   http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. package org.gringlobal.model;

  17. import java.time.Instant;
  18. import java.util.List;
  19. import java.util.Objects;

  20. import javax.persistence.*;

  21. import lombok.Getter;
  22. import lombok.NoArgsConstructor;
  23. import lombok.Setter;
  24. import org.apache.commons.lang3.StringUtils;
  25. import org.genesys.blocks.model.Copyable;
  26. import org.gringlobal.compatibility.LookupDisplay;
  27. import org.gringlobal.component.elastic.ElasticLoader;
  28. import org.gringlobal.custom.elasticsearch.SearchField;
  29. import org.gringlobal.custom.validation.javax.CodeValueField;
  30. import org.springframework.data.elasticsearch.annotations.Document;
  31. import org.springframework.data.elasticsearch.annotations.Field;
  32. import org.springframework.data.elasticsearch.annotations.FieldType;

  33. import com.fasterxml.jackson.annotation.JsonIdentityInfo;
  34. import com.fasterxml.jackson.annotation.JsonIdentityReference;
  35. import com.fasterxml.jackson.annotation.JsonIgnore;
  36. import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  37. import com.fasterxml.jackson.annotation.JsonProperty;
  38. import com.fasterxml.jackson.annotation.ObjectIdGenerators;

  39. import static org.gringlobal.model.community.CommunityCodeValues.CODE_VALUE_LENGTH;

  40. /**
  41.  * Auto-generated by:
  42.  * org.apache.openjpa.jdbc.meta.ReverseMappingTool$AnnotatedCodeGenerator
  43.  */
  44. @Entity
  45. @Cacheable
  46. @Table(name = "taxonomy_species")
  47. @Document(indexName = "taxonomyspecies")
  48. @JsonIdentityInfo(scope = TaxonomySpecies.class, generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
  49. @Getter
  50. @Setter
  51. @NoArgsConstructor
  52. @LookupDisplay(template = "case when exists (select id from TaxonomySpecies ts2 where ts2.name = t.name and ts2.id != t.id) then concat(t.name, concat(' ', coalesce(t.nameAuthority, ''))) else t.name end") // JPQL root uses alias 't'
  53. public class TaxonomySpecies extends CooperatorOwnedModel implements Copyable<TaxonomySpecies>, ElasticLoader {

  54.     private static final long serialVersionUID = -5958397094175847499L;

  55.     public static final String PREFIX_SUBSPECIES = "subsp. ";
  56.     public static final String PREFIX_NOTHOSUBSPECIES = "nothosubsp. ";
  57.     public static final String PREFIX_VARIETY = "var. ";
  58.     public static final String PREFIX_SUBVARIETY = "subvar. ";
  59.     public static final String PREFIX_FORMA = "f.";

  60.     @Id
  61.     @JsonProperty
  62.     @GeneratedValue(strategy = GenerationType.IDENTITY)
  63.     @Column(name = "taxonomy_species_id", columnDefinition = "int")
  64.     private Long id;

  65.     @Column(name = "grin_species_id")
  66.     private Long grinId;

  67.     @Basic
  68.     @Column(name = "alternate_name", length = 2000)
  69.     private String alternateName;

  70.     @Basic
  71.     @Column(name = "common_fertilization_code", length = CODE_VALUE_LENGTH)
  72.     @CodeValueField(value = "TAXONOMY_FERTILIZATION_METHOD", strict = false)
  73.     private String commonFertilizationCode;

  74.     @ManyToOne(fetch = FetchType.LAZY, cascade = {})
  75.     @JoinColumn(name = "curator1_cooperator_id")
  76.     @Field(type = FieldType.Object)
  77.     @JsonIgnoreProperties({ "ownedBy", "createdBy", "modifiedBy", "note", "geography", "secondaryGeography", "webCooperator" })
  78.     @JsonIdentityReference(alwaysAsId = false)
  79.     private Cooperator curator1Cooperator;

  80.     @ManyToOne(fetch = FetchType.LAZY, cascade = {})
  81.     @JoinColumn(name = "curator2_cooperator_id")
  82.     @Field(type = FieldType.Object)
  83.     @JsonIgnoreProperties({ "ownedBy", "createdBy", "modifiedBy", "note", "geography", "secondaryGeography", "webCooperator" })
  84.     @JsonIdentityReference(alwaysAsId = false)
  85.     private Cooperator curator2Cooperator;

  86.     @ManyToOne(fetch = FetchType.LAZY, cascade = {})
  87.     @JoinColumn(name = "current_taxonomy_species_id")
  88.     @JsonIdentityInfo(scope = TaxonomySpecies.class, generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
  89.     @JsonIdentityReference(alwaysAsId = true)
  90.     @Field(index = false, type = FieldType.Long)
  91.     private TaxonomySpecies currentTaxonomySpecies;

  92.     @Basic
  93.     @Column(name = "forma_authority", length = 100)
  94.     private String formaAuthority;

  95.     @Basic
  96.     @Column(name = "forma_name", length = 30)
  97.     private String formaName;

  98.     @Basic
  99.     @Column(name = "forma_rank_type", length = 30)
  100.     private String formaRankType;

  101.     @Basic
  102.     @Column(name = "is_forma_hybrid", nullable = false, length = 1)
  103.     private String isFormaHybrid = "N";

  104.     @Basic
  105.     @Column(name = "is_name_pending", nullable = false, length = 1)
  106.     private String isNamePending = "N";

  107.     @Basic
  108.     @Column(name = "is_specific_hybrid", nullable = false, length = 1)
  109.     private String isSpecificHybrid = "N";

  110.     @Basic
  111.     @Column(name = "is_subspecific_hybrid", nullable = false, length = 1)
  112.     private String isSubspecificHybrid = "N";

  113.     @Basic
  114.     @Column(name = "is_subvarietal_hybrid", nullable = false, length = 1)
  115.     private String isSubvarietalHybrid = "N";

  116.     @Basic
  117.     @Column(name = "is_varietal_hybrid", nullable = false, length = 1)
  118.     private String isVarietalHybrid = "N";

  119.     @Basic
  120.     @Column(name = "life_form_code", length = CODE_VALUE_LENGTH)
  121.     @CodeValueField(value = "ACCESSION_LIFE_FORM", strict = false)
  122.     private String lifeFormCode;

  123.     @Basic
  124.     @Column(nullable = false, length = 100)
  125.     @SearchField
  126.     private String name;

  127.     @Basic
  128.     @Column(name = "name_authority", length = 100)
  129.     private String nameAuthority;

  130.     @Basic
  131.     @Column(name = "name_verified_date")
  132.     private Instant nameVerifiedDate;

  133.     @Basic
  134.     @Column(name = "nomen_number")
  135.     private Integer nomenNumber;

  136.     @Basic
  137.     @Column
  138.     @Lob
  139.     private String note;

  140.     @ManyToOne(fetch = FetchType.LAZY, cascade = {})
  141.     @JoinColumn(name = "priority1_site_id")
  142.     @Field(type = FieldType.Object)
  143.     @JsonIgnoreProperties({ "ownedBy", "createdBy", "modifiedBy" })
  144.     private Site priority1Site;

  145.     @ManyToOne(fetch = FetchType.LAZY, cascade = {})
  146.     @JoinColumn(name = "priority2_site_id")
  147.     @Field(type = FieldType.Object)
  148.     @JsonIgnoreProperties({ "ownedBy", "createdBy", "modifiedBy" })
  149.     private Site priority2Site;

  150.     @Basic
  151.     @Column(length = 500)
  152.     private String protologue;

  153.     @Basic
  154.     @Column(name = "protologue_virtual_path")
  155.     private String protologueVirtualPath;

  156.     @Basic
  157.     @Column(name = "restriction_code", length = CODE_VALUE_LENGTH)
  158.     @CodeValueField(value = "TAXONOMY_RESTRICTION", strict = false)
  159.     private String restrictionCode;

  160.     @Basic
  161.     @Column(name = "site_note")
  162.     @Lob
  163.     private String siteNote;

  164.     @Basic
  165.     @Column(name = "species_authority", length = 100)
  166.     private String speciesAuthority;

  167.     @Basic
  168.     @Column(name = "species_name", nullable = false, length = 30)
  169.     private String speciesName;

  170.     @Basic
  171.     @Column(name = "subspecies_authority", length = 100)
  172.     private String subspeciesAuthority;

  173.     @Basic
  174.     @Column(name = "subspecies_name", length = 30)
  175.     private String subspeciesName;

  176.     @Basic
  177.     @Column(name = "subvariety_authority", length = 100)
  178.     private String subvarietyAuthority;

  179.     @Basic
  180.     @Column(name = "subvariety_name", length = 30)
  181.     private String subvarietyName;

  182.     @Basic
  183.     @Column(name = "synonym_code", length = CODE_VALUE_LENGTH)
  184.     @CodeValueField(value = "TAXONOMY_SPECIES_QUALIFIER", strict = false)
  185.     private String synonymCode;

  186.     @ManyToOne(fetch = FetchType.EAGER, cascade = {})
  187.     @JoinColumn(name = "taxonomy_genus_id", nullable = false)
  188.     @Field(type = FieldType.Object)
  189.     @JsonIgnoreProperties({ "ownedBy" })
  190.     private TaxonomyGenus taxonomyGenus;

  191.     @Basic
  192.     @Column(name = "variety_authority", length = 100)
  193.     private String varietyAuthority;

  194.     @Basic
  195.     @Column(name = "variety_name", length = 30)
  196.     private String varietyName;

  197.     @ManyToOne(fetch = FetchType.LAZY, cascade = {})
  198.     @JoinColumn(name = "verifier_cooperator_id")
  199.     @Field(type = FieldType.Object)
  200.     private Cooperator verifierCooperator;

  201.     @OneToMany(fetch = FetchType.LAZY, cascade = {}, mappedBy = "taxonomySpecies")
  202.     @JsonIgnore
  203.     private List<TaxonomyCropMap> taxonomyCrops;

  204.     @Basic
  205.     @Column(name = "hybrid_parentage", length = 500)
  206.     private String hybridParentage;

  207.     @Basic
  208.     @Column(name = "is_web_visible", nullable = false, length = 1)
  209.     private String isWebVisible = "N";

  210.     @PrePersist
  211.     @PreUpdate
  212.     private void updateName() {
  213.         if (StringUtils.isBlank(this.name)) { // Update name if not specified
  214.             this.name = this.taxonomyGenus.getName();
  215.             this.name += (Objects.equals(this.isSpecificHybrid, "Y") ? " x " : " ") + this.speciesName;

  216.             var subTaxon = getSubTaxon();
  217.             if (subTaxon != null) {
  218.                 this.name += " " + subTaxon;
  219.             }

  220.             this.name = this.name.strip().replaceAll("\\s+", " "); // strip and replace whitespaces with single whitespace
  221.         }

  222.         if (StringUtils.isBlank(this.nameAuthority)) { // Update nameAuthority if not specified
  223.             this.nameAuthority = this.speciesAuthority;

  224.             if (StringUtils.isNotBlank(this.subspeciesName)) {
  225.                 this.nameAuthority = this.subspeciesAuthority;
  226.             }
  227.             if (StringUtils.isNotBlank(this.varietyName)) {
  228.                 this.nameAuthority = this.varietyAuthority;
  229.             }
  230.             if (StringUtils.isNotBlank(this.subvarietyName)) {
  231.                 this.nameAuthority = this.subvarietyAuthority;
  232.             }
  233.             if (StringUtils.isNotBlank(this.formaName)) {
  234.                 this.nameAuthority = this.formaAuthority;
  235.             }
  236.         }
  237.     }

  238.     @Transient
  239.     @JsonIgnore
  240.     public String getSubTaxon() {
  241.         StringBuilder sb = new StringBuilder(50);
  242.         if (StringUtils.isNotBlank(this.subspeciesName)) {
  243.             if ("Y".equals(isSubspecificHybrid)) {
  244.                 sb.append(" ").append(PREFIX_NOTHOSUBSPECIES).append(subspeciesName);
  245.             } else {
  246.                 sb.append(" ").append(PREFIX_SUBSPECIES).append(this.subspeciesName);
  247.             }
  248.         }

  249.         if (StringUtils.isNotBlank(this.varietyName)) {
  250.             sb.append(" ").append(PREFIX_VARIETY).append(this.varietyName);
  251.         }

  252.         if (StringUtils.isNotBlank(this.subvarietyName)) {
  253.             sb.append(" ").append(PREFIX_SUBVARIETY).append(this.subvarietyName);
  254.         }

  255.         if (StringUtils.isNotBlank(this.formaName)) {
  256.             sb.append(" ").append(this.formaRankType).append(" ").append(this.formaName);
  257.         }

  258.         return StringUtils.trimToNull(sb.toString());
  259.     }

  260.     /**
  261.      * Get specific epithet including × for hybrids. Example: "×acuminata"
  262.      *
  263.      * @return
  264.      */
  265.     @Transient
  266.     public String getSpecificEpithet() {
  267.         return (Objects.equals(this.isSpecificHybrid, "Y") ? "×" : "") + this.speciesName;
  268.     }

  269.     public TaxonomySpecies(final Long id) {
  270.         this.id = id;
  271.     }

  272.     /**
  273.      * Find and return the {@code name} of the first {@link Crop} this species is linked
  274.      * with.
  275.      *
  276.      * @return first {@code crop.name} or {@code null} if not linked to crop.
  277.      */
  278.     @JsonIgnore
  279.     public String reportCropName() {
  280.         if (this.taxonomyCrops != null) {
  281.             if (this.taxonomyCrops.size() > 0) {
  282.                 var first = this.taxonomyCrops.get(0);
  283.                 if (first.getCrop() != null) {
  284.                     return first.getCrop().getName(); // not null
  285.                 }
  286.             }
  287.         }
  288.         return null;
  289.     }

  290.     @Override
  291.     public void lazyLoad() {
  292.         super.lazyLoad();
  293.         lazyLoad(this.getTaxonomyGenus());
  294.         lazyLoad(this.getVerifierCooperator());
  295.         lazyLoad(this.getPriority1Site());
  296.         lazyLoad(this.getPriority2Site());
  297.         lazyLoad(this.getCurator1Cooperator());
  298.         lazyLoad(this.getCurator2Cooperator());
  299.     }

  300.     @Override
  301.     public void prepareForIndexing() {
  302.         this.lazyLoad();
  303.     }

  304.     @Override
  305.     public boolean canEqual(Object other) {
  306.         return other instanceof TaxonomySpecies;
  307.     }
  308. }