Merge branch 'release/5.45.0'
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / taxon / Synonym.java
index f6fabaf95ba8e6a7b3addc6081b87fa9e8890e0e..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 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.XmlIDREF;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlType;
 
-import org.apache.log4j.Logger;
+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.common.IRelated;
-import eu.etaxonomy.cdm.model.name.TaxonNameBase;
-import eu.etaxonomy.cdm.model.reference.ReferenceBase;
-
-import java.util.*;
-
-import javax.persistence.*;
+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.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 {@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/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 = {
+    "acceptedTaxon",
+    "type",
+})
+@XmlRootElement(name = "Synonym")
 @Entity
-public class Synonym extends TaxonBase implements IRelated<SynonymRelationship>{
-       static Logger logger = Logger.getLogger(Synonym.class);
-       
-       private Set<SynonymRelationship> synonymRelations = new HashSet<SynonymRelationship>();
+@Indexed(index = "eu.etaxonomy.cdm.model.taxon.TaxonBase")
+@Audited
+@Configurable
+@HomotypicSynonymsShouldBelongToGroup(groups = Level3.class)
+public class Synonym extends TaxonBase<ITaxonCacheStrategy<Synonym>> {
 
+    private static final long serialVersionUID = 6977221584815363620L;
 
-       public static Synonym NewInstance(TaxonNameBase taxonName, ReferenceBase sec){
-               Synonym result = new Synonym();
-               result.setName(taxonName);
-               result.setSec(sec);
-               return result;
-       }
-       
+    @SuppressWarnings("unused")
+       private static final Logger logger = LogManager.getLogger();
+
+    @XmlElement(name = "acceptedTaxon")
+    @XmlIDREF
+    @XmlSchemaType(name = "IDREF")
+    @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;
+
+    @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(){
-       }
-       
+       Synonym(){}
 
-       @OneToMany(mappedBy="relatedFrom", fetch=FetchType.EAGER)
-       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
-       public Set<SynonymRelationship> getSynonymRelations() {
-               return synonymRelations;
-       }
-       protected void setSynonymRelations(Set<SynonymRelationship> synonymRelations) {
-               this.synonymRelations = synonymRelations;
-       }
-       protected void addSynonymRelation(SynonymRelationship synonymRelation) {
-               this.synonymRelations.add(synonymRelation);
-       }
-       protected void removeSynonymRelation(SynonymRelationship synonymRelation) {
-               synonymRelation.setSynonym(null);
-               Taxon taxon = synonymRelation.getAcceptedTaxon();
-               if (taxon != null){
-                       synonymRelation.setAcceptedTaxon(null);
-                       taxon.removeSynonymRelation(synonymRelation);
-               }
-               this.synonymRelations.remove(synonymRelation);
-       }
-       
-       
-       /* (non-Javadoc)
-        * @see eu.etaxonomy.cdm.model.common.IRelated#addRelationship(eu.etaxonomy.cdm.model.common.RelationshipBase)
-        */
-       public void addRelationship(SynonymRelationship rel){
-               addSynonymRelation(rel);
+       private Synonym(TaxonName taxonName, Reference sec, String secDetail){
+               super(taxonName, sec, secDetail);
        }
 
+//********************** GETTER/SETTER ******************************/
 
-       @Transient
-       public Set<Taxon> getAcceptedTaxa() {
-               Set<Taxon>taxa=new HashSet<Taxon>();
-               for (SynonymRelationship rel:getSynonymRelations()){
-                       taxa.add(rel.getAcceptedTaxon());
-               }
-               return taxa;
+       /**
+        * Returns the "accepted/valid" {@link Taxon taxon}
+        *
+        */
+       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();
+    }
+
+//***************** METHODS **************************/
        /**
-        * Return the synonymy relationship type for the relation to a given accepted taxon.
-        * If taxon is null or no relation exists to that taxon null is returned.
-        * @param taxon
-        * @return 
+        * 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 synonm of the given taxon
+        *
+        * @see #getAcceptedTaxon()
         */
        @Transient
-       public SynonymRelationshipType getRelationType(Taxon taxon){
-               if (taxon == null ){
-                       return null;
-               }
-               for (SynonymRelationship rel:getSynonymRelations()){
-                       Taxon acceptedTaxon = rel.getAcceptedTaxon();
-                       if (taxon.equals(acceptedTaxon)){
-                               return rel.getType();
-                       }
-               }
-               return null;
+       public boolean isSynonymOf(Taxon taxon){
+               return taxon != null && taxon.equals(this.acceptedTaxon);
+       }
+
+       @Override
+    @Transient
+       public boolean isOrphaned() {
+           return this.acceptedTaxon == null || this.acceptedTaxon.isOrphaned();
+       }
+
+    /**
+     * 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 Synonym clone() {
+               Synonym result;
+               result = (Synonym)super.clone();
+
+               //no changes to accepted taxon, type, partial, proParte
+
+               return result;
+
        }
-}
\ No newline at end of file
+
+
+}