X-Git-Url: https://dev.e-taxonomy.eu/gitweb/cdmlib.git/blobdiff_plain/57606ee3dcf9db7efd1b7a5220ad8d53f4200d97..b714e9868589a27627e3e0349a078273ff47eec8:/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/NonViralName.java diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/NonViralName.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/NonViralName.java index 31b7b6046f..272448f1e4 100644 --- a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/NonViralName.java +++ b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/NonViralName.java @@ -1,8 +1,8 @@ /** * Copyright (C) 2007 EDIT -* European Distributed Institute of Taxonomy +* European Distributed Institute of Taxonomy * http://www.e-taxonomy.eu -* +* * The contents of this file are subject to the Mozilla Public License Version 1.1 * See LICENSE.TXT at the top of this package for the full license terms. */ @@ -10,7 +10,13 @@ package eu.etaxonomy.cdm.model.name; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; import javax.persistence.Entity; @@ -18,6 +24,9 @@ import javax.persistence.FetchType; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Transient; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; @@ -32,30 +41,41 @@ import org.hibernate.annotations.Cascade; import org.hibernate.annotations.CascadeType; import org.hibernate.annotations.Target; import org.hibernate.envers.Audited; +import org.hibernate.search.annotations.Analyze; import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Fields; -import org.hibernate.search.annotations.Index; import org.hibernate.search.annotations.Indexed; +import org.hibernate.search.annotations.IndexedEmbedded; +import org.hibernate.validator.constraints.NotEmpty; import org.springframework.beans.factory.annotation.Configurable; +import eu.etaxonomy.cdm.common.CdmUtils; import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor; import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase; +import eu.etaxonomy.cdm.model.common.CdmBase; import eu.etaxonomy.cdm.model.common.RelationshipBase; import eu.etaxonomy.cdm.model.reference.INomenclaturalReference; -import eu.etaxonomy.cdm.model.reference.ReferenceBase; +import eu.etaxonomy.cdm.model.reference.Reference; +import eu.etaxonomy.cdm.strategy.cache.name.CacheUpdate; import eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy; import eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy; import eu.etaxonomy.cdm.strategy.match.Match; -import eu.etaxonomy.cdm.strategy.match.MatchMode; import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode; +import eu.etaxonomy.cdm.strategy.match.MatchMode; import eu.etaxonomy.cdm.strategy.merge.Merge; import eu.etaxonomy.cdm.strategy.merge.MergeMode; +import eu.etaxonomy.cdm.validation.Level2; +import eu.etaxonomy.cdm.validation.Level3; +import eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank; +import eu.etaxonomy.cdm.validation.annotation.MustHaveAuthority; +import eu.etaxonomy.cdm.validation.annotation.NoDuplicateNames; +import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty; /** * The taxon name class for all non viral taxa. Parenthetical authorship is derived * from basionym relationship. The scientific name including author strings and * maybe year can be stored as a string in the inherited {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute. - * The year itself is an information obtained from the {@link eu.etaxonomy.cdm.model.reference.ReferenceBase#getYear() nomenclatural reference}. + * The year itself is an information obtained from the {@link eu.etaxonomy.cdm.model.reference.Reference#getYear() nomenclatural reference}. * The scientific name string without author strings and year can be stored in the {@link #getNameCache() nameCache} attribute. *

* This class corresponds partially to:

- * + * * @author m.doering * @version 1.0 * @created 08-Nov-2007 13:06:39 @@ -87,469 +107,596 @@ import eu.etaxonomy.cdm.strategy.merge.MergeMode; "hybridFormula", "monomHybrid", "binomHybrid", - "trinomHybrid", + "trinomHybrid" }) @XmlRootElement(name = "NonViralName") @Entity @Indexed(index = "eu.etaxonomy.cdm.model.name.TaxonNameBase") @Audited @Configurable -public class NonViralName extends TaxonNameBase { - private static final long serialVersionUID = 4441110073881088033L; - private static final Logger logger = Logger.getLogger(NonViralName.class); - - @XmlElement(name = "NameCache") - @Fields({@Field(index = org.hibernate.search.annotations.Index.TOKENIZED), - @Field(name = "nameCache_forSort", index = org.hibernate.search.annotations.Index.UN_TOKENIZED) +@CorrectEpithetsForRank(groups = Level2.class) +@MustHaveAuthority(groups = Level2.class) +@NoDuplicateNames(groups = Level3.class) +public class NonViralName extends TaxonNameBase implements Cloneable{ + private static final long serialVersionUID = 4441110073881088033L; + private static final Logger logger = Logger.getLogger(NonViralName.class); + + @XmlElement(name = "NameCache") + @Fields({ + @Field(name = "nameCache_tokenized"), + @Field(analyze = Analyze.NO) }) - @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.DEFINED, - cacheReplacedProperties={"genusOrUninomial", "infraGenericEpithet", "specificEpithet", "infraSpecificEpithet"} ) - private String nameCache; - - @XmlElement(name = "ProtectedNameCache") - protected boolean protectedNameCache; - - @XmlElement(name = "GenusOrUninomial") - @Field(index=Index.TOKENIZED) - @Match(MatchMode.EQUAL_REQUIRED) - private String genusOrUninomial; - - @XmlElement(name = "InfraGenericEpithet") - @Field(index=Index.TOKENIZED) - private String infraGenericEpithet; - - @XmlElement(name = "SpecificEpithet") - @Field(index=Index.TOKENIZED) - private String specificEpithet; - - @XmlElement(name = "InfraSpecificEpithet") - @Field(index=Index.TOKENIZED) - private String infraSpecificEpithet; - - @XmlElement(name = "CombinationAuthorTeam", type = TeamOrPersonBase.class) + @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.DEFINED, + cacheReplacedProperties={"genusOrUninomial", "infraGenericEpithet", "specificEpithet", "infraSpecificEpithet"} ) + @NotEmpty(groups = Level2.class) // implicitly NotNull + @Size(max = 255) + private String nameCache; + + @XmlElement(name = "ProtectedNameCache") + @CacheUpdate(value="nameCache") + protected boolean protectedNameCache; + + @XmlElement(name = "GenusOrUninomial") + @Field + @Match(MatchMode.EQUAL_REQUIRED) + @CacheUpdate("nameCache") + @Size(max = 255) + @Pattern(regexp = "[A-Z][a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class, message="{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForUninomial.message}") + @NullOrNotEmpty + @NotEmpty(groups = Level3.class) //TODO shouldn't this be only @NotNull as @NullOrNotEmpty already checks for not being empty. + private String genusOrUninomial; + + @XmlElement(name = "InfraGenericEpithet") + @Field + @CacheUpdate("nameCache") + //TODO Val #3379 +// @NullOrNotEmpty + @Size(max = 255) + @Pattern(regexp = "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class,message="{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}") + private String infraGenericEpithet; + + @XmlElement(name = "SpecificEpithet") + @Field + @CacheUpdate("nameCache") + //TODO Val #3379 +// @NullOrNotEmpty + @Size(max = 255) + @Pattern(regexp = "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class, message = "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}") + private String specificEpithet; + + @XmlElement(name = "InfraSpecificEpithet") + @Field + @CacheUpdate("nameCache") + //TODO Val #3379 +// @NullOrNotEmpty + @Size(max = 255) + @Pattern(regexp = "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class, message = "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}") + private String infraSpecificEpithet; + + @XmlElement(name = "CombinationAuthorTeam", type = TeamOrPersonBase.class) @XmlIDREF @XmlSchemaType(name = "IDREF") @ManyToOne(fetch = FetchType.LAZY) - @Target(TeamOrPersonBase.class) - @Cascade(CascadeType.SAVE_UPDATE) - private INomenclaturalAuthor combinationAuthorTeam; - - @XmlElement(name = "ExCombinationAuthorTeam", type = TeamOrPersonBase.class) + @Target(TeamOrPersonBase.class) + @Cascade(CascadeType.SAVE_UPDATE) + @CacheUpdate("authorshipCache") + @IndexedEmbedded + private INomenclaturalAuthor combinationAuthorTeam; + + @XmlElement(name = "ExCombinationAuthorTeam", type = TeamOrPersonBase.class) @XmlIDREF @XmlSchemaType(name = "IDREF") @ManyToOne(fetch = FetchType.LAZY) - @Target(TeamOrPersonBase.class) - @Cascade(CascadeType.SAVE_UPDATE) - private INomenclaturalAuthor exCombinationAuthorTeam; - - @XmlElement(name = "BasionymAuthorTeam", type = TeamOrPersonBase.class) + @Target(TeamOrPersonBase.class) + @Cascade(CascadeType.SAVE_UPDATE) + @CacheUpdate("authorshipCache") + @IndexedEmbedded + private INomenclaturalAuthor exCombinationAuthorTeam; + + @XmlElement(name = "BasionymAuthorTeam", type = TeamOrPersonBase.class) @XmlIDREF @XmlSchemaType(name = "IDREF") @ManyToOne(fetch = FetchType.LAZY) - @Target(TeamOrPersonBase.class) - @Cascade(CascadeType.SAVE_UPDATE) - private INomenclaturalAuthor basionymAuthorTeam; - - @XmlElement(name = "ExBasionymAuthorTeam", type = TeamOrPersonBase.class) + @Target(TeamOrPersonBase.class) + @Cascade(CascadeType.SAVE_UPDATE) + @CacheUpdate("authorshipCache") + @IndexedEmbedded + private INomenclaturalAuthor basionymAuthorTeam; + + @XmlElement(name = "ExBasionymAuthorTeam", type = TeamOrPersonBase.class) @XmlIDREF @XmlSchemaType(name = "IDREF") @ManyToOne(fetch = FetchType.LAZY) - @Target(TeamOrPersonBase.class) - @Cascade(CascadeType.SAVE_UPDATE) - private INomenclaturalAuthor exBasionymAuthorTeam; - - @XmlElement(name = "AuthorshipCache") - @Field(index=Index.TOKENIZED) - @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.DEFINED, - cacheReplacedProperties={"combinationAuthorTeam", "basionymAuthorTeam", "exCombinationAuthorTeam", "exBasionymAuthorTeam"} ) - private String authorshipCache; - - @XmlElement(name = "ProtectedAuthorshipCache") - protected boolean protectedAuthorshipCache; + @Target(TeamOrPersonBase.class) + @Cascade(CascadeType.SAVE_UPDATE) + @CacheUpdate("authorshipCache") + @IndexedEmbedded + private INomenclaturalAuthor exBasionymAuthorTeam; + + @XmlElement(name = "AuthorshipCache") + @Fields({ + @Field(name = "authorshipCache_tokenized"), + @Field(analyze = Analyze.NO) + }) + @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.DEFINED, + cacheReplacedProperties={"combinationAuthorTeam", "basionymAuthorTeam", "exCombinationAuthorTeam", "exBasionymAuthorTeam"} ) + //TODO Val #3379 +// @NotNull + @Size(max = 255) + @Pattern(regexp = "[A-Za-z0-9 \\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-\\&\\,\\(\\)\\.]+", groups=Level2.class, message = "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForAuthority.message}") + private String authorshipCache; + + @XmlElement(name = "ProtectedAuthorshipCache") + @CacheUpdate("authorshipCache") + protected boolean protectedAuthorshipCache; @XmlElementWrapper(name = "HybridRelationsFromThisName") @XmlElement(name = "HybridRelationsFromThisName") - @OneToMany(mappedBy="relatedFrom", fetch = FetchType.LAZY) - @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE_ORPHAN }) - @Merge(MergeMode.RELATION) - private Set hybridParentRelations = new HashSet(); + @OneToMany(mappedBy="relatedFrom", fetch = FetchType.LAZY, orphanRemoval=true) + @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE}) + @Merge(MergeMode.RELATION) + @NotNull + private Set hybridParentRelations = new HashSet(); @XmlElementWrapper(name = "HybridRelationsToThisName") @XmlElement(name = "HybridRelationsToThisName") - @OneToMany(mappedBy="relatedTo", fetch = FetchType.LAZY) - @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE_ORPHAN }) - @Merge(MergeMode.RELATION) - private Set hybridChildRelations = new HashSet(); + @OneToMany(mappedBy="relatedTo", fetch = FetchType.LAZY, orphanRemoval=true) //a hybrid relation can be deleted automatically if the child is deleted. + @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE}) + @Merge(MergeMode.RELATION) + @NotNull + private Set hybridChildRelations = new HashSet(); - //if set: this name is a hybrid formula (a hybrid that does not have an own name) and no other hybrid flags may be set. A - //hybrid name may not have either an authorteam nor other name components. + //if set: this name is a hybrid formula (a hybrid that does not have an own name) and no other hybrid flags may be set. A + //hybrid name may not have either an authorteam nor other name components. @XmlElement(name ="IsHybridFormula") - private boolean hybridFormula = false; - + @CacheUpdate("nameCache") + private boolean hybridFormula = false; + @XmlElement(name ="IsMonomHybrid") - private boolean monomHybrid = false; - + @CacheUpdate("nameCache") + private boolean monomHybrid = false; + @XmlElement(name ="IsBinomHybrid") - private boolean binomHybrid = false; - + @CacheUpdate("nameCache") + private boolean binomHybrid = false; + @XmlElement(name ="IsTrinomHybrid") - private boolean trinomHybrid = false; - -// @XmlTransient -// @Transient -// protected INonViralNameCacheStrategy cacheStrategy; - - // ************* CONSTRUCTORS *************/ - - //needed by hibernate - /** - * Class constructor: creates a new non viral taxon name instance - * only containing the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}. - * - * @see #NonViralName(Rank, HomotypicalGroup) - * @see #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, ReferenceBase, String, HomotypicalGroup) - * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy - * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy - * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy - */ - protected NonViralName(){ - super(); - setNameCacheStrategy(); - } - - /** - * Class constructor: creates a new non viral taxon name instance - * only containing its {@link Rank rank}, - * its {@link HomotypicalGroup homotypical group} and - * the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}. - * The new non viral taxon name instance will be also added to the set of - * non viral taxon names belonging to this homotypical group. - * - * @param rank the rank to be assigned to this non viral taxon name - * @param homotypicalGroup the homotypical group to which this non viral taxon name belongs - * @see #NonViralName() - * @see #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, ReferenceBase, String, HomotypicalGroup) - * @see #NewInstance(Rank, HomotypicalGroup) - * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy - * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy - * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy - */ - protected NonViralName(Rank rank, HomotypicalGroup homotypicalGroup) { - super(rank, homotypicalGroup); - setNameCacheStrategy(); - } - /** - * Class constructor: creates a new non viral taxon name instance - * containing its {@link Rank rank}, - * its {@link HomotypicalGroup homotypical group}, - * its scientific name components, its {@link eu.etaxonomy.cdm.model.agent.TeamOrPersonBase author(team)}, - * its {@link eu.etaxonomy.cdm.model.reference.ReferenceBase nomenclatural reference} and - * the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}. - * The new non viral taxon name instance will be also added to the set of - * non viral taxon names belonging to this homotypical group. - * - * @param rank the rank to be assigned to this non viral taxon name - * @param genusOrUninomial the string for this non viral taxon name - * if its rank is genus or higher or for the genus part - * if its rank is lower than genus - * @param infraGenericEpithet the string for the first epithet of - * this non viral taxon name if its rank is lower than genus - * and higher than species aggregate - * @param specificEpithet the string for the first epithet of - * this non viral taxon name if its rank is species aggregate or lower - * @param infraSpecificEpithet the string for the second epithet of - * this non viral taxon name if its rank is lower than species - * @param combinationAuthorTeam the author or the team who published this non viral taxon name - * @param nomenclaturalReference the nomenclatural reference where this non viral taxon name was published - * @param nomenclMicroRef the string with the details for precise location within the nomenclatural reference - * @param homotypicalGroup the homotypical group to which this non viral taxon name belongs - * @see #NonViralName() - * @see #NonViralName(Rank, HomotypicalGroup) - * @see #NewInstance(Rank, HomotypicalGroup) - * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy - * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy - * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy - */ - protected NonViralName(Rank rank, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, TeamOrPersonBase combinationAuthorTeam, INomenclaturalReference nomenclaturalReference, String nomenclMicroRef, HomotypicalGroup homotypicalGroup) { - super(rank, homotypicalGroup); - setNameCacheStrategy(); - setGenusOrUninomial(genusOrUninomial); - setInfraGenericEpithet (infraGenericEpithet); - setSpecificEpithet(specificEpithet); - setInfraSpecificEpithet(infraSpecificEpithet); - setCombinationAuthorTeam(combinationAuthorTeam); - setNomenclaturalReference((ReferenceBase)nomenclaturalReference); - this.setNomenclaturalMicroReference(nomenclMicroRef); - } - - //********* METHODS **************************************/ - /** - * Creates a new non viral taxon name instance - * only containing its {@link common.Rank rank} and - * the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}. - * - * @param rank the rank to be assigned to this non viral taxon name - * @see #NewInstance(Rank, HomotypicalGroup) - * @see #NonViralName(Rank, HomotypicalGroup) - * @see #NonViralName() - * @see #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, ReferenceBase, String, HomotypicalGroup) - * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy - * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy - * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy - */ - public static NonViralName NewInstance(Rank rank){ - return new NonViralName(rank, null); - } - - /** - * Creates a new non viral taxon name instance - * only containing its {@link common.Rank rank}, - * its {@link HomotypicalGroup homotypical group} and - * the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}. - * The new non viral taxon name instance will be also added to the set of - * non viral taxon names belonging to this homotypical group. - * - * @param rank the rank to be assigned to this non viral taxon name - * @param homotypicalGroup the homotypical group to which this non viral taxon name belongs - * @see #NewInstance(Rank) - * @see #NonViralName(Rank, HomotypicalGroup) - * @see #NonViralName() - * @see #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, ReferenceBase, String, HomotypicalGroup) - * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy - * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy - * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy - */ - public static NonViralName NewInstance(Rank rank, HomotypicalGroup homotypicalGroup){ - return new NonViralName(rank, homotypicalGroup); - } - - private void setNameCacheStrategy(){ - if (getClass() == NonViralName.class){ - this.cacheStrategy = NonViralNameDefaultCacheStrategy.NewInstance(); - } - - } - - //TODO for PROTOTYPE - /** - * Returns the {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy cache strategy} used to generate - * several strings corresponding to this non viral taxon name - * (in particular taxon name caches and author strings). - * - * @return the cache strategy used for this non viral taxon name - * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy - * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy - * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy - */ -// @Override -// @Transient -// public INonViralNameCacheStrategy getCacheStrategy() { -// return cacheStrategy; -// } - - /** - * @see #getCacheStrategy() - */ -// @Override -// public void setCacheStrategy(INonViralNameCacheStrategy cacheStrategy) { -// this.cacheStrategy = cacheStrategy; -// } + @CacheUpdate("nameCache") + private boolean trinomHybrid = false; + + /** + * Creates a new non viral taxon name instance + * only containing its {@link common.Rank rank} and + * the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}. + * + * @param rank the rank to be assigned to this non viral taxon name + * @see #NewInstance(Rank, HomotypicalGroup) + * @see #NonViralName(Rank, HomotypicalGroup) + * @see #NonViralName() + * @see #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup) + * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy + * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy + * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy + */ + public static NonViralName NewInstance(Rank rank){ + return new NonViralName(rank, null); + } + + /** + * Creates a new non viral taxon name instance + * only containing its {@link common.Rank rank}, + * its {@link HomotypicalGroup homotypical group} and + * the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}. + * The new non viral taxon name instance will be also added to the set of + * non viral taxon names belonging to this homotypical group. + * + * @param rank the rank to be assigned to this non viral taxon name + * @param homotypicalGroup the homotypical group to which this non viral taxon name belongs + * @see #NewInstance(Rank) + * @see #NonViralName(Rank, HomotypicalGroup) + * @see #NonViralName() + * @see #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup) + * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy + * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy + * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy + */ + public static NonViralName NewInstance(Rank rank, HomotypicalGroup homotypicalGroup){ + return new NonViralName(rank, homotypicalGroup); + } + +// ************************** CONSTRUCTORS *************/ + + //needed by hibernate + /** + * Class constructor: creates a new non viral taxon name instance + * only containing the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}. + * + * @see #NonViralName(Rank, HomotypicalGroup) + * @see #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup) + * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy + * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy + * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy + */ + protected NonViralName(){ + super(); + setNameCacheStrategy(); + } + + /** + * Class constructor: creates a new non viral taxon name instance + * only containing its {@link Rank rank}, + * its {@link HomotypicalGroup homotypical group} and + * the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}. + * The new non viral taxon name instance will be also added to the set of + * non viral taxon names belonging to this homotypical group. + * + * @param rank the rank to be assigned to this non viral taxon name + * @param homotypicalGroup the homotypical group to which this non viral taxon name belongs + * @see #NonViralName() + * @see #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup) + * @see #NewInstance(Rank, HomotypicalGroup) + * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy + * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy + * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy + */ + protected NonViralName(Rank rank, HomotypicalGroup homotypicalGroup) { + super(rank, homotypicalGroup); + setNameCacheStrategy(); + } + /** + * Class constructor: creates a new non viral taxon name instance + * containing its {@link Rank rank}, + * its {@link HomotypicalGroup homotypical group}, + * its scientific name components, its {@link eu.etaxonomy.cdm.model.agent.TeamOrPersonBase author(team)}, + * its {@link eu.etaxonomy.cdm.model.reference.Reference nomenclatural reference} and + * the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}. + * The new non viral taxon name instance will be also added to the set of + * non viral taxon names belonging to this homotypical group. + * + * @param rank the rank to be assigned to this non viral taxon name + * @param genusOrUninomial the string for this non viral taxon name + * if its rank is genus or higher or for the genus part + * if its rank is lower than genus + * @param infraGenericEpithet the string for the first epithet of + * this non viral taxon name if its rank is lower than genus + * and higher than species aggregate + * @param specificEpithet the string for the first epithet of + * this non viral taxon name if its rank is species aggregate or lower + * @param infraSpecificEpithet the string for the second epithet of + * this non viral taxon name if its rank is lower than species + * @param combinationAuthorTeam the author or the team who published this non viral taxon name + * @param nomenclaturalReference the nomenclatural reference where this non viral taxon name was published + * @param nomenclMicroRef the string with the details for precise location within the nomenclatural reference + * @param homotypicalGroup the homotypical group to which this non viral taxon name belongs + * @see #NonViralName() + * @see #NonViralName(Rank, HomotypicalGroup) + * @see #NewInstance(Rank, HomotypicalGroup) + * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy + * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy + * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy + */ + protected NonViralName(Rank rank, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, TeamOrPersonBase combinationAuthorTeam, INomenclaturalReference nomenclaturalReference, String nomenclMicroRef, HomotypicalGroup homotypicalGroup) { + super(rank, homotypicalGroup); + setNameCacheStrategy(); + setGenusOrUninomial(genusOrUninomial); + setInfraGenericEpithet (infraGenericEpithet); + setSpecificEpithet(specificEpithet); + setInfraSpecificEpithet(infraSpecificEpithet); + setCombinationAuthorTeam(combinationAuthorTeam); + setNomenclaturalReference(nomenclaturalReference); + this.setNomenclaturalMicroReference(nomenclMicroRef); + } + + + +//**************************** METHODS **************************************/ + + + private void setNameCacheStrategy(){ + if (getClass() == NonViralName.class){ + this.cacheStrategy = NonViralNameDefaultCacheStrategy.NewInstance(); + } + } + + @Override + protected void initListener(){ + PropertyChangeListener listener = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent e) { + boolean protectedByLowerCache = false; + //authorship cache + if (fieldHasCacheUpdateProperty(e.getPropertyName(), "authorshipCache")){ + if (protectedAuthorshipCache){ + protectedByLowerCache = true; + }else{ + authorshipCache = null; + } + } - /** - * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that published this non viral - * taxon name. - * - * @return the nomenclatural author (team) of this non viral taxon name - * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor - * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle() - */ - public INomenclaturalAuthor getCombinationAuthorTeam(){ - return this.combinationAuthorTeam; - } - - /** - * @see #getCombinationAuthorTeam() - */ - public void setCombinationAuthorTeam(INomenclaturalAuthor combinationAuthorTeam){ - this.combinationAuthorTeam = combinationAuthorTeam; - } - - /** - * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that contributed to - * the publication of this non viral taxon name as generally stated by - * the {@link #getCombinationAuthorTeam() combination author (team)} itself.
- * An ex-author(-team) is an author(-team) to whom a taxon name was ascribed - * although it is not the author(-team) of a valid publication (for instance - * without the validating description or diagnosis in case of a name for a - * new taxon). The name of this ascribed authorship, followed by "ex", may - * be inserted before the name(s) of the publishing author(s) of the validly - * published name:
- * Lilium tianschanicum was described by Grubov (1977) as a new species and - * its name was ascribed to Ivanova; since there is no indication that - * Ivanova provided the validating description, the name may be cited as - * Lilium tianschanicum N. A. Ivanova ex Grubov or Lilium tianschanicum Grubov. - *

- * The presence of an author (team) of this non viral taxon name is a - * condition for the existence of an ex author (team) for this same name. - * - * @return the nomenclatural ex author (team) of this non viral taxon name - * @see #getCombinationAuthorTeam() - * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor - * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle() - */ - public INomenclaturalAuthor getExCombinationAuthorTeam(){ - return this.exCombinationAuthorTeam; - } - - /** - * @see #getExCombinationAuthorTeam() - */ - public void setExCombinationAuthorTeam(INomenclaturalAuthor exCombinationAuthorTeam){ - this.exCombinationAuthorTeam = exCombinationAuthorTeam; - } - - /** - * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that published the original combination - * on which this non viral taxon name is nomenclaturally based. Such an - * author (team) can only exist if this non viral taxon name is a new - * combination due to a taxonomical revision. - * - * @return the nomenclatural basionym author (team) of this non viral taxon name - * @see #getCombinationAuthorTeam() - * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor - * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle() - */ - public INomenclaturalAuthor getBasionymAuthorTeam(){ - return basionymAuthorTeam; - } - - /** - * @see #getBasionymAuthorTeam() - */ - public void setBasionymAuthorTeam(INomenclaturalAuthor basionymAuthorTeam) { - this.basionymAuthorTeam = basionymAuthorTeam; - } - - /** - * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that contributed to - * the publication of the original combination this non viral taxon name is - * based on. This should have been generally stated by - * the {@link #getBasionymAuthorTeam() basionym author (team)} itself. - * The presence of a basionym author (team) of this non viral taxon name is a - * condition for the existence of an ex basionym author (team) - * for this same name. - * - * @return the nomenclatural ex basionym author (team) of this non viral taxon name - * @see #getBasionymAuthorTeam() - * @see #getExCombinationAuthorTeam() - * @see #getCombinationAuthorTeam() - * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor - * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle() - */ - public INomenclaturalAuthor getExBasionymAuthorTeam(){ - return exBasionymAuthorTeam; - } - - /** - * @see #getExBasionymAuthorTeam() - */ - public void setExBasionymAuthorTeam(INomenclaturalAuthor exBasionymAuthorTeam) { - this.exBasionymAuthorTeam = exBasionymAuthorTeam; - } - /** - * Returns either the scientific name string (without authorship) for this - * non viral taxon name if its rank is genus or higher (monomial) or the string for - * the genus part of it if its {@link Rank rank} is lower than genus (bi- or trinomial). - * Genus or uninomial strings begin with an upper case letter. - * - * @return the string containing the suprageneric name, the genus name or the genus part of this non viral taxon name - * @see #getNameCache() - */ - public String getGenusOrUninomial() { - return genusOrUninomial; - } - - /** - * @see #getGenusOrUninomial() - */ - public void setGenusOrUninomial(String genusOrUninomial) { - this.genusOrUninomial = genusOrUninomial; - } - - /** - * Returns the genus subdivision epithet string (infrageneric part) for - * this non viral taxon name if its {@link Rank rank} is infrageneric (lower than genus and - * higher than species aggregate: binomial). Genus subdivision epithet - * strings begin with an upper case letter. - * - * @return the string containing the infrageneric part of this non viral taxon name - * @see #getNameCache() - */ - public String getInfraGenericEpithet(){ - return this.infraGenericEpithet; - } - - /** - * @see #getInfraGenericEpithet() - */ - public void setInfraGenericEpithet(String infraGenericEpithet){ - this.infraGenericEpithet = infraGenericEpithet; - } - - /** - * Returns the species epithet string for this non viral taxon name if its {@link Rank rank} is - * species aggregate or lower (bi- or trinomial). Species epithet strings - * begin with a lower case letter. - * - * @return the string containing the species epithet of this non viral taxon name - * @see #getNameCache() - */ - public String getSpecificEpithet(){ - return this.specificEpithet; - } - - /** - * @see #getSpecificEpithet() - */ - public void setSpecificEpithet(String specificEpithet){ - this.specificEpithet = specificEpithet; - } - - /** - * Returns the species subdivision epithet string (infraspecific part) for - * this non viral taxon name if its {@link Rank rank} is infraspecific - * (lower than species: trinomial). Species subdivision epithet strings - * begin with a lower case letter. - * - * @return the string containing the infraspecific part of this non viral taxon name - * @see #getNameCache() - */ - public String getInfraSpecificEpithet(){ - return this.infraSpecificEpithet; - } - - /** - * @see #getInfraSpecificEpithet() - */ - public void setInfraSpecificEpithet(String infraSpecificEpithet){ - this.infraSpecificEpithet = infraSpecificEpithet; - } - - /** - * Generates and returns the string with the scientific name of this - * non viral taxon name including author strings and maybe year according to - * the strategy defined in - * {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy INonViralNameCacheStrategy}. - * This string may be stored in the inherited - * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute. - * This method overrides the generic and inherited - * TaxonNameBase#generateTitle() method. - * - * @return the string with the composed name of this non viral taxon name with authorship (and maybe year) - * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() - * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() - * @see TaxonNameBase#generateTitle() - */ + //nameCache + if (fieldHasCacheUpdateProperty(e.getPropertyName(), "nameCache")){ + if (protectedNameCache){ + protectedByLowerCache = true; + }else{ + nameCache = null; + } + } + //title cache + if (! fieldHasNoUpdateProperty(e.getPropertyName(), "titleCache")){ + if (isProtectedTitleCache()|| protectedByLowerCache == true ){ + protectedByLowerCache = true; + }else{ + titleCache = null; + } + } + //full title cache + if (! fieldHasNoUpdateProperty(e.getPropertyName(), "fullTitleCache")){ + if (isProtectedFullTitleCache()|| protectedByLowerCache == true ){ + protectedByLowerCache = true; + }else{ + fullTitleCache = null; + } + } + } + }; + addPropertyChangeListener(listener); //didn't use this.addXXX to make lsid.AssemblerTest run in cdmlib-remote + } + + private static Map allFields = null; + @Override + protected Map getAllFields(){ + if (allFields == null){ + allFields = CdmUtils.getAllFields(this.getClass(), CdmBase.class, false, false, false, true); + } + return allFields; + } + + /** + * @param propertyName + * @param string + * @return + */ + private boolean fieldHasCacheUpdateProperty(String propertyName, String cacheName) { + java.lang.reflect.Field field; + try { + field = getAllFields().get(propertyName); + if (field != null){ + CacheUpdate updateAnnotation = field.getAnnotation(CacheUpdate.class); + if (updateAnnotation != null){ + for (String value : updateAnnotation.value()){ + if (cacheName.equals(value)){ + return true; + } + } + } + } + return false; + } catch (SecurityException e1) { + throw e1; + } + } + + private boolean fieldHasNoUpdateProperty(String propertyName, String cacheName) { + java.lang.reflect.Field field; + //do not update fields with the same name + if (cacheName.equals(propertyName)){ + return true; + } + //evaluate annotation + try { + field = getAllFields().get(propertyName); + if (field != null){ + CacheUpdate updateAnnotation = field.getAnnotation(CacheUpdate.class); + if (updateAnnotation != null){ + for (String value : updateAnnotation.noUpdate()){ + if (cacheName.equals(value)){ + return true; + } + } + } + } + return false; + } catch (SecurityException e1) { + throw e1; + } + } + + + /** + * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that published this non viral + * taxon name. + * + * @return the nomenclatural author (team) of this non viral taxon name + * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor + * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle() + */ + public INomenclaturalAuthor getCombinationAuthorTeam(){ + return this.combinationAuthorTeam; + } + + /** + * @see #getCombinationAuthorTeam() + */ + public void setCombinationAuthorTeam(INomenclaturalAuthor combinationAuthorTeam){ + this.combinationAuthorTeam = combinationAuthorTeam; + } + + /** + * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that contributed to + * the publication of this non viral taxon name as generally stated by + * the {@link #getCombinationAuthorTeam() combination author (team)} itself.
+ * An ex-author(-team) is an author(-team) to whom a taxon name was ascribed + * although it is not the author(-team) of a valid publication (for instance + * without the validating description or diagnosis in case of a name for a + * new taxon). The name of this ascribed authorship, followed by "ex", may + * be inserted before the name(s) of the publishing author(s) of the validly + * published name:
+ * Lilium tianschanicum was described by Grubov (1977) as a new species and + * its name was ascribed to Ivanova; since there is no indication that + * Ivanova provided the validating description, the name may be cited as + * Lilium tianschanicum N. A. Ivanova ex Grubov or Lilium tianschanicum Grubov. + *

+ * The presence of an author (team) of this non viral taxon name is a + * condition for the existence of an ex author (team) for this same name. + * + * @return the nomenclatural ex author (team) of this non viral taxon name + * @see #getCombinationAuthorTeam() + * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor + * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle() + */ + public INomenclaturalAuthor getExCombinationAuthorTeam(){ + return this.exCombinationAuthorTeam; + } + + /** + * @see #getExCombinationAuthorTeam() + */ + public void setExCombinationAuthorTeam(INomenclaturalAuthor exCombinationAuthorTeam){ + this.exCombinationAuthorTeam = exCombinationAuthorTeam; + } + + /** + * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that published the original combination + * on which this non viral taxon name is nomenclaturally based. Such an + * author (team) can only exist if this non viral taxon name is a new + * combination due to a taxonomical revision. + * + * @return the nomenclatural basionym author (team) of this non viral taxon name + * @see #getCombinationAuthorTeam() + * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor + * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle() + */ + public INomenclaturalAuthor getBasionymAuthorTeam(){ + return basionymAuthorTeam; + } + + /** + * @see #getBasionymAuthorTeam() + */ + public void setBasionymAuthorTeam(INomenclaturalAuthor basionymAuthorTeam) { + this.basionymAuthorTeam = basionymAuthorTeam; + } + + /** + * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that contributed to + * the publication of the original combination this non viral taxon name is + * based on. This should have been generally stated by + * the {@link #getBasionymAuthorTeam() basionym author (team)} itself. + * The presence of a basionym author (team) of this non viral taxon name is a + * condition for the existence of an ex basionym author (team) + * for this same name. + * + * @return the nomenclatural ex basionym author (team) of this non viral taxon name + * @see #getBasionymAuthorTeam() + * @see #getExCombinationAuthorTeam() + * @see #getCombinationAuthorTeam() + * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor + * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle() + */ + public INomenclaturalAuthor getExBasionymAuthorTeam(){ + return exBasionymAuthorTeam; + } + + /** + * @see #getExBasionymAuthorTeam() + */ + public void setExBasionymAuthorTeam(INomenclaturalAuthor exBasionymAuthorTeam) { + this.exBasionymAuthorTeam = exBasionymAuthorTeam; + } + /** + * Returns either the scientific name string (without authorship) for this + * non viral taxon name if its rank is genus or higher (monomial) or the string for + * the genus part of it if its {@link Rank rank} is lower than genus (bi- or trinomial). + * Genus or uninomial strings begin with an upper case letter. + * + * @return the string containing the suprageneric name, the genus name or the genus part of this non viral taxon name + * @see #getNameCache() + */ + public String getGenusOrUninomial() { + return genusOrUninomial; + } + + /** + * @see #getGenusOrUninomial() + */ + public void setGenusOrUninomial(String genusOrUninomial) { + this.genusOrUninomial = genusOrUninomial; + } + + /** + * Returns the genus subdivision epithet string (infrageneric part) for + * this non viral taxon name if its {@link Rank rank} is infrageneric (lower than genus and + * higher than species aggregate: binomial). Genus subdivision epithet + * strings begin with an upper case letter. + * + * @return the string containing the infrageneric part of this non viral taxon name + * @see #getNameCache() + */ + public String getInfraGenericEpithet(){ + return this.infraGenericEpithet; + } + + /** + * @see #getInfraGenericEpithet() + */ + public void setInfraGenericEpithet(String infraGenericEpithet){ + this.infraGenericEpithet = infraGenericEpithet; + } + + /** + * Returns the species epithet string for this non viral taxon name if its {@link Rank rank} is + * species aggregate or lower (bi- or trinomial). Species epithet strings + * begin with a lower case letter. + * + * @return the string containing the species epithet of this non viral taxon name + * @see #getNameCache() + */ + public String getSpecificEpithet(){ + return this.specificEpithet; + } + + /** + * @see #getSpecificEpithet() + */ + public void setSpecificEpithet(String specificEpithet){ + this.specificEpithet = specificEpithet; + } + + /** + * Returns the species subdivision epithet string (infraspecific part) for + * this non viral taxon name if its {@link Rank rank} is infraspecific + * (lower than species: trinomial). Species subdivision epithet strings + * begin with a lower case letter. + * + * @return the string containing the infraspecific part of this non viral taxon name + * @see #getNameCache() + */ + public String getInfraSpecificEpithet(){ + return this.infraSpecificEpithet; + } + + /** + * @see #getInfraSpecificEpithet() + */ + public void setInfraSpecificEpithet(String infraSpecificEpithet){ + this.infraSpecificEpithet = infraSpecificEpithet; + } + + /** + * Generates and returns the string with the scientific name of this + * non viral taxon name including author strings and maybe year according to + * the strategy defined in + * {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy INonViralNameCacheStrategy}. + * This string may be stored in the inherited + * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute. + * This method overrides the generic and inherited + * TaxonNameBase#generateTitle() method. + * + * @return the string with the composed name of this non viral taxon name with authorship (and maybe year) + * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() + * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() + * @see TaxonNameBase#generateTitle() + */ // @Override // public String generateTitle(){ // if (cacheStrategy == null){ @@ -559,522 +706,739 @@ public class NonViralName extends TaxonNameBasethis non viral taxon name without author - * strings or year according to the strategy defined in - * {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy INonViralNameCacheStrategy}. - * The result might be stored in {@link #getNameCache() nameCache} if the - * flag {@link #isProtectedNameCache() protectedNameCache} is not set. - * - * @return the string with the composed name of this non viral taxon name without authors or year - * @see #getNameCache() - */ - protected String generateNameCache(){ - if (cacheStrategy == null){ - logger.warn("No CacheStrategy defined for taxonName: " + this.toString()); - return null; - }else{ - return cacheStrategy.getNameCache(this); - } - } - - /** - * Returns or generates the nameCache (scientific name - * without author strings and year) string for this non viral taxon name. If the - * {@link #isProtectedNameCache() protectedNameCache} flag is not set (False) - * the string will be generated according to a defined strategy, - * otherwise the value of the actual nameCache string will be returned. - * - * @return the string which identifies this non viral taxon name (without authors or year) - * @see #generateNameCache() - */ - @Transient - public String getNameCache() { - if (protectedNameCache){ - return this.nameCache; - } - // is title dirty, i.e. equal NULL? - if (nameCache == null){ - this.nameCache = generateNameCache(); - } - return nameCache; - } - - /** - * Assigns a nameCache string to this non viral taxon name and protects it from being overwritten. - * Sets the protectedNameCache flag to true. - * - * @param nameCache the string which identifies this non viral taxon name (without authors or year) - * @see #getNameCache() - */ - public void setNameCache(String nameCache){ - setNameCache(nameCache, true); - } - - /** - * Assigns a nameCache string to this non viral taxon name and protects it from being overwritten. - * Sets the protectedNameCache flag to true. - * - * @param nameCache the string which identifies this non viral taxon name (without authors or year) - * @param protectedNameCache if true teh protectedNameCache is set to true or otherwise set to - * false - * @see #getNameCache() - */ - public void setNameCache(String nameCache, boolean protectedNameCache){ - this.nameCache = nameCache; - //this.setProtectedTitleCache(false); //now handled by propertyChangeListener - this.setProtectedNameCache(protectedNameCache); - } - - /** - * Returns the boolean value of the flag intended to protect (true) - * or not (false) the {@link #getNameCache() nameCache} (scientific name without author strings and year) - * string of this non viral taxon name. - * - * @return the boolean value of the protectedNameCache flag - * @see #getNameCache() - */ - public boolean isProtectedNameCache() { - return protectedNameCache; - } - - /** - * @see #isProtectedNameCache() - */ - public void setProtectedNameCache(boolean protectedNameCache) { - this.protectedNameCache = protectedNameCache; - } - - - /** - * Generates and returns a concatenated and formated authorteams string - * including basionym and combination authors of this non viral taxon name - * according to the strategy defined in - * {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(NonViralName) INonViralNameCacheStrategy}. - * - * @return the string with the concatenated and formated authorteams for this non viral taxon name - * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(NonViralName) - */ - public String generateAuthorship(){ - if (cacheStrategy == null){ - logger.warn("No CacheStrategy defined for nonViralName: " + this.getUuid()); - return null; - }else{ - return ((INonViralNameCacheStrategy)cacheStrategy).getAuthorshipCache((T)this); - } - } - - /** - * Returns the concatenated and formated authorteams string including - * basionym and combination authors of this non viral taxon name. - * If the protectedAuthorshipCache flag is set this method returns the - * string stored in the the authorshipCache attribute, otherwise it - * generates the complete authorship string, returns it and stores it in - * the authorshipCache attribute. - * - * @return the string with the concatenated and formated authorteams for this non viral taxon name - * @see #generateAuthorship() - */ - @Transient - public String getAuthorshipCache() { - if (protectedAuthorshipCache){ - return this.authorshipCache; - } - // is title dirty, i.e. equal NULL? - if (this.authorshipCache == null || this.titleCache == null ){ - //TODO get is Dirty of authors - this.authorshipCache = generateAuthorship(); - } - return authorshipCache; - } - - /** - * Assigns an authorshipCache string to this non viral taxon name. Sets the isProtectedAuthorshipCache - * flag to true. - * - * @param authorshipCache the string which identifies the complete authorship of this non viral taxon name - * @see #getAuthorshipCache() - */ - public void setAuthorshipCache(String authorshipCache) { - setAuthorshipCache(authorshipCache, true); - } - - /** - * Assigns an authorshipCache string to this non viral taxon name. - * - * @param authorshipCache the string which identifies the complete authorship of this non viral taxon name - * @param protectedAuthorshipCache if true the isProtectedAuthorshipCache flag is set to true, otherwise - * the flag is set to false. - * @see #getAuthorshipCache() - */ - public void setAuthorshipCache(String authorshipCache, boolean protectedAuthorshipCache) { - this.authorshipCache = authorshipCache; - //TODO hibernate safe? - if (! this.isProtectedTitleCache()){ - this.setTitleCache(null, false); - } - this.setProtectedAuthorshipCache(protectedAuthorshipCache); - } - - public void setTitleCache(String titleCache, boolean protectCache){ - super.setTitleCache(titleCache, protectCache); - if (! this.isProtectedFullTitleCache()){ - this.fullTitleCache = null; - } - } - - /** - * Returns the boolean value "false" since the components of this taxon name - * cannot follow the rules of a corresponding {@link NomenclaturalCode nomenclatural code} - * which is not defined for this class. The nomenclature code depends on - * the concrete name subclass ({@link BacterialName BacterialName}, - * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} or - * {@link ZoologicalName ZoologicalName} to which this non viral taxon name belongs. - * This method overrides the isCodeCompliant method from the abstract - * {@link TaxonNameBase#isCodeCompliant() TaxonNameBase} class. - * - * @return false - * @see TaxonNameBase#isCodeCompliant() - */ - @Override - @Transient - public boolean isCodeCompliant() { - //FIXME - logger.warn("is CodeCompliant not yet implemented"); - return false; - } - - /* (non-Javadoc) - * @see eu.etaxonomy.cdm.model.name.TaxonNameBase#getNomeclaturalCode() - */ - /** - * Returns null as {@link NomenclaturalCode nomenclatural code} that governs - * the construction of this non viral taxon name since there is no specific - * nomenclatural code defined. The real implementention takes place in the - * subclasses {@link BacterialName BacterialName}, - * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and - * {@link ZoologicalName ZoologicalName}. - * This method overrides the getNomeclaturalCode method from {@link TaxonNameBase TaxonNameBase}. - * - * @return null - * @see #isCodeCompliant() - * @see TaxonNameBase#getHasProblem() - */ - @Override - @Transient - public NomenclaturalCode getNomenclaturalCode() { - logger.warn("Non Viral Name has no specific Code defined. Use subclasses"); - return null; - } - - /** - * Returns the boolean value of the flag intended to protect (true) - * or not (false) the {@link #getAuthorshipCache() authorshipCache} (complete authorship string) - * of this non viral taxon name. - * - * @return the boolean value of the protectedAuthorshipCache flag - * @see #getAuthorshipCache() - */ - public boolean isProtectedAuthorshipCache() { - return protectedAuthorshipCache; - } - - /** - * @see #isProtectedAuthorshipCache() - * @see #getAuthorshipCache() - */ - public void setProtectedAuthorshipCache(boolean protectedAuthorshipCache) { - this.protectedAuthorshipCache = protectedAuthorshipCache; - if (protectedAuthorshipCache == false){ - if (! this.isProtectedFullTitleCache()){ - this.setFullTitleCache(null, false); - } - } - } - - - /** - * Returns the boolean value of the flag indicating whether the name of this - * botanical taxon name is a hybrid formula (true) or not (false). A hybrid - * named by a hybrid formula (composed with its parent names by placing the - * multiplication sign between them) does not have an own published name - * and therefore has neither an {@link NonViralName#getAuthorshipCache() autorship} - * nor other name components. If this flag is set no other hybrid flags may - * be set. - * - * @return the boolean value of the isHybridFormula flag - * @see #isMonomHybrid() - * @see #isBinomHybrid() - * @see #isTrinomHybrid() - */ - public boolean isHybridFormula(){ - return this.hybridFormula; - } - - /** - * @see #isHybridFormula() - */ - public void setHybridFormula(boolean hybridFormula){ - this.hybridFormula = hybridFormula; - } - - /** - * Returns the boolean value of the flag indicating whether this botanical - * taxon name is the name of an intergeneric hybrid (true) or not (false). - * In this case the multiplication sign is placed before the scientific - * name. If this flag is set no other hybrid flags may be set. - * - * @return the boolean value of the isMonomHybrid flag - * @see #isHybridFormula() - * @see #isBinomHybrid() - * @see #isTrinomHybrid() - */ - public boolean isMonomHybrid(){ - return this.monomHybrid; - } - - /** - * @see #isMonomHybrid() - * @see #isBinomHybrid() - * @see #isTrinomHybrid() - */ - public void setMonomHybrid(boolean monomHybrid){ - this.monomHybrid = monomHybrid; - } - - /** - * Returns the boolean value of the flag indicating whether this botanical - * taxon name is the name of an interspecific hybrid (true) or not (false). - * In this case the multiplication sign is placed before the species - * epithet. If this flag is set no other hybrid flags may be set. - * - * @return the boolean value of the isBinomHybrid flag - * @see #isHybridFormula() - * @see #isMonomHybrid() - * @see #isTrinomHybrid() - */ - public boolean isBinomHybrid(){ - return this.binomHybrid; - } - - /** - * @see #isBinomHybrid() - * @see #isMonomHybrid() - * @see #isTrinomHybrid() - */ - public void setBinomHybrid(boolean binomHybrid){ - this.binomHybrid = binomHybrid; - } - - /** - * Returns the boolean value of the flag indicating whether this botanical - * taxon name is the name of an infraspecific hybrid (true) or not (false). - * In this case the term "notho-" (optionally abbreviated "n-") is used as - * a prefix to the term denoting the infraspecific rank of this botanical - * taxon name. If this flag is set no other hybrid flags may be set. - * - * @return the boolean value of the isTrinomHybrid flag - * @see #isHybridFormula() - * @see #isMonomHybrid() - * @see #isBinomHybrid() - */ - public boolean isTrinomHybrid(){ - return this.trinomHybrid; - } - - /** - * @see #isTrinomHybrid() - * @see #isBinomHybrid() - * @see #isMonomHybrid() - */ - public void setTrinomHybrid(boolean trinomHybrid){ - this.trinomHybrid = trinomHybrid; - } - - /** - * Returns the set of all {@link HybridRelationship hybrid relationships} - * in which this taxon name is involved as a {@link common.RelationshipBase#getRelatedFrom() parent}. - * - * @see #getHybridRelationships() - * @see #getChildRelationships() - * @see HybridRelationshipType - */ - public Set getParentRelationships() { - return hybridParentRelations; - } - - /** - * Returns the set of all {@link HybridRelationship hybrid relationships} - * in which this taxon name is involved as a {@link common.RelationshipBase#getRelatedTo() child}. - * - * @see #getHybridRelationships() - * @see #getParentRelationships() - * @see HybridRelationshipType - */ - public Set getChildRelationships() { - return hybridChildRelations; - } - - /** - * Adds the given {@link HybridRelationship hybrid relationship} to the set - * of {@link #getHybridRelationships() hybrid relationships} of both non viral taxon names - * involved in this hybrid relationship. One of both non viral taxon names - * must be this botanical taxon name otherwise no addition will be carried - * out. The {@link eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo() child non viral taxon name} - * must be a hybrid, which means that one of its four hybrid flags must be set. - * - * @param relationship the hybrid relationship to be added - * @see #isHybridFormula() - * @see #isMonomHybrid() - * @see #isBinomHybrid() - * @see #isTrinomHybrid() - * @see #getHybridRelationships() - * @see #getParentRelationships() - * @see #getChildRelationships() - * @see #addRelationship(RelationshipBase) - * @throws IllegalArgumentException - */ - protected void addHybridRelationship(HybridRelationship rel) { - if (rel!=null && rel.getHybridName().equals(this)){ - this.hybridChildRelations.add(rel); - }else if(rel!=null && rel.getParentName().equals(this)){ - this.hybridParentRelations.add(rel); - }else{ - throw new IllegalArgumentException("Hybrid relationship is either null or the relationship does not reference this name"); - } - } - - - - /** - * Does the same as the addHybridRelationship method if the given - * {@link common.RelationshipBase relation} is also a {@link HybridRelationship hybrid relationship}. - * Otherwise this method does the same as the overwritten {@link TaxonNameBase#addRelationship(RelationshipBase) addRelationship} - * method from TaxonNameBase. - * - * @param relation the relationship to be added to some of this taxon name's relationships sets - * @see #addHybridRelationship(HybridRelationship) - * @see TaxonNameBase#addRelationship(RelationshipBase) - * @see TaxonNameBase#addNameRelationship(NameRelationship) - */ - @Override - @Deprecated //To be used by RelationshipBase only - public void addRelationship(RelationshipBase relation) { - if (relation instanceof HybridRelationship){ - addHybridRelationship((HybridRelationship)relation); - }else { - super.addRelationship(relation); - } - } - - /** - * Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship} - * to this botanical name. A HybridRelationship may be of type - * "is first/second parent" or "is male/female parent". By invoking this - * method this botanical name becomes a hybrid child of the parent - * botanical name. - * - * @param parentName the botanical name of the parent for this new hybrid name relationship - * @param type the type of this new name relationship - * @param ruleConsidered the string which specifies the rule on which this name relationship is based - * @see #addHybridChild(BotanicalName, HybridRelationshipType,String ) - * @see #getRelationsToThisName() - * @see #getNameRelations() - * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String) - * @see #addNameRelationship(NameRelationship) - */ - public void addHybridParent(NonViralName parentName, HybridRelationshipType type, String ruleConsidered){ - HybridRelationship rel = new HybridRelationship(this, parentName, type, ruleConsidered); - } - - /** - * Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship} - * to this botanical name. A HybridRelationship may be of type - * "is first/second parent" or "is male/female parent". By invoking this - * method this botanical name becomes a parent of the hybrid child - * botanical name. - * - * @param childName the botanical name of the child for this new hybrid name relationship - * @param type the type of this new name relationship - * @param ruleConsidered the string which specifies the rule on which this name relationship is based - * @see #addHybridParent(BotanicalName, HybridRelationshipType,String ) - * @see #getRelationsToThisName() - * @see #getNameRelations() - * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String) - * @see #addNameRelationship(NameRelationship) - */ - public void addHybridChild(NonViralName childName, HybridRelationshipType type, String ruleConsidered){ - HybridRelationship rel = new HybridRelationship(childName, this, type, ruleConsidered); - } - - - /** - * Removes one {@link HybridRelationship hybrid relationship} from the set of - * {@link #getHybridRelationships() hybrid relationships} in which this botanical taxon name - * is involved. The hybrid relationship will also be removed from the set - * belonging to the second botanical taxon name involved. - * - * @param relationship the hybrid relationship which should be deleted from the corresponding sets - * @see #getHybridRelationships() - */ - public void removeHybridRelationship(HybridRelationship hybridRelation) { - if (hybridRelation == null) { - return; - } - - NonViralName parent = hybridRelation.getParentName(); - NonViralName child = hybridRelation.getHybridName(); - - hybridRelation.setHybridName(null); - hybridRelation.setParentName(null); - - if (parent != null) { - parent.removeHybridRelationship(hybridRelation); - } - - if (child != null) { - child.removeHybridRelationship(hybridRelation); - } - - this.hybridChildRelations.remove(hybridRelation); - this.hybridParentRelations.remove(hybridRelation); - } - - - public void removeHybridChild(NonViralName child) { - Set hybridRelationships = new HashSet(); - hybridRelationships.addAll(this.getChildRelationships()); - hybridRelationships.addAll(this.getParentRelationships()); - for(HybridRelationship hybridRelationship : hybridRelationships) { - // remove name relationship from this side - if (hybridRelationship.getParentName().equals(this) && hybridRelationship.getHybridName().equals(child)) { - this.removeHybridRelationship(hybridRelationship); - } - } - } - - public void removeHybridParent(NonViralName parent) { - Set hybridRelationships = new HashSet(); - hybridRelationships.addAll(this.getChildRelationships()); - hybridRelationships.addAll(this.getParentRelationships()); - for(HybridRelationship hybridRelationship : hybridRelationships) { - // remove name relationship from this side - if (hybridRelationship.getParentName().equals(parent) && hybridRelationship.getHybridName().equals(this)) { - this.removeHybridRelationship(hybridRelationship); - } - } - } + @Override + public String generateFullTitle(){ + if (cacheStrategy == null){ + logger.warn("No CacheStrategy defined for nonViralName: " + this.getUuid()); + return null; + }else{ + return cacheStrategy.getFullTitleCache(this); + } + } + + /** + * Generates the composed name string of this non viral taxon name without author + * strings or year according to the strategy defined in + * {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy INonViralNameCacheStrategy}. + * The result might be stored in {@link #getNameCache() nameCache} if the + * flag {@link #isProtectedNameCache() protectedNameCache} is not set. + * + * @return the string with the composed name of this non viral taxon name without authors or year + * @see #getNameCache() + */ + protected String generateNameCache(){ + if (cacheStrategy == null){ + logger.warn("No CacheStrategy defined for taxonName: " + this.toString()); + return null; + }else{ + return cacheStrategy.getNameCache(this); + } + } + + /** + * Returns or generates the nameCache (scientific name + * without author strings and year) string for this non viral taxon name. If the + * {@link #isProtectedNameCache() protectedNameCache} flag is not set (False) + * the string will be generated according to a defined strategy, + * otherwise the value of the actual nameCache string will be returned. + * + * @return the string which identifies this non viral taxon name (without authors or year) + * @see #generateNameCache() + */ + @Transient + public String getNameCache() { + if (protectedNameCache){ + return this.nameCache; + } + // is title dirty, i.e. equal NULL? + if (nameCache == null){ + this.nameCache = generateNameCache(); + } + return nameCache; + } + + /** + * Assigns a nameCache string to this non viral taxon name and protects it from being overwritten. + * Sets the protectedNameCache flag to true. + * + * @param nameCache the string which identifies this non viral taxon name (without authors or year) + * @see #getNameCache() + */ + public void setNameCache(String nameCache){ + setNameCache(nameCache, true); + } + + /** + * Assigns a nameCache string to this non viral taxon name and protects it from being overwritten. + * Sets the protectedNameCache flag to true. + * + * @param nameCache the string which identifies this non viral taxon name (without authors or year) + * @param protectedNameCache if true teh protectedNameCache is set to true or otherwise set to + * false + * @see #getNameCache() + */ + public void setNameCache(String nameCache, boolean protectedNameCache){ + this.nameCache = nameCache; + this.setProtectedNameCache(protectedNameCache); + } + + /** + * Returns the boolean value of the flag intended to protect (true) + * or not (false) the {@link #getNameCache() nameCache} (scientific name without author strings and year) + * string of this non viral taxon name. + * + * @return the boolean value of the protectedNameCache flag + * @see #getNameCache() + */ + public boolean isProtectedNameCache() { + return protectedNameCache; + } + + /** + * @see #isProtectedNameCache() + */ + public void setProtectedNameCache(boolean protectedNameCache) { + this.protectedNameCache = protectedNameCache; + } + + + /** + * Generates and returns a concatenated and formated authorteams string + * including basionym and combination authors of this non viral taxon name + * according to the strategy defined in + * {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(NonViralName) INonViralNameCacheStrategy}. + * + * @return the string with the concatenated and formated authorteams for this non viral taxon name + * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(NonViralName) + */ + public String generateAuthorship(){ + if (cacheStrategy == null){ + logger.warn("No CacheStrategy defined for nonViralName: " + this.getUuid()); + return null; + }else{ + return ((INonViralNameCacheStrategy)cacheStrategy).getAuthorshipCache((T)this); + } + } + + /** + * Returns the concatenated and formated authorteams string including + * basionym and combination authors of this non viral taxon name. + * If the protectedAuthorshipCache flag is set this method returns the + * string stored in the the authorshipCache attribute, otherwise it + * generates the complete authorship string, returns it and stores it in + * the authorshipCache attribute. + * + * @return the string with the concatenated and formated authorteams for this non viral taxon name + * @see #generateAuthorship() + */ + @Transient + public String getAuthorshipCache() { + if (protectedAuthorshipCache){ + return this.authorshipCache; + } + if (this.authorshipCache == null ){ + this.authorshipCache = generateAuthorship(); + }else{ + //TODO get is Dirty of authors, make better if possible + this.setAuthorshipCache(generateAuthorship(), protectedAuthorshipCache); //throw change event to inform higher caches + + } + return authorshipCache; + } + + + /** + * Updates the authorship cache if any changes appeared in the authors nomenclatural caches. + * Deletes the titleCache and the fullTitleCache if not protected and if any change has happened + * @return + */ + private void updateAuthorshipCache() { + //updates the authorship cache if necessary and via the listener updates all higher caches + if (protectedAuthorshipCache == false){ + String oldCache = this.authorshipCache; + String newCache = this.getAuthorshipCache(); + if ( (oldCache == null && newCache != null) || CdmUtils.nullSafeEqual(oldCache,newCache)){ + this.setAuthorshipCache(this.getAuthorshipCache(), false); + } + } + } + + /** + * Assigns an authorshipCache string to this non viral taxon name. Sets the isProtectedAuthorshipCache + * flag to true. + * + * @param authorshipCache the string which identifies the complete authorship of this non viral taxon name + * @see #getAuthorshipCache() + */ + public void setAuthorshipCache(String authorshipCache) { + setAuthorshipCache(authorshipCache, true); + } + + @Override + @Transient + public String getFullTitleCache(){ + updateAuthorshipCache(); + return super.getFullTitleCache(); + } + + @Override + public String getTitleCache(){ + if(!protectedTitleCache) { + updateAuthorshipCache(); + } + + return super.getTitleCache(); + } + + + /** + * Assigns an authorshipCache string to this non viral taxon name. + * + * @param authorshipCache the string which identifies the complete authorship of this non viral taxon name + * @param protectedAuthorshipCache if true the isProtectedAuthorshipCache flag is set to true, otherwise + * the flag is set to false. + * @see #getAuthorshipCache() + */ + public void setAuthorshipCache(String authorshipCache, boolean protectedAuthorshipCache) { + this.authorshipCache = authorshipCache; + this.setProtectedAuthorshipCache(protectedAuthorshipCache); + } + + @Override + public void setTitleCache(String titleCache, boolean protectCache){ + super.setTitleCache(titleCache, protectCache); + } + + /** + * Returns the boolean value "false" since the components of this taxon name + * cannot follow the rules of a corresponding {@link NomenclaturalCode nomenclatural code} + * which is not defined for this class. The nomenclature code depends on + * the concrete name subclass ({@link BacterialName BacterialName}, + * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} or + * {@link ZoologicalName ZoologicalName} to which this non viral taxon name belongs. + * This method overrides the isCodeCompliant method from the abstract + * {@link TaxonNameBase#isCodeCompliant() TaxonNameBase} class. + * + * @return false + * @see TaxonNameBase#isCodeCompliant() + */ + @Override + @Transient + public boolean isCodeCompliant() { + //FIXME + logger.warn("is CodeCompliant not yet implemented"); + return false; + } + + /* (non-Javadoc) + * @see eu.etaxonomy.cdm.model.name.TaxonNameBase#getNomeclaturalCode() + */ + /** + * Returns null as {@link NomenclaturalCode nomenclatural code} that governs + * the construction of this non viral taxon name since there is no specific + * nomenclatural code defined. The real implementention takes place in the + * subclasses {@link BacterialName BacterialName}, + * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and + * {@link ZoologicalName ZoologicalName}. + * This method overrides the getNomeclaturalCode method from {@link TaxonNameBase TaxonNameBase}. + * + * @return null + * @see #isCodeCompliant() + * @see TaxonNameBase#getHasProblem() + */ + @Override + @Transient + public NomenclaturalCode getNomenclaturalCode() { + logger.warn("Non Viral Name has no specific Code defined. Use subclasses"); + return null; + } + + /** + * Returns the boolean value of the flag intended to protect (true) + * or not (false) the {@link #getAuthorshipCache() authorshipCache} (complete authorship string) + * of this non viral taxon name. + * + * @return the boolean value of the protectedAuthorshipCache flag + * @see #getAuthorshipCache() + */ + public boolean isProtectedAuthorshipCache() { + return protectedAuthorshipCache; + } + + /** + * @see #isProtectedAuthorshipCache() + * @see #getAuthorshipCache() + */ + public void setProtectedAuthorshipCache(boolean protectedAuthorshipCache) { + this.protectedAuthorshipCache = protectedAuthorshipCache; + } + + + /** + * Returns the boolean value of the flag indicating whether the name of this + * botanical taxon name is a hybrid formula (true) or not (false). A hybrid + * named by a hybrid formula (composed with its parent names by placing the + * multiplication sign between them) does not have an own published name + * and therefore has neither an {@link NonViralName#getAuthorshipCache() autorship} + * nor other name components. If this flag is set no other hybrid flags may + * be set. + * + * @return the boolean value of the isHybridFormula flag + * @see #isMonomHybrid() + * @see #isBinomHybrid() + * @see #isTrinomHybrid() + */ + public boolean isHybridFormula(){ + return this.hybridFormula; + } + + /** + * @see #isHybridFormula() + */ + public void setHybridFormula(boolean hybridFormula){ + this.hybridFormula = hybridFormula; + } + + /** + * Returns the boolean value of the flag indicating whether this botanical + * taxon name is the name of an intergeneric hybrid (true) or not (false). + * In this case the multiplication sign is placed before the scientific + * name. If this flag is set no other hybrid flags may be set. + * + * @return the boolean value of the isMonomHybrid flag + * @see #isHybridFormula() + * @see #isBinomHybrid() + * @see #isTrinomHybrid() + */ + public boolean isMonomHybrid(){ + return this.monomHybrid; + } + + /** + * @see #isMonomHybrid() + * @see #isBinomHybrid() + * @see #isTrinomHybrid() + */ + public void setMonomHybrid(boolean monomHybrid){ + this.monomHybrid = monomHybrid; + } + + /** + * Returns the boolean value of the flag indicating whether this botanical + * taxon name is the name of an interspecific hybrid (true) or not (false). + * In this case the multiplication sign is placed before the species + * epithet. If this flag is set no other hybrid flags may be set. + * + * @return the boolean value of the isBinomHybrid flag + * @see #isHybridFormula() + * @see #isMonomHybrid() + * @see #isTrinomHybrid() + */ + public boolean isBinomHybrid(){ + return this.binomHybrid; + } + + /** + * @see #isBinomHybrid() + * @see #isMonomHybrid() + * @see #isTrinomHybrid() + */ + public void setBinomHybrid(boolean binomHybrid){ + this.binomHybrid = binomHybrid; + } + + /** + * Returns the boolean value of the flag indicating whether this botanical + * taxon name is the name of an infraspecific hybrid (true) or not (false). + * In this case the term "notho-" (optionally abbreviated "n-") is used as + * a prefix to the term denoting the infraspecific rank of this botanical + * taxon name. If this flag is set no other hybrid flags may be set. + * + * @return the boolean value of the isTrinomHybrid flag + * @see #isHybridFormula() + * @see #isMonomHybrid() + * @see #isBinomHybrid() + */ + public boolean isTrinomHybrid(){ + return this.trinomHybrid; + } + + /** + * @see #isTrinomHybrid() + * @see #isBinomHybrid() + * @see #isMonomHybrid() + */ + public void setTrinomHybrid(boolean trinomHybrid){ + this.trinomHybrid = trinomHybrid; + } + + + /** + * Returns the set of all {@link HybridRelationship hybrid relationships} + * in which this taxon name is involved as a {@link common.RelationshipBase#getRelatedFrom() parent}. + * + * @see #getHybridRelationships() + * @see #getChildRelationships() + * @see HybridRelationshipType + */ + public Set getHybridParentRelations() { + if(hybridParentRelations == null) { + this.hybridParentRelations = new HashSet(); + } + return hybridParentRelations; + } + + private void setHybridParentRelations(Set hybridParentRelations) { + this.hybridParentRelations = hybridParentRelations; + } + + + /** + * Returns the set of all {@link HybridRelationship hybrid relationships} + * in which this taxon name is involved as a {@link common.RelationshipBase#getRelatedTo() child}. + * + * @see #getHybridRelationships() + * @see #getParentRelationships() + * @see HybridRelationshipType + */ + public Set getHybridChildRelations() { + if(hybridChildRelations == null) { + this.hybridChildRelations = new HashSet(); + } + return hybridChildRelations; + } + + private void setHybridChildRelations(Set hybridChildRelations) { + this.hybridChildRelations = hybridChildRelations; + } + + /** + * Returns the set of all {@link HybridRelationship hybrid relationships} + * in which this taxon name is involved as a {@link common.RelationshipBase#getRelatedFrom() parent}. + * @see #getHybridParentRelations() + * @see #getHybridRelationships() + * @see #getChildRelationships() + * @see HybridRelationshipType + * @deprecated use {@link #getHybridParentRelations()} instead. Will be removed in higher versions. + */ + @Deprecated + @Transient + public Set getParentRelationships() { + return getHybridParentRelations(); + } + + /** + * Returns the hybrid child relationships ordered by relationship type, or if equal + * by title cache of the related names. + * @see #getHybridParentRelations() + */ + @Transient + public List getOrderedChildRelationships(){ + List result = new ArrayList(); + result.addAll(this.hybridChildRelations); + Collections.sort(result); + Collections.reverse(result); + return result; + + } + + + /** + * @see #getHybridChildRelations() + * @deprecated use {@link #getHybridChildRelations()} instead. Will be removed in higher versions. + */ + @Transient + @Deprecated + public Set getChildRelationships() { + return this.getHybridChildRelations(); + } + + /** + * Adds the given {@link HybridRelationship hybrid relationship} to the set + * of {@link #getHybridRelationships() hybrid relationships} of both non-viral names + * involved in this hybrid relationship. One of both non-viral names + * must be this non-viral name otherwise no addition will be carried + * out. The {@link eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo() child + * non viral taxon name} must be a hybrid, which means that one of its four hybrid flags must be set. + * + * @param relationship the hybrid relationship to be added + * @see #isHybridFormula() + * @see #isMonomHybrid() + * @see #isBinomHybrid() + * @see #isTrinomHybrid() + * @see #getHybridRelationships() + * @see #getParentRelationships() + * @see #getChildRelationships() + * @see #addRelationship(RelationshipBase) + * @throws IllegalArgumentException + */ + protected void addHybridRelationship(HybridRelationship rel) { + if (rel!=null && rel.getHybridName().equals(this)){ + this.hybridChildRelations.add(rel); + }else if(rel!=null && rel.getParentName().equals(this)){ + this.hybridParentRelations.add(rel); + }else{ + throw new IllegalArgumentException("Hybrid relationship is either null or the relationship does not reference this name"); + } + } + + + + /** + * Does the same as the addHybridRelationship method if the given + * {@link common.RelationshipBase relation} is also a {@link HybridRelationship hybrid relationship}. + * Otherwise this method does the same as the overwritten {@link TaxonNameBase#addRelationship(RelationshipBase) addRelationship} + * method from TaxonNameBase. + * + * @param relation the relationship to be added to some of this taxon name's relationships sets + * @see #addHybridRelationship(HybridRelationship) + * @see TaxonNameBase#addRelationship(RelationshipBase) + * @see TaxonNameBase#addNameRelationship(NameRelationship) + * @deprecated to be used by RelationshipBase only + */ + @Override + @Deprecated //to be used by RelationshipBase only + public void addRelationship(RelationshipBase relation) { + if (relation instanceof HybridRelationship){ + addHybridRelationship((HybridRelationship)relation); + }else { + super.addRelationship(relation); + } + } + + /** + * Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship} + * to this botanical name. A HybridRelationship may be of type + * "is first/second parent" or "is male/female parent". By invoking this + * method this botanical name becomes a hybrid child of the parent + * botanical name. + * + * @param parentName the botanical name of the parent for this new hybrid name relationship + * @param type the type of this new name relationship + * @param ruleConsidered the string which specifies the rule on which this name relationship is based + * @return + * @see #addHybridChild(BotanicalName, HybridRelationshipType,String ) + * @see #getRelationsToThisName() + * @see #getNameRelations() + * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String) + * @see #addNameRelationship(NameRelationship) + */ + public HybridRelationship addHybridParent(NonViralName parentName, HybridRelationshipType type, String ruleConsidered){ + return new HybridRelationship(this, parentName, type, ruleConsidered); + } + + /** + * Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship} + * to this botanical name. A HybridRelationship may be of type + * "is first/second parent" or "is male/female parent". By invoking this + * method this botanical name becomes a parent of the hybrid child + * botanical name. + * + * @param childName the botanical name of the child for this new hybrid name relationship + * @param type the type of this new name relationship + * @param ruleConsidered the string which specifies the rule on which this name relationship is based + * @return + * @see #addHybridParent(BotanicalName, HybridRelationshipType,String ) + * @see #getRelationsToThisName() + * @see #getNameRelations() + * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String) + * @see #addNameRelationship(NameRelationship) + */ + public HybridRelationship addHybridChild(NonViralName childName, HybridRelationshipType type, String ruleConsidered){ + return new HybridRelationship(childName, this, type, ruleConsidered); + } + + + /** + * Removes one {@link HybridRelationship hybrid relationship} from the set of + * {@link #getHybridRelationships() hybrid relationships} in which this botanical taxon name + * is involved. The hybrid relationship will also be removed from the set + * belonging to the second botanical taxon name involved. + * + * @param relationship the hybrid relationship which should be deleted from the corresponding sets + * @see #getHybridRelationships() + */ + public void removeHybridRelationship(HybridRelationship hybridRelation) { + if (hybridRelation == null) { + return; + } + + NonViralName parent = hybridRelation.getParentName(); + NonViralName child = hybridRelation.getHybridName(); + + hybridRelation.setHybridName(null); + hybridRelation.setParentName(null); + + if (parent != null) { + parent.removeHybridRelationship(hybridRelation); + } + + if (child != null) { + child.removeHybridRelationship(hybridRelation); + } + + this.hybridChildRelations.remove(hybridRelation); + this.hybridParentRelations.remove(hybridRelation); + } + + + public void removeHybridChild(NonViralName child) { + Set hybridRelationships = new HashSet(); + hybridRelationships.addAll(this.getChildRelationships()); + hybridRelationships.addAll(this.getParentRelationships()); + for(HybridRelationship hybridRelationship : hybridRelationships) { + // remove name relationship from this side + if (hybridRelationship.getParentName().equals(this) && hybridRelationship.getHybridName().equals(child)) { + this.removeHybridRelationship(hybridRelationship); + } + } + } + + public void removeHybridParent(NonViralName parent) { + Set hybridRelationships = new HashSet(); + hybridRelationships.addAll(this.getChildRelationships()); + hybridRelationships.addAll(this.getParentRelationships()); + for(HybridRelationship hybridRelationship : hybridRelationships) { + // remove name relationship from this side + if (hybridRelationship.getParentName().equals(parent) && hybridRelationship.getHybridName().equals(this)) { + this.removeHybridRelationship(hybridRelationship); + } + } + } + + /** + * Needs to be implemented by those classes that handle autonyms (e.g. botanical names). + **/ + @Transient + public boolean isAutonym(){ + return false; + } + + +// /** +// * Returns the boolean value indicating whether this names rank is Rank "unranked" +// * (uuid = 'a965befb-70a9-4747-a18f-624456c65223') but most likely it is an infrageneric rank +// * due to existing atomized data for the genus epithet and the infrageneric epithet but missing +// * specific epithet. +// * Returns false if this names rank is null. +// * +// * @see #isSupraGeneric() +// * @see #isGenus() +// * @see #isSpeciesAggregate() +// * @see #isSpecies() +// * @see #isInfraSpecific() +// */ +// @Transient +// public boolean isInfragenericUnranked() { +// Rank rank = this.getRank(); +// if (rank == null || ! rank.equals(Rank.UNRANKED())){ +// return false; +// } +// if (StringUtils.isBlank(this.getSpecificEpithet()) && StringUtils.isBlank(this.getInfraSpecificEpithet()) ){ +// return true; +// }else{ +// return false; +// } +// } + + + /** + * Tests if the given name has any authors. + * @return false if no author ((ex)combination or (ex)basionym) exists, true otherwise + */ + public boolean hasAuthors() { + return (this.getCombinationAuthorTeam() != null || + this.getExCombinationAuthorTeam() != null || + this.getBasionymAuthorTeam() != null || + this.getExBasionymAuthorTeam() != null); + } + + /** + * Shortcut. Returns the combination authors title cache. Returns null if no combination author exists. + * @return + */ + public String computeCombinationAuthorNomenclaturalTitle() { + return computeNomenclaturalTitle(this.getCombinationAuthorTeam()); + } + + /** + * Shortcut. Returns the basionym authors title cache. Returns null if no basionym author exists. + * @return + */ + public String computeBasionymAuthorNomenclaturalTitle() { + return computeNomenclaturalTitle(this.getBasionymAuthorTeam()); + } + + + /** + * Shortcut. Returns the ex-combination authors title cache. Returns null if no ex-combination author exists. + * @return + */ + public String computeExCombinationAuthorNomenclaturalTitle() { + return computeNomenclaturalTitle(this.getExCombinationAuthorTeam()); + } + + /** + * Shortcut. Returns the ex-basionym authors title cache. Returns null if no exbasionym author exists. + * @return + */ + public String computeExBasionymAuthorNomenclaturalTitle() { + return computeNomenclaturalTitle(this.getExBasionymAuthorTeam()); + } + + private String computeNomenclaturalTitle(INomenclaturalAuthor author){ + if (author == null){ + return null; + }else{ + return author.getNomenclaturalTitle(); + } + } + +//*********************** CLONE ********************************************************/ + + /** + * Clones this non-viral name. This is a shortcut that enables to create + * a new instance that differs only slightly from this non-viral name by + * modifying only some of the attributes. + * + * @see eu.etaxonomy.cdm.model.name.TaxonNameBase#clone() + * @see java.lang.Object#clone() + */ + @Override + public Object clone() { + NonViralName result = (NonViralName)super.clone(); + + //HybridChildRelations + result.hybridChildRelations = new HashSet(); + for (HybridRelationship hybridRelationship : getHybridChildRelations()){ + HybridRelationship newChildRelationship = (HybridRelationship)hybridRelationship.clone(); + newChildRelationship.setRelatedTo(result); + result.hybridChildRelations.add(newChildRelationship); + } + + //HybridParentRelations + result.hybridParentRelations = new HashSet(); + for (HybridRelationship hybridRelationship : getHybridParentRelations()){ + HybridRelationship newParentRelationship = (HybridRelationship)hybridRelationship.clone(); + newParentRelationship.setRelatedFrom(result); + result.hybridParentRelations.add(newParentRelationship); + } + + //empty caches + if (! protectedNameCache){ + result.nameCache = null; + } + + //empty caches + if (! protectedAuthorshipCache){ + result.authorshipCache = null; + } + //no changes to: basionamyAuthorTeam, combinationAuthorTeam, exBasionymAuthorTeam, exCombinationAuthorTeam + //genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet, + //protectedAuthorshipCache, protectedNameCache + //binomHybrid, monomHybrid, trinomHybrid, hybridFormula, + return result; + } }