Merge branch 'release/5.45.0'
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / taxon / Classification.java
index 7d75d8a2a9c170ef9887d9fe1492ed14dbb791c0..c3a24d3a16bc83d3e4a7fe67d51bdbecf316c48b 100644 (file)
@@ -6,7 +6,6 @@
 * 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 java.util.ArrayList;
@@ -22,7 +21,6 @@ import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
 import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
-import javax.persistence.ManyToOne;
 import javax.persistence.MapKeyJoinColumn;
 import javax.persistence.OneToMany;
 import javax.persistence.OneToOne;
@@ -37,7 +35,8 @@ import javax.xml.bind.annotation.XmlSchemaType;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 
-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.envers.Audited;
@@ -46,27 +45,28 @@ import org.hibernate.search.annotations.IndexedEmbedded;
 
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
 import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
-import eu.etaxonomy.cdm.model.common.IReferencedEntity;
 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
 import eu.etaxonomy.cdm.model.common.Language;
 import eu.etaxonomy.cdm.model.common.LanguageString;
 import eu.etaxonomy.cdm.model.common.MultilanguageText;
 import eu.etaxonomy.cdm.model.common.TimePeriod;
 import eu.etaxonomy.cdm.model.location.NamedArea;
+import eu.etaxonomy.cdm.model.reference.NamedSource;
+import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
 import eu.etaxonomy.cdm.model.reference.Reference;
 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
+import eu.etaxonomy.cdm.strategy.cache.taxon.ClassificationDefaultCacheStrategy;
 
 /**
  * @author a.mueller
- * @created 31.03.2009
+ * @since 31.03.2009
  */
 @XmlAccessorType(XmlAccessType.FIELD)
 @XmlType(name = "Classification", propOrder = {
     "name",
     "description",
     "rootNode",
-    "reference",
-    "microReference",
+    "source",
     "timeperiod",
     "geoScopes"
 })
@@ -74,9 +74,12 @@ import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
 @Entity
 @Audited
 @Indexed(index = "eu.etaxonomy.cdm.model.taxon.Classification")
-public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheStrategy<Classification>> implements IReferencedEntity, ITaxonTreeNode, Cloneable{
+public class Classification
+            extends IdentifiableEntity<IIdentifiableEntityCacheStrategy<Classification>>
+            implements ITaxonTreeNode{
+
     private static final long serialVersionUID = -753804821474209635L;
-    private static final Logger logger = Logger.getLogger(Classification.class);
+    private static final Logger logger = LogManager.getLogger();
 
     @XmlElement(name = "Name")
     @OneToOne(fetch = FetchType.LAZY)
@@ -85,7 +88,6 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
     @IndexedEmbedded
     private LanguageString name;
 
-
     @XmlElement(name = "rootNode")
     @XmlIDREF
     @XmlSchemaType(name = "IDREF")
@@ -93,16 +95,6 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
     @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
     private TaxonNode rootNode;
 
-    @XmlElement(name = "reference")
-    @XmlIDREF
-    @XmlSchemaType(name = "IDREF")
-    @ManyToOne(fetch = FetchType.LAZY)
-    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
-    private Reference reference;
-
-    @XmlElement(name = "microReference")
-    private String microReference;
-
        @XmlElement(name = "TimePeriod")
     private TimePeriod timeperiod = TimePeriod.NewInstance();
 
@@ -113,7 +105,7 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
     @ManyToMany(fetch = FetchType.LAZY)
     @JoinTable(name="Classification_GeoScope")
 //    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})  remove cascade #5755
-    private Set<NamedArea> geoScopes = new HashSet<NamedArea>();
+    private Set<NamedArea> geoScopes = new HashSet<>();
 
        @XmlElement(name = "Description")
        @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
@@ -125,15 +117,14 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
 //    @FieldBridge(impl=MultilanguageTextFieldBridge.class)
     private Map<Language,LanguageString> description = new HashMap<>();
 
-
-
-//     /**
-//      * If this classification is an alternative classification for a subclassification in
-//      * an other classification(parent view),
-//      * the alternativeViewRoot is the connection node from this classification to the parent classification.
-//      * It replaces another node in the parent view.
-//      */
-//     private AlternativeViewRoot alternativeViewRoot;
+       //TODO remove, due to #9211
+       //the source for this single classification
+    @XmlElement(name = "source")
+    @XmlIDREF
+    @XmlSchemaType(name = "IDREF")
+    @OneToOne(fetch = FetchType.LAZY, orphanRemoval=true)
+    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.DELETE})
+    private NamedSource source;
 
 // ********************** FACTORY METHODS *********************************************/
 
@@ -155,8 +146,9 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
 
 // **************************** CONSTRUCTOR *********************************/
 
-    //for hibernate use only, protected required by Javassist
-    protected Classification(){super();}
+    //for hibernate use only, *packet* private required by bytebuddy
+    @Deprecated
+    Classification(){}
 
     protected Classification(String name, Reference reference, Language language){
         this();
@@ -167,7 +159,13 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
         rootNode.setClassification(this);
     }
 
+    @Override
+    protected void initDefaultCacheStrategy() {
+        this.cacheStrategy = ClassificationDefaultCacheStrategy.NewInstance();
+    }
+
 //********************** xxxxxxxxxxxxx ******************************************/
+
     /**
      * Returns the topmost {@link TaxonNode taxon node} (root node) of <i>this</i>
      * classification. The root node does not have any parent and no taxon. Since taxon nodes
@@ -192,8 +190,8 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
 
         childNode.setParentTreeNode(this.rootNode, index);
 
-        childNode.setReference(citation);
-        childNode.setMicroReference(microCitation);
+        childNode.setCitation(citation);
+        childNode.setCitationMicroReference(microCitation);
 //             childNode.setSynonymToBeUsed(synonymToBeUsed);
 
         return childNode;
@@ -206,7 +204,25 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
 
     @Override
     public TaxonNode addChildTaxon(Taxon taxon, int index, Reference citation, String microCitation) {
-        return addChildNode(new TaxonNode(taxon), index, citation, microCitation);
+        return addChildNode(new TaxonNode(taxon), index, NamedSource.NewPrimarySourceInstance(citation, microCitation));
+    }
+
+    @Override
+    public TaxonNode addChildTaxon(Taxon taxon, NamedSource source) {
+        return addChildTaxon(taxon, rootNode.getCountChildren(), source);
+    }
+
+    @Override
+    public TaxonNode addChildTaxon(Taxon taxon, int index, NamedSource source) {
+        return addChildNode(new TaxonNode(taxon), index, source);
+    }
+
+    @Override
+    public TaxonNode addChildNode(TaxonNode childNode, int index, NamedSource source) {
+        childNode.setParentTreeNode(this.rootNode, index);
+        childNode.setSource(source);
+
+        return childNode;
     }
 
     @Override
@@ -218,7 +234,7 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
             node.setTaxon(null);
         }
 
-        ArrayList<TaxonNode> childNodes = new ArrayList<TaxonNode>(node.getChildNodes());
+        ArrayList<TaxonNode> childNodes = new ArrayList<>(node.getChildNodes());
         for (TaxonNode childNode : childNodes){
             if (childNode != null){
                 node.deleteChildNode(childNode);
@@ -229,9 +245,11 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
 
     public boolean deleteChildNode(TaxonNode node, boolean deleteChildren) {
         boolean result = removeChildNode(node);
+        if (node.hasTaxon()){
+            node.getTaxon().removeTaxonNode(node);
+            node.setTaxon(null);
+        }
 
-        node.getTaxon().removeTaxonNode(node);
-        //node.setTaxon(null);
         if (deleteChildren){
             ArrayList<TaxonNode> childNodes = new ArrayList<TaxonNode>(node.getChildNodes());
             for (TaxonNode childNode : childNodes){
@@ -241,17 +259,13 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
         return result;
     }
 
-    /**
-     *
-     * @param node
-     * @return
-     */
     protected boolean removeChildNode(TaxonNode node){
         boolean result = false;
+        rootNode = HibernateProxyHelper.deproxy(rootNode, TaxonNode.class);
         if(!rootNode.getChildNodes().contains(node)){
             throw new IllegalArgumentException("TaxonNode is a not a root node of this classification");
         }
-        rootNode = HibernateProxyHelper.deproxy(rootNode, TaxonNode.class);
+//        rootNode = HibernateProxyHelper.deproxy(rootNode, TaxonNode.class);
         result = rootNode.removeChildNode(node);
 
         node.setParent(null);
@@ -270,7 +284,6 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
             result = true;
         }
         return result;
-
     }
 
     /**
@@ -299,7 +312,6 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
         otherNode.addChildNode(topmostNode, ref, microReference);
     }
 
-
     /**
      * Checks if the given taxon is part of <b>this</b> tree.
      * @param taxon
@@ -312,7 +324,7 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
 
     /**
      * Checks if the given taxon is part of <b>this</b> tree. If so the according TaxonNode is returned.
-     * Otherwise null is returned.
+     * Otherwise <code>null</code> is returned.
      * @param taxon
      * @return
      */
@@ -322,7 +334,7 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
         }
 
         for (TaxonNode taxonNode: taxon.getTaxonNodes()){
-               Classification classification = HibernateProxyHelper.deproxy(taxonNode.getClassification(), Classification.class);
+               Classification classification = deproxy(taxonNode.getClassification());
             if (classification.equals(this)){
                 return taxonNode;
             }
@@ -339,10 +351,9 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
         return (getTopmostNode(taxon) != null);
     }
 
-
     /**
-     * Checks if the taxon is a direct child of <b>this</b> tree and returns the according node if true.
-     * Returns null otherwise.
+     * Checks if the taxon is a direct child of the root of <b>this</b> tree and returns the according node if true.
+     * Returns <code>null</code> otherwise.
      * @param taxon
      * @return
      */
@@ -370,15 +381,15 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
     private boolean handleCitationOverwrite(TaxonNode childNode, Reference citation, String microCitation){
         if (citation != null){
             if (childNode.getReference() != null && ! childNode.getReference().equals(citation)){
-                logger.warn("ReferenceForParentChildRelation will be overwritten");
+                logger.warn("TaxonNode source will be overwritten");
             }
-            childNode.setReference(citation);
+            childNode.setCitation(citation);
         }
         if (microCitation != null){
             if (childNode.getMicroReference() != null && ! childNode.getMicroReference().equals(microCitation)){
-                logger.warn("MicroReferenceForParentChildRelation will be overwritten");
+                logger.warn("TaxonNode source detail will be overwritten");
             }
-            childNode.setMicroReference(microCitation);
+            childNode.setCitationMicroReference(microCitation);
         }
         return true;
     }
@@ -465,17 +476,49 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
         }
     }
 
+    @Override
+    @Transient
+    public String getMicroReference() {
+        return source == null ? null : this.source.getCitationMicroReference();
+    }
+    public void setMicroReference(String microReference) {
+        this.getSource(true).setCitationMicroReference(isBlank(microReference)? null : microReference);
+        checkNullSource();
+    }
 
     @Override
     @Transient
-    public Reference getCitation() {
-        return reference;
+    public Reference getReference() {
+        return (this.source == null) ? null : source.getCitation();
+    }
+    public void setReference(Reference reference) {
+        getSource(true).setCitation(reference);
+        checkNullSource();
+    }
+
+    public NamedSource getSource() {
+        return source;
+    }
+    public void setSource(NamedSource source) {
+        this.source = source;
+    }
+
+    private void checkNullSource() {
+        if (this.source != null && this.source.checkEmpty(true)){
+            this.source = null;
+        }
+    }
+
+    private NamedSource getSource(boolean createIfNotExist){
+        if (this.source == null && createIfNotExist){
+            this.source = NamedSource.NewInstance(OriginalSourceType.PrimaryTaxonomicSource);
+        }
+        return source;
     }
 
     public LanguageString getName() {
         return name;
     }
-
     public void setName(LanguageString name) {
         this.name = name;
     }
@@ -490,7 +533,7 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
      */
     @Transient
     public Set<TaxonNode> getAllNodes() {
-        Set<TaxonNode> allNodes = new HashSet<TaxonNode>();
+        Set<TaxonNode> allNodes = new HashSet<>();
 
         for(TaxonNode rootNode : getChildNodes()){
             allNodes.addAll(rootNode.getDescendants());
@@ -505,29 +548,6 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
         return rootNode.getChildNodes();
     }
 
-    @Override
-    public Reference getReference() {
-        return reference;
-    }
-
-    public void setReference(Reference reference) {
-        this.reference = reference;
-    }
-
-
-    @Override
-    public String getMicroReference() {
-        return microReference;
-    }
-
-    /**
-     * @param microReference the microReference to set
-     */
-    public void setMicroReference(String microReference) {
-        this.microReference = microReference;
-    }
-
-
     /**
         * The point in time, the time period or the season for which this description element
         * is valid. A season may be expressed by not filling the year part(s) of the time period.
@@ -535,7 +555,6 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
        public TimePeriod getTimeperiod() {
                return timeperiod;
        }
-
        /**
         * @see #getTimeperiod()
         */
@@ -546,7 +565,6 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
                this.timeperiod = timeperiod;
        }
 
-
     /**
      * Returns the set of {@link NamedArea named areas} indicating the geospatial
      * data where <i>this</i> {@link Classification} is valid.
@@ -578,7 +596,6 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
         this.geoScopes.remove(geoScope);
     }
 
-
        /**
         * Returns the i18n description used to describe
         * <i>this</i> {@link Classification}. The different {@link LanguageString language strings}
@@ -629,43 +646,27 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
                this.description.remove(language);
        }
 
-
-    @Override
-    public String generateTitle() {
-        //TODO implement as cache strategy
-        if (protectedTitleCache){
-            return this.titleCache;
-        }else if (name != null){
-            return name.getText();
-        }else if (reference != null){
-            return this.reference.getTitleCache();
-        }else{
-            return this.toString();
-        }
-    }
-
     public int compareTo(Object o) {
         //TODO needs to be implemented
         return 0;
     }
 
-
     @Override
     public boolean hasChildNodes() {
         return getChildNodes().size() > 0;
     }
 
     //*********************** CLONE ********************************************************/
+
     /**
      * Clones <i>this</i> classification. This is a shortcut that enables to create
      * a new instance that differs only slightly from <i>this</i> classification by
      * modifying only some of the attributes.<BR><BR>
 
-     * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
      * @see java.lang.Object#clone()
      */
     @Override
-    public Object clone() {
+    public Classification clone() {
         Classification result;
         try{
             result = (Classification)super.clone();
@@ -673,7 +674,6 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
             List<TaxonNode> rootNodes = new ArrayList<>();
             TaxonNode rootNodeClone;
 
-
             rootNodes.addAll(rootNode.getChildNodes());
             TaxonNode rootNode;
             Iterator<TaxonNode> iterator = rootNodes.iterator();
@@ -688,6 +688,10 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
                 rootNodeClone.setSynonymToBeUsed(rootNode.getSynonymToBeUsed());
             }
 
+            if (this.source != null){
+                result.source = source.clone();
+            }
+
             //geo-scopes
             result.geoScopes = new HashSet<>();
             for (NamedArea namedArea : getGeoScopes()){
@@ -701,7 +705,5 @@ public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheS
             e.printStackTrace();
             return null;
         }
-
     }
-
 }