merging branches/cdmlib/2.2 [7345:7377] to trunk
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / name / HomotypicalGroup.java
index ee0b7bee357f5f06df0f2f1127d752d1258fa44f..b856ce4ef155228037146ec183c654473436b0da 100644 (file)
@@ -28,14 +28,12 @@ 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.reference.ReferenceBase;
 import eu.etaxonomy.cdm.model.taxon.Synonym;
 import eu.etaxonomy.cdm.model.taxon.TaxonComparator;
-import eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy;
 
 
 /**
@@ -72,6 +70,7 @@ import eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy;
     "typifiedNames"
 })
 @Entity
+@Audited
 public class HomotypicalGroup extends AnnotatableEntity {
        static Logger logger = Logger.getLogger(HomotypicalGroup.class);
 
@@ -79,6 +78,7 @@ public class HomotypicalGroup extends AnnotatableEntity {
        @XmlElement(name = "TypifiedName")
        @XmlIDREF
        @XmlSchemaType(name = "IDREF")
+       @OneToMany(mappedBy="homotypicalGroup", fetch=FetchType.LAZY)
        protected Set<TaxonNameBase> typifiedNames = new HashSet<TaxonNameBase>();
            
        /** 
@@ -104,16 +104,9 @@ public class HomotypicalGroup extends AnnotatableEntity {
         *
         * @see #getSpecimenTypeDesignations()
         */
-       @OneToMany(mappedBy="homotypicalGroup", fetch=FetchType.LAZY)
        public Set<TaxonNameBase> getTypifiedNames() {
                return typifiedNames;
        }
-       /** 
-        * @see #getTypifiedNames()
-        */
-       protected void setTypifiedNames(Set<TaxonNameBase> typifiedNames) {
-               this.typifiedNames = typifiedNames;
-       }
        
        /** 
         * Adds a new {@link TaxonNameBase taxon name} to the set of taxon names that belong
@@ -364,18 +357,218 @@ public class HomotypicalGroup extends AnnotatableEntity {
         * @see                 TaxonNameBase#getTaxa()
         * @see                 taxon.Synonym
         */
-       @Transient
        public List<Synonym> getSynonymsInGroup(ReferenceBase sec){
                List<Synonym> result = new ArrayList();
-               for (TaxonNameBase<TaxonNameBase, INameCacheStrategy> n:this.getTypifiedNames()){
-                       for (Synonym s:n.getSynonyms()){
-                               if ( (s.getSec() == null && sec == null) ||
-                                               s.getSec().equals(sec)){
-                                       result.add(s);
+               for (TaxonNameBase<?, ?>name : 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, ReferenceBase 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<NameRelationship> relations = basionymName.getRelationsFromThisName();
+         Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
+        
+         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<TaxonNameBase> getUnrelatedNames(){
+               Set<NameRelationship> set = getBasionymOrReplacedSynonymRelations(true, true);
+               Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
+               result.addAll(this.getTypifiedNames());
+               for (NameRelationship nameRelationship : set){
+                       result.remove(nameRelationship.getFromName());
+                       result.remove(nameRelationship.getToName());
+               }
+               return result;
+       }       
+       
+       /**
+        * 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<TaxonNameBase> getNewCombinations(){
+               Set<NameRelationship> set = getBasionymOrReplacedSynonymRelations(true, true);
+               Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
+               for (NameRelationship nameRelationship : set){
+                       result.add(nameRelationship.getToName());
+               }
+               return result;
+       }       
+
+       
+       
+       /**
+        * 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<TaxonNameBase> getBasionymsOrReplacedSynonyms(){
+               Set<NameRelationship> set = getBasionymOrReplacedSynonymRelations(true, true);
+               Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
+               for (NameRelationship nameRelationship : set){
+                       result.add(nameRelationship.getFromName());
+               }
+               return result;
+       }       
+       
+       /**
+        * Returns all taxon names in the homotypical group that have a 'is_basionym_for' (zool.: 'is_original_combination_for') relationship.
+        * @return
+        */
+       @Transient
+       public Set<TaxonNameBase> getBasionyms(){
+               Set<NameRelationship> set = getBasionymOrReplacedSynonymRelations(true, false);
+               Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
+               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<TaxonNameBase> getReplacedSynonym(){
+               Set<NameRelationship> set = getBasionymOrReplacedSynonymRelations(false, true);
+               Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
+               for (NameRelationship nameRelationship : set){
+                       result.add(nameRelationship.getFromName());
+               }
+               return result;
+       }
+       
+       /**
+        * Returns the name relationships that represent either a basionym (original combination) relationship or
+        * a replaced synonym relationship.  
+        * @return
+        */
+       @Transient
+       public Set<NameRelationship> getBasionymAndReplacedSynonymRelations(){
+               return getBasionymOrReplacedSynonymRelations(true, true);
+       }
+       
+       /**
+        * Computes all basionym and replaced synonym relationships between names in this group.
+        * If <code>doBasionym</code> is <code>false</code> basionym relationships are excluded.
+        * If <code>doReplacedSynonym</code> is <code>false</code> replaced synonym relationships are excluded.
+        * @param doBasionym
+        * @param doReplacedSynonym
+        * @return
+        */
+       @Transient
+       private Set<NameRelationship> getBasionymOrReplacedSynonymRelations(boolean doBasionym, boolean doReplacedSynonym){
+               Set<NameRelationship> result = new HashSet<NameRelationship>(); 
+               Set<TaxonNameBase> 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");
+                                               }
+                                       }
+                               }
+                       }
+               }
+               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;
+               }
+       }
 }