X-Git-Url: https://dev.e-taxonomy.eu/gitweb/cdmlib.git/blobdiff_plain/33f9b501bdb2e894f4e57b280a932717eb14525b..e17b2b9fde8c2b9f8dee72b5626fcd56eab2ebf0:/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/HomotypicalGroup.java diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/HomotypicalGroup.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/HomotypicalGroup.java index b43933e576..affd0055fe 100644 --- a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/HomotypicalGroup.java +++ b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/HomotypicalGroup.java @@ -10,97 +10,572 @@ package eu.etaxonomy.cdm.model.name; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.OneToMany; import javax.persistence.Transient; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlIDREF; +import javax.xml.bind.annotation.XmlSchemaType; +import javax.xml.bind.annotation.XmlType; import org.apache.log4j.Logger; -import org.hibernate.annotations.Cascade; -import org.hibernate.annotations.CascadeType; +import org.hibernate.envers.Audited; import eu.etaxonomy.cdm.model.common.AnnotatableEntity; -import eu.etaxonomy.cdm.model.occurrence.Specimen; -import eu.etaxonomy.cdm.model.reference.ReferenceBase; +import eu.etaxonomy.cdm.model.reference.Reference; import eu.etaxonomy.cdm.model.taxon.Synonym; -import eu.etaxonomy.cdm.model.taxon.SynonymRelationship; -import eu.etaxonomy.cdm.model.taxon.TaxonBase; +import eu.etaxonomy.cdm.model.taxon.TaxonComparator; + /** - * A homotypical group represents all names that share the same type specimens. - * This also includes supergeneric names like genera or families which usually have a name type designation - * that finally (a name type designation can also point to another supergeneric name) points to a species name, - * which in turn has a (set of) physical type specimen(s). - * @author m.doering - * + * The homotypical group class represents a set of {@link TaxonNameBase taxon names} associated + * on the base of their typifications. Since it can be asserted that two taxon + * names are typified by the same type without mentioning the type itself, even + * taxon names without explicit {@link TypeDesignationBase type designation} can belong + * to an homotypical group.
+ * Taxon names belonging to an homotypical group and the taxon names or + * {@link eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase specimens} used as types for their + * {@link TypeDesignationBase type designations} have the following properties: + * + * @see TypeDesignationBase + * @see NameTypeDesignation + * @see SpecimenTypeDesignation + * @author m.doering + * @version 1.0 + * @created 08-Nov-2007 */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "HomotypicalGroup", propOrder = { + "typifiedNames" +}) @Entity +@Audited public class HomotypicalGroup extends AnnotatableEntity { - static Logger logger = Logger.getLogger(HomotypicalGroup.class); + private static final Logger logger = Logger.getLogger(HomotypicalGroup.class); - protected Set typifiedNames = new HashSet(); - protected Set typeDesignations = new HashSet(); + @XmlElementWrapper(name = "TypifiedNames") + @XmlElement(name = "TypifiedName") + @XmlIDREF + @XmlSchemaType(name = "IDREF") + @OneToMany(mappedBy="homotypicalGroup", fetch=FetchType.LAZY) + protected Set typifiedNames = new HashSet(); - public HomotypicalGroup() { - super(); +// ******************** static methods **************************************/ + /** + * Creates a new homotypical group instance with an empty set of typified + * {@link TaxonNameBase taxon names}. + * + * @see #HomotypicalGroup() + */ + public static HomotypicalGroup NewInstance(){ + return new HomotypicalGroup(); } - @OneToMany +//********************** CONSTRUCTOR ********************************************/ + + /** + * Class constructor: creates a new homotypical group instance with an + * empty set of typified {@link TaxonNameBase taxon names}. + */ + public HomotypicalGroup() { + super(); + } + +// ********************** GETTER/SETTER/ADDER/REMOVER ********************************/ + + /** + * Returns the set of {@link TaxonNameBase taxon names} that belong to this homotypical group. + * + * @see #getSpecimenTypeDesignations() + */ public Set getTypifiedNames() { return typifiedNames; } - protected void setTypifiedNames(Set typifiedNames) { - this.typifiedNames = typifiedNames; - } + + /** + * Adds a new {@link TaxonNameBase taxon name} to the set of taxon names that belong + * to this homotypical group. + * + * @param typifiedName the taxon name to be added to this group + * @see #getTypifiedNames() + * @see #removeTypifiedName(TaxonNameBase) + */ public void addTypifiedName(TaxonNameBase typifiedName) { - typifiedName.setHomotypicalGroup(this); + if (typifiedName != null){ + typifiedNames.add(typifiedName); + typifiedName.setHomotypicalGroup(this); + } } + /** + * Removes one element from the set of {@link TaxonNameBase taxon names} + * that belong to this homotypical group. + * + * @param taxonBase the taxon name which should be removed from the corresponding set + * @see #addTypifiedName(TaxonNameBase) + */ public void removeTypifiedName(TaxonNameBase typifiedName) { - typifiedName.setHomotypicalGroup(null); + HomotypicalGroup newHomotypicalGroup = HomotypicalGroup.NewInstance(); + typifiedName.setHomotypicalGroup(newHomotypicalGroup); + typifiedNames.remove(typifiedName); } + /** + * Merges the typified {@link TaxonNameBase taxon names} from one homotypical group into + * the set of typified taxon names of this homotypical group. + * + * @param homotypicalGroupToMerge the homotypical group the typified names of which + * are to be transferred to this homotypical group + */ + public void merge(HomotypicalGroup homotypicalGroupToMerge){ + if (homotypicalGroupToMerge != null){ + Set typifiedNames = new HashSet(); + typifiedNames.addAll(homotypicalGroupToMerge.getTypifiedNames()); + for (TaxonNameBase typifiedName: typifiedNames){ + this.addTypifiedName(typifiedName); + } + } + } - @OneToMany - @Cascade({CascadeType.SAVE_UPDATE}) - public Set getTypeDesignations() { - return typeDesignations; + + /** + * Returns the set of {@link SpecimenTypeDesignation specimen type designations} that + * typify the {@link TaxonNameBase taxon names} belonging to this homotypical group + * including the status of these designations. + * + * @see #getTypifiedNames() + * @see #getNameTypeDesignations() + * @see #getTypeDesignations() + * @see TaxonNameBase#getSpecimenTypeDesignations() + */ + @Transient + public Set getSpecimenTypeDesignations(){ + Set result = new HashSet(); + for (TaxonNameBase taxonName : typifiedNames){ + result.addAll(taxonName.getSpecimenTypeDesignations()); + } + return result; } - protected void setTypeDesignations(Set typeDesignations) { - this.typeDesignations = typeDesignations; + + /** + * Returns the set of {@link NameTypeDesignation name type designations} that + * typify the {@link TaxonNameBase taxon names} belonging to this homotypical group + * including the status of these designations. + * + * @see #getTypifiedNames() + * @see #getSpecimenTypeDesignations() + * @see #getTypeDesignations() + * @see TaxonNameBase#getNameTypeDesignations() + */ + @Transient + public Set getNameTypeDesignations(){ + Set result = new HashSet(); + for (TaxonNameBase taxonName : typifiedNames){ + result.addAll(taxonName.getNameTypeDesignations()); + } + return result; + } + + + /** + * Returns the set of all {@link TypeDesignationBase type designations} that + * typify the {@link TaxonNameBase taxon names} belonging to this homotypical group + * (this includes either {@link NameTypeDesignation name type designations} or + * {@link SpecimenTypeDesignation specimen type designations}). + * + * @see #getTypifiedNames() + * @see #getNameTypeDesignations() + * @see #getSpecimenTypeDesignations() + * @see TaxonNameBase#getTypeDesignations() + */ + @Transient + public Set getTypeDesignations(){ + Set result = new HashSet(); + for (TaxonNameBase taxonName : typifiedNames){ + result.addAll(taxonName.getTypeDesignations()); + } + return result; + } + +// /** +// * Returns the set of {@link SpecimenTypeDesignation specimen type designations} that +// * typify this homotypical group including the status of these designations. +// * +// * @see #getTypifiedNames() +// */ +// @OneToMany +// @Cascade({CascadeType.SAVE_UPDATE}) +// public Set getSpecimenTypeDesignations() { +// return specimenTypeDesignations; +// } +// /** +// * @see #getSpecimenTypeDesignations() +// */ +// protected void setSpecimenTypeDesignations(Set specimenTypeDesignations) { +// this.specimenTypeDesignations = specimenTypeDesignations; +// } +// /** +// * Adds a new {@link SpecimenTypeDesignation specimen type designation} to the set +// * of specimen type designations assigned to this homotypical group and eventually +// * (with a boolean parameter) also to the corresponding set of each of the +// * {@link TaxonNameBase taxon names} belonging to this homotypical group. +// * +// * @param specimenTypeDesignation the specimen type designation to be added +// * @param addToAllNames the boolean flag indicating whether the addition will also +// * carried out for each taxon name +// * +// * @see TaxonNameBase#getSpecimenTypeDesignations() +// * @see SpecimenTypeDesignation +// */ +// public void addSpecimenTypeDesignation(SpecimenTypeDesignation specimenTypeDesignation, boolean addToAllNames) { +// if (specimenTypeDesignation != null){ +// specimenTypeDesignation.setHomotypicalGroup(this); +// specimenTypeDesignations.add(specimenTypeDesignation); +// } +// if (addToAllNames){ +// for (TaxonNameBase taxonNameBase : this.typifiedNames){ +// taxonNameBase.addSpecimenTypeDesignation(specimenTypeDesignation); +// } +// } +// } +// /** +// * Removes one element from the set of {@link SpecimenTypeDesignation specimen type designations} assigned to the +// * {@link HomotypicalGroup homotypical group} to which this {@link TaxonNameBase taxon name} belongs. +// * The same element will be removed from the corresponding set of each of +// * the taxon names belonging to this homotypical group. Furthermore the +// * homotypical group attribute of the specimen type designation will be +// * nullified. +// * +// * @param specimenTypeDesignation the specimen type designation which should be deleted +// * @see #getSpecimenTypeDesignations() +// * @see #addSpecimenTypeDesignation(SpecimenTypeDesignation, boolean) +// * @see TaxonNameBase#removeSpecimenTypeDesignation(SpecimenTypeDesignation) +// * @see SpecimenTypeDesignation#getHomotypicalGroup() +// */ +// public void removeSpecimenTypeDesignation(SpecimenTypeDesignation specimenTypeDesignation) { +// if (specimenTypeDesignation != null){ +// specimenTypeDesignation.setHomotypicalGroup(null); +// specimenTypeDesignations.remove(specimenTypeDesignation); +// } +// for (TaxonNameBase taxonNameBase : this.typifiedNames){ +// taxonNameBase.removeSpecimenTypeDesignation(specimenTypeDesignation); +// } +// } + + +// /** +// * Returns the set of {@link NameTypeDesignation name type designations} that +// * typify this homotypical group including the status of these designations. +// * +// * @see #getTypifiedNames() +// */ +// @OneToMany +// @Cascade({CascadeType.SAVE_UPDATE}) +// public Set getNameTypeDesignations() { +// return nameTypeDesignations; +// } +// /** +// * @see #getNameTypeDesignations() +// */ +// protected void setNameTypeDesignations(Set nameTypeDesignations) { +// this.nameTypeDesignations = nameTypeDesignations; +// } +// /** +// * Adds a new {@link NameTypeDesignation name type designation} to the set +// * of name type designations assigned to this homotypical group and eventually +// * (with a boolean parameter) also to the corresponding set of each of the +// * {@link TaxonNameBase taxon names} belonging to this homotypical group. +// * +// * @param nameTypeDesignation the name type designation to be added +// * @param addToAllNames the boolean flag indicating whether the addition will also +// * carried out for each taxon name +// * +// * @see TaxonNameBase#getNameTypeDesignations() +// * @see NameTypeDesignation +// */ +// public void addNameTypeDesignation(NameTypeDesignation nameTypeDesignation, boolean addToAllNames) { +// if (nameTypeDesignation != null){ +// nameTypeDesignation.setHomotypicalGroup(this); +// nameTypeDesignations.add(nameTypeDesignation); +// } +// if (addToAllNames){ +// for (TaxonNameBase taxonNameBase : this.typifiedNames){ +// taxonNameBase.addNameTypeDesignation(nameTypeDesignation); +// } +// } +// } +// /** +// * Removes one element from the set of {@link NameTypeDesignation name type designations} assigned to the +// * {@link HomotypicalGroup homotypical group} to which this {@link TaxonNameBase taxon name} belongs. +// * The same element will be removed from the corresponding set of each of +// * the taxon names belonging to this homotypical group. Furthermore the +// * homotypical group attribute of the name type designation will be +// * nullified. +// * +// * @param nameTypeDesignation the name type designation which should be deleted +// * @see #getNameTypeDesignations() +// * @see #addNameTypeDesignation(NameTypeDesignation, boolean) +// * @see TaxonNameBase#removeNameTypeDesignation(NameTypeDesignation) +// * @see NameTypeDesignation#getHomotypicalGroup() +// */ +// public void removeNameTypeDesignation(NameTypeDesignation nameTypeDesignation) { +// if (nameTypeDesignation != null){ +// nameTypeDesignation.setHomotypicalGroup(null); +// nameTypeDesignations.remove(nameTypeDesignation); +// } +// for (TaxonNameBase taxonNameBase : this.typifiedNames){ +// taxonNameBase.removeNameTypeDesignation(nameTypeDesignation); +// } +// } + + + /** + * Retrieves the ordered list (depending on the date of publication) of + * {@link taxon.Synonym synonyms} (according to a given reference) + * the {@link TaxonNameBase taxon names} of which belong to this homotypical group. + * If other names are part of this group that are not considered synonyms + * according to the respective reference, then they will not be included in + * the result set. + * + * @param sec the reference whose treatment is to be considered + * @return the ordered list of synonyms + * @see TaxonNameBase#getSynonyms() + * @see TaxonNameBase#getTaxa() + * @see taxon.Synonym + */ + public List getSynonymsInGroup(Reference sec){ + List result = new ArrayList(); + for (TaxonNameBasename : this.getTypifiedNames()){ + for (Synonym synonym : name.getSynonyms()){ + if ( (synonym.getSec() == null && sec == null) || + synonym.getSec() != null && synonym.getSec().equals(sec)){ + result.add(synonym); + } + } + } + Collections.sort(result, new TaxonComparator()); + return result; + } + + + /** + * Creates a basionym relationship to all other names in this names homotypical + * group. + * + * @see HomotypicalGroup.setGroupBasionym(TaxonNameBase basionymName) + * + * @param basionymName + * @throws IllegalArgumentException if basionymName is not member in this homotypical group + */ + public void setGroupBasionym(TaxonNameBase basionymName) throws IllegalArgumentException{ + setGroupBasionym(basionymName, null, null, null); + } + + public void setGroupBasionym(TaxonNameBase basionymName, Reference citation, String microCitation, String ruleConsidered) + throws IllegalArgumentException { + if (! typifiedNames.contains(basionymName)){ + throw new IllegalArgumentException("Name to be set as basionym/original combination must be part of the homotypical group but is not"); + } + if (typifiedNames.size() < 2){return;} +// + //Add new relations + for (TaxonNameBase name : typifiedNames) { + if (!name.equals(basionymName)) { + name.addRelationshipFromName(basionymName, NameRelationshipType.BASIONYM(), citation, microCitation, ruleConsidered); + } + } + } + + /** + * Removes all basionym relationships between basionymName and any other name + * in its homotypic group + * + * @param basionymName + */ + public static void removeGroupBasionym(TaxonNameBase basionymName) { + HomotypicalGroup homotypicalGroup = basionymName.getHomotypicalGroup(); + Set relations = basionymName.getRelationsFromThisName(); + Set removeRelations = new HashSet(); + + for (NameRelationship relation : relations) { + + // If this is a basionym relation, and toName is in the homotypical group, + // remove the relationship. + if (relation.getType().isBasionymRelation() && + relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) { + removeRelations.add(relation); + } + } + + // Removing relations from a set through which we are iterating causes a + // ConcurrentModificationException. Therefore, we delete the targeted + // relations in a second step. + for (NameRelationship relation : removeRelations) { + basionymName.removeNameRelationship(relation); + } + } + + + /** + * Returns all taxon names in the homotypical group that do not have an 'is_basionym_for' (zool.: 'is_original_combination_for') + * or a replaced synonym relationship. + * @return + */ + @Transient + public Set getUnrelatedNames(){ + Set set = getBasionymOrReplacedSynonymRelations(true, true); + Set result = new HashSet(); + result.addAll(this.getTypifiedNames()); + for (NameRelationship nameRelationship : set){ + result.remove(nameRelationship.getFromName()); + result.remove(nameRelationship.getToName()); + } + return result; } - public void addTypeDesignation(SpecimenTypeDesignation typeDesignation) { - typeDesignation.setHomotypicalGroup(this); + + /** + * Returns all taxon names in the homotypical group that are new combinations (have a basionym/original combination + * or a replaced synonym). + * @return + */ + @Transient + public Set getNewCombinations(){ + Set set = getBasionymOrReplacedSynonymRelations(true, true); + Set result = new HashSet(); + for (NameRelationship nameRelationship : set){ + result.add(nameRelationship.getToName()); + } + return result; } - public void removeTypeDesignation(SpecimenTypeDesignation typeDesignation) { - typeDesignation.setHomotypicalGroup(null); + + + + /** + * Returns all taxon names in the homotypical group that have an 'is_basionym_for' (zool.: 'is_original_combination_for') + * or a replaced synonym relationship. + * @return + */ + @Transient + public Set getBasionymsOrReplacedSynonyms(){ + Set set = getBasionymOrReplacedSynonymRelations(true, true); + Set result = new HashSet(); + for (NameRelationship nameRelationship : set){ + result.add(nameRelationship.getFromName()); + } + return result; } - public void addTypeDesignation(Specimen typeSpecimen, TypeDesignationStatus status, ReferenceBase citation, String citationMicroReference, String originalNameString) { - SpecimenTypeDesignation td = new SpecimenTypeDesignation(this, typeSpecimen, status, citation, citationMicroReference, originalNameString); - td.setHomotypicalGroup(this); + + /** + * Returns all taxon names in the homotypical group that have a 'is_basionym_for' (zool.: 'is_original_combination_for') relationship. + * @return + */ + @Transient + public Set getBasionyms(){ + Set set = getBasionymOrReplacedSynonymRelations(true, false); + Set result = new HashSet(); + for (NameRelationship nameRelationship : set){ + result.add(nameRelationship.getFromName()); + } + return result; + } + + /** + * Returns all taxon names in the homotypical group that have a 'is_replaced_synonym_for' relationship. + * @return + */ + @Transient + public Set getReplacedSynonym(){ + Set set = getBasionymOrReplacedSynonymRelations(false, true); + Set result = new HashSet(); + for (NameRelationship nameRelationship : set){ + result.add(nameRelationship.getFromName()); + } + return result; } /** - * Retrieves the synonyms of reference sec that are part of this homotypical group. - * If other names are part of this group that are not considered synonyms in the respective sec-reference, - * then they will not be included in the resultset. - * @param sec + * Returns the name relationships that represent either a basionym (original combination) relationship or + * a replaced synonym relationship. * @return */ @Transient - public List getSynonymsInGroup(ReferenceBase sec){ - List result = new ArrayList(); - for (TaxonNameBase n:this.getTypifiedNames()){ - for (Synonym s:n.getSynonyms()){ - if (s.getSec().equals(sec)){ - result.add(s); + public Set getBasionymAndReplacedSynonymRelations(){ + return getBasionymOrReplacedSynonymRelations(true, true); + } + + /** + * Computes all basionym and replaced synonym relationships between names in this group. + * If doBasionym is false basionym relationships are excluded. + * If doReplacedSynonym is false replaced synonym relationships are excluded. + * @param doBasionym + * @param doReplacedSynonym + * @return + */ + @Transient + private Set getBasionymOrReplacedSynonymRelations(boolean doBasionym, boolean doReplacedSynonym){ + Set result = new HashSet(); + Set names = this.getTypifiedNames(); + if (names.size() > 1){ + for (TaxonNameBase name : names){ + Set nameRels = name.getNameRelations(); + //TODO make getNameRelations generic + for (Object obj : nameRels){ + NameRelationship nameRel = (NameRelationship)obj; + NameRelationshipType type = nameRel.getType(); + if ( type.isBasionymRelation() && doBasionym){ + if (testRelatedNameInThisGroup(nameRel)){ + result.add(nameRel); + }else{ + logger.warn("Name has basionym relation to a name that is not in the same homotypical group"); + } + }else if (type.isReplacedSynonymRelation() && doReplacedSynonym) { + if (testRelatedNameInThisGroup(nameRel)){ + result.add(nameRel); + }else{ + logger.warn("Name has replaced synonym relation to a name that is not in the same homotypical group"); + } + } } } } - // TODO: sort result list according to date first published, see nomenclatural reference return result; } + + private boolean testRelatedNameInThisGroup(NameRelationship nameRel){ + TaxonNameBase toName = nameRel.getToName(); + return (this.getTypifiedNames().contains(toName)); + } + + private boolean isBasionymOrRepSynRel(NameRelationshipType relType){ + if (relType == null){ + throw new IllegalArgumentException("NameRelationshipType should never be null"); + }else if (relType.equals(NameRelationshipType.BASIONYM())) { + return true; + }else if (relType.equals(NameRelationshipType.REPLACED_SYNONYM())){ + return true; + }else{ + return false; + } + } }