TaxonomySpecies.java

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

import javax.persistence.Basic;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.genesys.blocks.model.Copyable;
import org.genesys.custom.elasticsearch.IgnoreField;
import org.genesys.custom.elasticsearch.SearchField;
import org.genesys.taxonomy.gringlobal.model.IGrinSpecies;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

import java.time.Instant;
import java.util.Date;

/**
 * Auto-generated by:
 * org.apache.openjpa.jdbc.meta.ReverseMappingTool$AnnotatedCodeGenerator
 */
@Entity
@Cacheable
@Table(name = "grin_species")
@Document(indexName = "grinspecies")
@JsonIdentityInfo(scope = TaxonomySpecies.class, generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
@Getter
@Setter
@NoArgsConstructor
public class TaxonomySpecies extends CooperatorOwnedModel implements IGrinSpecies, Copyable<TaxonomySpecies> {

	private static final long serialVersionUID = -6577613192196396663L;

	private static final String GRIN_SP_ABBREVIATION = "sp.";
	private static final String GRIN_SPP_ABBREVIATION = "spp.";

	private static final String PREFIX_SUBSPECIES = "subsp. ";
	private static final String PREFIX_NOTHOSUBSPECIES = "nothosubsp. ";
	private static final String PREFIX_VARIETY = "var. ";
	private static final String PREFIX_SUBVARIETY = "subvar. ";
	private static final String PREFIX_FORMA = "f.";

	@Id
	@JsonProperty
	@Column(name = "taxonomy_species_id")
	private Long id;

	@Basic
	@Column(name = "alternate_name", length = 2000)
	@JsonIgnore
	@SearchField
	private String alternateName;

	@Basic
	@Column(name = "common_fertilization_code", length = 20)
	@JsonIgnore
	private String commonFertilizationCode;

	@ManyToOne(fetch = FetchType.LAZY, cascade = {})
	@JoinColumn(name = "current_taxonomy_species_id")
	@JsonIdentityInfo(scope = TaxonomySpecies.class, generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
	@IgnoreField
	@JsonIgnoreProperties({ "currentTaxonomySpecies", "taxonomyGenus" })
	@NotFound(action = NotFoundAction.IGNORE)
	private TaxonomySpecies currentTaxonomySpecies;

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

	@Basic
	@Column(name = "forma_name", length = 30)
	@SearchField
	private String formaName;

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

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

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

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

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

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

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

	@Basic
	@Column(name = "life_form_code", length = 20)
	private String lifeFormCode;

	@Basic
	@Column(nullable = false, length = 100)
	@SearchField
	private String name;

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

	@Basic
	@Column(name = "name_verified_date")
	private Instant nameVerifiedDate;

	@Basic
	@Column(name = "nomen_number")
	private Integer nomenNumber;

	@Basic
	@Column
	@Lob
	@JsonIgnore
	private String note;

	@Basic
	@Column(length = 500)
	@JsonIgnore
	private String protologue;

	@Basic
	@Column(name = "protologue_virtual_path")
	@JsonIgnore
	private String protologueVirtualPath;

	@Basic
	@Column(name = "restriction_code", length = 20)
	private String restrictionCode;

	@Basic
	@Column(name = "site_note")
	@Lob
	@JsonIgnore
	private String siteNote;

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

	@Basic
	@Column(name = "species_name", nullable = false, length = 30)
	@SearchField
	private String speciesName;

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

	@Basic
	@Column(name = "subspecies_name", length = 30)
	@SearchField
	private String subspeciesName;

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

	@Basic
	@Column(name = "subvariety_name", length = 30)
	@SearchField
	private String subvarietyName;

	@Basic
	@Column(name = "synonym_code", length = 20)
	private String synonymCode;

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

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

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

	@Column(name = "subtaxa", length = 100)
	@Field(type = FieldType.Text)
	@SearchField
	private String subtaxa;

	// This is a utility for looking up all species records by name
	@Column(name = "genus_species", length = 100)
	@SearchField
	private String genusSpecies;

	@PrePersist
	@PreUpdate
	private void updateName() {
		{
			// Update genusSpecies label
			this.genusSpecies = this.taxonomyGenus.getName() + " " + (GRIN_SPP_ABBREVIATION.equals(this.speciesName) ? GRIN_SP_ABBREVIATION : this.speciesName);
		}

		if (StringUtils.isBlank(this.name)) {
			this.name = this.genusSpecies;
			this.nameAuthority = this.speciesAuthority;

			if (StringUtils.isNotBlank(this.subspeciesName)) {
				if ("Y".equals(isSubspecificHybrid)) {
					this.name += PREFIX_NOTHOSUBSPECIES + subspeciesName;
				} else {
					this.name += PREFIX_SUBSPECIES + this.subspeciesName;
				}
				this.nameAuthority = this.subspeciesAuthority;
			}

			if (StringUtils.isNotBlank(this.varietyName)) {
				this.name += PREFIX_VARIETY + this.varietyName;
				this.nameAuthority = this.varietyAuthority;
			}

			if (StringUtils.isNotBlank(this.subvarietyName)) {
				this.name += PREFIX_SUBVARIETY + this.subvarietyName;
				this.nameAuthority = this.subvarietyAuthority;
			}

			if (StringUtils.isNotBlank(this.formaName)) {
				this.name += this.formaRankType + " " + this.formaName;
				this.nameAuthority = this.formaAuthority;
			}
		}

		{
			// Always recalculate subtaxa field
			this.subtaxa = null;
			if (StringUtils.isNotBlank(this.subspeciesName)) {
				if ("Y".equals(isSubspecificHybrid)) {
					this.subtaxa = PREFIX_NOTHOSUBSPECIES + subspeciesName;
				} else {
					this.subtaxa = PREFIX_SUBSPECIES + subspeciesName;
				}
			}
			if (StringUtils.isNotBlank(this.varietyName)) {
				this.subtaxa += " " + PREFIX_VARIETY + varietyName;
			}
			if (StringUtils.isNotBlank(this.subvarietyName)) {
				this.subtaxa += " " + PREFIX_SUBVARIETY + subvarietyName;
			}
			if (StringUtils.isNotBlank(this.formaName)) {
				String prefix = StringUtils.defaultIfBlank(formaRankType, PREFIX_FORMA) + " ";
				this.subtaxa = prefix + formaName;
			}
			if (this.subtaxa != null) {
				this.subtaxa = this.subtaxa.replaceAll("\\s\\s+", " "); // remove double spaces
			}
		}
	}

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

	@Override
	public Long getGenusId() {
		return this.taxonomyGenus.getId();
	}

	@Override
	public Long getSpeciesId() {
		return this.id;
	}

	@Override
	public boolean isCurrent() {
		return this.id.equals(getCurrentTaxonomySpeciesId());
	}

	@Override
	public Long getCurrentTaxonomySpeciesId() {
		return this.currentTaxonomySpecies == null ? null : this.currentTaxonomySpecies.getId();
	}

	@Override
	public String getSubtaxaAuthority() {
		if (formaName != null) {
			return formaAuthority;
		}
		if (subvarietyName != null) {
			return subvarietyAuthority;
		}
		if (varietyName != null) {
			return varietyAuthority;
		}
		if (subspeciesName != null) {
			return subspeciesAuthority;
		}
		return null;
	}

	public void setNameVerifiedDate(Instant nameVerifiedDate) {
		this.nameVerifiedDate = nameVerifiedDate;
	}

	public void setNameVerifiedDate(Date nameVerifiedDate) {
		setNameVerifiedDate(nameVerifiedDate == null ? null : nameVerifiedDate.toInstant());
	}

	/**
	 * Utility to create an object reference to specified {@link TaxonomySpecies#id}.
	 * 
	 * @param taxonomySpeciesId may be {@code null}
	 * @return {@code null} if incoming id is {@code null}, reference object otherwise
	 */
	public static TaxonomySpecies withId(Long taxonomySpeciesId) {
		return taxonomySpeciesId == null ? null : new TaxonomySpecies(taxonomySpeciesId);
	}

	@Override
	public boolean canEqual(Object other) {
		return other instanceof TaxonomySpecies;
	}
}