Merge branch 'release/5.45.0'
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / taxon / Synonym.java
index 0ef3d4cb9fa275f02fdc6a8d3b81d69c37c4c792..c8becc9ddd087ec63f77c831c5193df79b32161a 100644 (file)
 /**
 * 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.
 */
-
 package eu.etaxonomy.cdm.model.taxon;
 
-import org.apache.log4j.Logger;
-import org.hibernate.annotations.Cascade;
-import org.hibernate.annotations.CascadeType;
-import org.hibernate.envers.Audited;
-import org.hibernate.search.annotations.Indexed;
-import org.hibernate.validator.constraints.NotEmpty;
-import org.springframework.beans.factory.annotation.Configurable;
-
-import eu.etaxonomy.cdm.model.common.IRelated;
-import eu.etaxonomy.cdm.model.name.TaxonNameBase;
-import eu.etaxonomy.cdm.model.reference.Reference;
-import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
-import eu.etaxonomy.cdm.strategy.cache.taxon.TaxonBaseDefaultCacheStrategy;
-import eu.etaxonomy.cdm.validation.Level2;
-
-import java.util.*;
-
-import javax.persistence.*;
-import javax.validation.Valid;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.ManyToOne;
+import javax.persistence.Transient;
 import javax.validation.constraints.NotNull;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlIDREF;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlSchemaType;
 import javax.xml.bind.annotation.XmlType;
 
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.hibernate.annotations.Cascade;
+import org.hibernate.annotations.CascadeType;
+import org.hibernate.annotations.Type;
+import org.hibernate.envers.Audited;
+import org.hibernate.search.annotations.ContainedIn;
+import org.hibernate.search.annotations.Indexed;
+import org.springframework.beans.factory.annotation.Configurable;
+
+import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
+import eu.etaxonomy.cdm.model.name.ITaxonNameBase;
+import eu.etaxonomy.cdm.model.name.TaxonName;
+import eu.etaxonomy.cdm.model.reference.Reference;
+import eu.etaxonomy.cdm.strategy.cache.taxon.ITaxonCacheStrategy;
+import eu.etaxonomy.cdm.validation.Level3;
+import eu.etaxonomy.cdm.validation.annotation.HomotypicSynonymsShouldBelongToGroup;
+
 /**
- * The class for synonyms: these are {@link TaxonBase taxa} the {@link name.TaxonNameBase taxon names}
+ * The class for synonyms: these are {@link TaxonBase taxa} the {@link name.TaxonName taxon names}
  * of which are not used by the {@link TaxonBase#getSec() reference} to designate a real
  * taxon but are mentioned as taxon names that were oder are used by some other
  * unspecified references to designate (at least to some extent) the same
- * particular real taxon. Synonyms that are involved in no
- * {@link SynonymRelationship synonym relationship} are actually meaningless.<BR>
- * Splitting taxa in "accepted/correct" and "synonyms"
+ * particular real taxon. Synonyms that are {@link #getAcceptedTaxon() attached} to an accepted {@link Taxon taxon}
+ * are actually meaningless.<BR>
+ * Splitting taxa in "accepted/valid" and "synonyms"
  * makes it easier to handle particular relationships between
- * ("accepted/correct") {@link Taxon taxa} on the one hand and between ("synonym") taxa
- * and ("accepted/correct") taxa on the other.
- * 
+ * ("accepted/valid") {@link Taxon taxa} on the one hand and ("synonym") taxa
+ *  on the other.
+ *
  * @author m.doering
- * @version 1.0
- * @created 08-Nov-2007 13:06:55
+ * @since 08-Nov-2007 13:06:55
  */
 @XmlAccessorType(XmlAccessType.FIELD)
 @XmlType(name = "Synonym", propOrder = {
-    "synonymRelations"
+    "acceptedTaxon",
+    "type",
 })
 @XmlRootElement(name = "Synonym")
 @Entity
 @Indexed(index = "eu.etaxonomy.cdm.model.taxon.TaxonBase")
 @Audited
 @Configurable
-public class Synonym extends TaxonBase<IIdentifiableEntityCacheStrategy<Synonym>> implements IRelated<SynonymRelationship>{
-       private static final long serialVersionUID = -454067515022159757L;
+@HomotypicSynonymsShouldBelongToGroup(groups = Level3.class)
+public class Synonym extends TaxonBase<ITaxonCacheStrategy<Synonym>> {
 
-       @SuppressWarnings("unused")
-       private static final Logger logger = Logger.getLogger(Synonym.class);
+    private static final long serialVersionUID = 6977221584815363620L;
 
-       // Don't need the synonym relations here since they are stored at taxon side?
-       @XmlElementWrapper(name = "SynonymRelations")
-       @XmlElement(name = "SynonymRelationship")
+    @SuppressWarnings("unused")
+       private static final Logger logger = LogManager.getLogger();
+
+    @XmlElement(name = "acceptedTaxon")
     @XmlIDREF
     @XmlSchemaType(name = "IDREF")
-    @OneToMany(mappedBy="relatedFrom", fetch=FetchType.LAZY, orphanRemoval=true)
-       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
-       @NotNull
-       @NotEmpty(groups = Level2.class,message="{eu.etaxonomy.cdm.model.taxon.Synonym.noOrphanedSynonyms.message}")
-       @Valid
-       private Set<SynonymRelationship> synonymRelations = new HashSet<SynonymRelationship>();
+    @ManyToOne(fetch = FetchType.LAZY)
+    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
+    @ContainedIn
+//  @NotEmpty(groups = Level2.class,message="{eu.etaxonomy.cdm.model.taxon.Synonym.noOrphanedSynonyms.message}")
+//    @NotNull(groups = Level2.class)
+    private Taxon acceptedTaxon;
 
-       // ************* CONSTRUCTORS *************/    
-       /** 
-        * Class constructor: creates a new empty synonym instance.
-        * 
-        * @see         #Synonym(TaxonNameBase, Reference)
-        */
+    @XmlAttribute(name ="Type")
+    @NotNull
+    @Type(type = "eu.etaxonomy.cdm.hibernate.EnumUserType",
+        parameters = {@org.hibernate.annotations.Parameter(name="enumClass", value="eu.etaxonomy.cdm.model.taxon.SynonymType")}
+    )
+    private SynonymType type = SynonymType.SYNONYM_OF;
+
+//************************************* FACTORY ****************************/
+    /**
+     * @see #NewInstance(TaxonName, Reference)
+     * @param taxonName
+     * @param sec
+     * @return
+     */
+    public static Synonym NewInstance(ITaxonNameBase taxonName, Reference sec){
+        return NewInstance(TaxonName.castAndDeproxy(taxonName), sec);
+    }
+
+    /**
+     * Creates a new synonym instance with
+     * the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
+     * using it as a synonym and not as an ("accepted/correct") {@link Taxon taxon}.
+     *
+     * @param  TAXON_NAME    the taxon name used
+     * @param  sec          the reference using the taxon name
+     * @see  #Synonym(TaxonName, Reference)
+     */
+    public static Synonym NewInstance(TaxonName taxonName, Reference sec){
+        Synonym result = new Synonym(taxonName, sec, null);
+        return result;
+    }
+
+    public static Synonym NewInstance(TaxonName taxonName, Reference sec, String secDetail){
+        Synonym result = new Synonym(taxonName, sec, secDetail);
+        return result;
+    }
+
+// ********************* CONSTRUCTORS ******************************/
+
+    //for hibernate use only, *packet* private required by bytebuddy
        //TODO should be private, but still produces Spring init errors
-       public Synonym(){
-               this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Synonym>();
-       }
-       
-       /** 
-        * Class constructor: creates a new synonym instance with
-        * the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
-        * using it as a synonym and not as an ("accepted/correct") {@link Taxon taxon}.
-        * 
-        * @param  taxonNameBase        the taxon name used
-        * @param  sec                          the reference using the taxon name
-        * @see                                         Synonym#Synonym(TaxonNameBase, Reference)
-        */
-       public Synonym(TaxonNameBase taxonNameBase, Reference sec){
-               super(taxonNameBase, sec);
-               this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Synonym>();
-       }
-        
-       //********* METHODS **************************************/
+       Synonym(){}
 
-       /** 
-        * Creates a new synonym instance with
-        * the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
-        * using it as a synonym and not as an ("accepted/correct") {@link Taxon taxon}.
-        * 
-        * @param  taxonNameBase        the taxon name used
-        * @param  sec                          the reference using the taxon name
-        * @see                                         #Synonym(TaxonNameBase, Reference)
-        */
-       public static Synonym NewInstance(TaxonNameBase taxonName, Reference sec){
-               Synonym result = new Synonym(taxonName, sec);
-               return result;
-       }
-       
-       /** 
-        * Returns the set of all {@link SynonymRelationship synonym relationships}
-        * in which <i>this</i> synonym is involved. <i>This</i> synonym can only
-        * be the source within these synonym relationships. 
-        *  
-        * @see    #addSynonymRelation(SynonymRelationship)
-        * @see    #addRelationship(SynonymRelationship)
-        * @see    #removeSynonymRelation(SynonymRelationship)
-        */
-       public Set<SynonymRelationship> getSynonymRelations() {
-               if(synonymRelations == null) {
-                       this.synonymRelations = new HashSet<SynonymRelationship>();
-               }
-               return synonymRelations;
-       }
-       
-       /** 
-        * @see    #getSynonymRelations()
-        */
-       protected void setSynonymRelations(Set<SynonymRelationship> synonymRelations) {
-               this.synonymRelations = synonymRelations;
+       private Synonym(TaxonName taxonName, Reference sec, String secDetail){
+               super(taxonName, sec, secDetail);
        }
 
+//********************** GETTER/SETTER ******************************/
+
        /**
-        * Adds an existing {@link SynonymRelationship synonym relationship} to the set of
-        * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> synonym. If
-        * the source of the synonym relationship does not match with <i>this</i>
-        * synonym no addition will be carried out.<BR>
-        * This methods does the same as the {@link #addRelationship() addRelationship} method.
-        * 
-        * @param synonymRelation       the synonym relationship to be added to <i>this</i> synonym's
-        *                                                      synonym relationships set
-        * @see                                         #addRelationship(SynonymRelationship)
-        * @see                                         #getSynonymRelations()
-        * @see                                         #removeSynonymRelation(SynonymRelationship)
-        */
-       protected void addSynonymRelation(SynonymRelationship synonymRelation) {
-               this.synonymRelations.add(synonymRelation);
-       }
-       /** 
-        * Removes one element from the set of {@link SynonymRelationship synonym relationships} assigned
-        * to <i>this</i> synonym. Due to bidirectionality the given
-        * synonym relationship will also be removed from the set of synonym
-        * relationships assigned to the {@link Taxon#getSynonymRelations() taxon} involved in the
-        * relationship. Furthermore the content of
-        * the {@link SynonymRelationship#getAcceptedTaxon() accepted taxon} attribute and of the
-        * {@link SynonymRelationship#getSynonym() synonym} attribute within the synonym relationship
-        * itself will be set to "null".
+        * Returns the "accepted/valid" {@link Taxon taxon}
         *
-        * @param  synonymRelation  the synonym relationship which should be deleted
-        * @see                                 #getSynonymRelations()
-        * @see                                 #addRelationship(SynonymRelationship)
-        */
-       public void removeSynonymRelation(SynonymRelationship synonymRelation) {
-               synonymRelation.setSynonym(null);
-               Taxon taxon = synonymRelation.getAcceptedTaxon();
-               if (taxon != null){
-                       synonymRelation.setAcceptedTaxon(null);
-                       taxon.removeSynonymRelation(synonymRelation);
-               }
-               this.synonymRelations.remove(synonymRelation);
-       }
-       
-       
-       /**
-        * Adds an existing {@link SynonymRelationship synonym relationship} to the set of
-        * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> synonym. If
-        * the source of the synonym relationship does not match with <i>this</i>
-        * synonym no addition will be carried out.<BR>
-        * This methods does the same as the {@link #addSynonymRelation(SynonymRelationship) addSynonymRelation} method.
-        * 
-        * @param synonymRelation       the synonym relationship to be added to <i>this</i> synonym's
-        *                                                      synonym relationships set
-        * @see                                         #addSynonymRelation(SynonymRelationship)
-        * @see                                         #getSynonymRelations()
-        * @see                                         #removeSynonymRelation(SynonymRelationship)
         */
-       /* (non-Javadoc)
-        * @see eu.etaxonomy.cdm.model.common.IRelated#addRelationship(eu.etaxonomy.cdm.model.common.RelationshipBase)
-        */
-       public void addRelationship(SynonymRelationship rel){
-               addSynonymRelation(rel);
+       public Taxon getAcceptedTaxon() {
+               return this.acceptedTaxon;
        }
+    protected void setAcceptedTaxon(Taxon acceptedTaxon) {
+        if (acceptedTaxon == null){
+            Taxon oldTaxon = this.acceptedTaxon;
+            this.acceptedTaxon = null;
+            oldTaxon.removeSynonym(this);
+        }else{
+            if (this.acceptedTaxon != null){
+                this.acceptedTaxon.removeSynonym(this, false);
+            }
+            this.acceptedTaxon = acceptedTaxon;
+            this.acceptedTaxon.addSynonym(this);
+            checkHomotypic();
+        }
+    }
 
+    public SynonymType getType() {
+        return type;
+    }
+    public void setType(SynonymType type) {
+        this.type = type;
+        checkHomotypic();
+    }
 
-       /** 
-        * Returns the set of all ("accepted/correct") {@link Taxon taxa} involved in the same
-        * {@link SynonymRelationship synonym relationships} as <i>this</i> synonym.
-        * Each taxon is the target and <i>this</i> synonym is the source of a {@link SynonymRelationship synonym relationship}
-        * belonging to the {@link #getSynonymRelations() set of synonym relationships} assigned to
-        * <i>this</i> synonym. For a particular synonym there are more than one
-        * ("accepted/correct") taxon only if the {@link SynonymRelationship#isProParte() "is pro parte" flag}
-        * of the corresponding {@link SynonymRelationship synonym relationships} is set.
-        *  
-        * @see    #getSynonymRelations()
-        * @see    #getRelationType(Taxon)
-        * @see    SynonymRelationship#isProParte()
-        */
-       @Transient
-       public Set<Taxon> getAcceptedTaxa() {
-               Set<Taxon>taxa=new HashSet<Taxon>();
-               for (SynonymRelationship rel:getSynonymRelations()){
-                       taxa.add(rel.getAcceptedTaxon());
-               }
-               return taxa;
-       }
-       
+//***************** METHODS **************************/
        /**
         * Returns true if <i>this</i> is a synonym of the given taxon.
-        * 
+        *
         * @param taxon the taxon to check synonym for
-        * @return      true if <i>this</i> is a ynonms of the given taxon
-        * 
-        * @see #getAcceptedTaxa()
+        * @return      true if <i>this</i> is a synonm of the given taxon
+        *
+        * @see #getAcceptedTaxon()
         */
        @Transient
        public boolean isSynonymOf(Taxon taxon){
-               return getAcceptedTaxa().contains(taxon);
+               return taxon != null && taxon.equals(this.acceptedTaxon);
        }
 
-       @Transient
+       @Override
+    @Transient
        public boolean isOrphaned() {
-               return false;
-       }
-       /** 
-        * Returns the set of {@link SynonymRelationshipType synonym relationship types} of the
-        * {@link SynonymRelationship synonym relationships} where the {@link SynonymRelationship#getSynonym() synonym}
-        * is <i>this</i> synonym and the {@link SynonymRelationship#getAcceptedTaxon() taxon}
-        * is the given one. "Null" is returned if the given taxon is "null" or if
-        * no synonym relationship exists from <i>this</i> synonym to the
-        * given taxon.
-        *  
-        * @param taxon the ("accepted/correct") taxon which a synonym relationship 
-        *                              from <i>this</i> synonym should point to 
-        * @see                 #getSynonymRelations()
-        * @see                 #getAcceptedTaxa()
-        */
-       public Set<SynonymRelationshipType> getRelationType(Taxon taxon){
-               Set<SynonymRelationshipType> result = new HashSet<SynonymRelationshipType>();
-               if (taxon == null ){
-                       return result;
-               }
-               for (SynonymRelationship rel : getSynonymRelations()){
-                       Taxon acceptedTaxon = rel.getAcceptedTaxon();
-                       if (taxon.equals(acceptedTaxon)){
-                               result.add(rel.getType());
-                       }
-               }
-               return result;
+           return this.acceptedTaxon == null || this.acceptedTaxon.isOrphaned();
        }
 
-       /**
-        * Replaces ALL accepted taxa of this synonym by the new accepted taxon.
-        * The citation information (citation /microcitation) of the synonym relationship
-        * is kept.
-        * @param newAcceptedTaxon
-        *                      the new accepted taxon
-        * @param relType
-        *                      if not <code>null</code> the relationship type is changed to relType
-        * @param copyCitationInfo
-        *                      if true the citation and the microcitation of relationship
-        *                      is not changed.
-        * @param citation
-        *                      if copyCitationInfo is <code>false</code> this citation is set
-        *                      to the synonym relationship. 
-        * @param microCitation
-        *                      if copyCitationInfo is <code>false</code> this micro citation is set
-        *                      to the synonym relationship. 
-        
-        * @param acceptedTaxon
-        */
-       public void replaceAcceptedTaxon(Taxon newAcceptedTaxon, SynonymRelationshipType relType, boolean copyCitationInfo, Reference citation, String microCitation) {
-               Set<SynonymRelationship> rels = new HashSet<SynonymRelationship>();
-               rels.addAll(this.getSynonymRelations());  //avoid concurrent modification exception
-               
-               for (SynonymRelationship rel : rels){
-                       Taxon oldAcceptedTaxon = rel.getAcceptedTaxon();
-                       //remove old
-                       oldAcceptedTaxon.getSynonymRelations().remove(rel);
-                       Synonym syn = rel.getSynonym();  //syn should be this
-                       syn.getSynonymRelations().remove(rel);
-                       //create new
-                       SynonymRelationship newRel = (SynonymRelationship)rel.clone();
-                       newRel.setAcceptedTaxon(newAcceptedTaxon);
-                       newAcceptedTaxon.getSynonymRelations().add(newRel);
-                       newRel.setSynonym(syn);
-                       syn.getSynonymRelations().add(newRel);
-                       
-                       newRel.setType(relType);
-               }
-       }
+    /**
+     * Checks if the synonym type is homotypic. If it is
+     * the name of <code>this</code> synonym is added to the {@link HomotypicalGroup
+     * homotypic group} of the {@link Taxon accepted taxon}.
+     */
+    private void checkHomotypic() {
+        if (type != null && type.equals(SynonymType.HOMOTYPIC_SYNONYM_OF)
+                && acceptedTaxon != null && acceptedTaxon.getName() != null){
+                acceptedTaxon.getName().getHomotypicalGroup().addTypifiedName(this.getName());
+        }
+    }
+
+
+
 //*********************** CLONE ********************************************************/
-       
+
        @Override
-       public Object clone() {
+       public Synonym clone() {
                Synonym result;
                result = (Synonym)super.clone();
-               
-               result.setSynonymRelations(new HashSet<SynonymRelationship>());
-               
-                       for (SynonymRelationship synRelationship : this.getSynonymRelations()){
-                               SynonymRelationship newRelationship = (SynonymRelationship)synRelationship.clone();
-                               newRelationship.setRelatedFrom(result);
-                               result.synonymRelations.add(newRelationship);
-                       }
-                       return result;
-               
+
+               //no changes to accepted taxon, type, partial, proParte
+
+               return result;
+
        }
-}
\ No newline at end of file
+
+
+}