increased storage capacity of mock cdm cachers
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / taxon / Classification.java
index 4bf49c117a1e445740d62a02ccdfdf06f26521c0..835f9820cfc4e99596c3045faf7db2bec1db2322 100644 (file)
@@ -1,9 +1,9 @@
 // $Id$\r
 /**\r
 * Copyright (C) 2007 EDIT\r
-* European Distributed Institute of Taxonomy \r
+* European Distributed Institute of Taxonomy\r
 * http://www.e-taxonomy.eu\r
-* \r
+*\r
 * The contents of this file are subject to the Mozilla Public License Version 1.1\r
 * See LICENSE.TXT at the top of this package for the full license terms.\r
 */\r
@@ -16,27 +16,21 @@ import java.util.Iterator;
 import java.util.List;\r
 import java.util.Set;\r
 \r
-import javax.persistence.ElementCollection;\r
 import javax.persistence.Entity;\r
 import javax.persistence.FetchType;\r
 import javax.persistence.JoinColumn;\r
 import javax.persistence.ManyToOne;\r
-import javax.persistence.OneToMany;\r
 import javax.persistence.OneToOne;\r
-import javax.persistence.OrderColumn;\r
 import javax.persistence.Transient;\r
-import javax.validation.constraints.NotNull;\r
 import javax.xml.bind.annotation.XmlAccessType;\r
 import javax.xml.bind.annotation.XmlAccessorType;\r
 import javax.xml.bind.annotation.XmlElement;\r
-import javax.xml.bind.annotation.XmlElementWrapper;\r
 import javax.xml.bind.annotation.XmlIDREF;\r
 import javax.xml.bind.annotation.XmlRootElement;\r
 import javax.xml.bind.annotation.XmlSchemaType;\r
 import javax.xml.bind.annotation.XmlType;\r
 \r
 import org.apache.log4j.Logger;\r
-import org.apache.poi.poifs.property.Child;\r
 import org.hibernate.annotations.Cascade;\r
 import org.hibernate.annotations.CascadeType;\r
 import org.hibernate.envers.Audited;\r
@@ -57,455 +51,500 @@ import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
 @XmlAccessorType(XmlAccessType.FIELD)\r
 @XmlType(name = "Classification", propOrder = {\r
     "name",\r
-    "rootNodes",\r
+    "rootNode",\r
     "reference",\r
     "microReference"\r
+\r
 })\r
 @XmlRootElement(name = "Classification")\r
 @Entity\r
 @Audited\r
 @Indexed(index = "eu.etaxonomy.cdm.model.taxon.Classification")\r
-public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheStrategy> implements IReferencedEntity, ITaxonTreeNode, Cloneable{\r
-       private static final long serialVersionUID = -753804821474209635L;\r
-       private static final Logger logger = Logger.getLogger(Classification.class);\r
-       \r
-       @XmlElement(name = "Name")\r
-       @OneToOne(fetch = FetchType.LAZY)\r
-       @Cascade({CascadeType.SAVE_UPDATE})\r
-       @JoinColumn(name = "name_id", referencedColumnName = "id")\r
-       @IndexedEmbedded\r
-       private LanguageString name;\r
-       \r
-       @XmlElementWrapper(name = "rootNodes")\r
-       @XmlElement(name = "rootNode")\r
+public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheStrategy<Classification>> implements IReferencedEntity, ITaxonTreeNode, Cloneable{\r
+    private static final long serialVersionUID = -753804821474209635L;\r
+    private static final Logger logger = Logger.getLogger(Classification.class);\r
+\r
+    @XmlElement(name = "Name")\r
+    @OneToOne(fetch = FetchType.LAZY)\r
+    @Cascade({CascadeType.SAVE_UPDATE})\r
+    @JoinColumn(name = "name_id", referencedColumnName = "id")\r
+    @IndexedEmbedded\r
+    private LanguageString name;\r
+\r
+\r
+    @XmlElement(name = "rootNode")\r
     @XmlIDREF\r
     @XmlSchemaType(name = "IDREF")\r
-       @OrderColumn(name="sortIndex")\r
-       @OneToMany(fetch=FetchType.LAZY)\r
+    @OneToOne(fetch=FetchType.LAZY)\r
     @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})\r
-       //TODO\r
-//  @NotNull // avoids creating a UNIQUE key for this field\r
-       private List<TaxonNode> rootNodes = new ArrayList<TaxonNode>();\r
-\r
-       @XmlElement(name = "reference")\r
-       @XmlIDREF\r
-       @XmlSchemaType(name = "IDREF")\r
-       @ManyToOne(fetch = FetchType.LAZY)\r
-       @Cascade({CascadeType.SAVE_UPDATE})\r
-       private Reference<?> reference;\r
-       \r
-       @XmlElement(name = "microReference")\r
-       private String microReference;\r
-       \r
+    private TaxonNode rootNode;\r
+\r
+    @XmlElement(name = "reference")\r
+    @XmlIDREF\r
+    @XmlSchemaType(name = "IDREF")\r
+    @ManyToOne(fetch = FetchType.LAZY)\r
+    @Cascade({CascadeType.SAVE_UPDATE})\r
+    private Reference<?> reference;\r
+\r
+\r
+\r
+    @XmlElement(name = "microReference")\r
+    private String microReference;\r
+\r
 //     /**\r
-//      * If this classification is an alternative classification for a subclassification in \r
+//      * If this classification is an alternative classification for a subclassification in\r
 //      * an other classification(parent view),\r
 //      * the alternativeViewRoot is the connection node from this classification to the parent classification.\r
 //      * It replaces another node in the parent view.\r
 //      */\r
 //     private AlternativeViewRoot alternativeViewRoot;\r
-       \r
+\r
 // ********************** FACTORY METHODS *********************************************/\r
-       \r
-       public static Classification NewInstance(String name){\r
-               return NewInstance(name, null, Language.DEFAULT());\r
-       }\r
-       \r
-       public static Classification NewInstance(String name, Language language){\r
-               return NewInstance(name, null, language);\r
-       }\r
-       \r
-       public static Classification NewInstance(String name, Reference reference){\r
-               return NewInstance(name, reference, Language.DEFAULT());\r
-       }\r
-       \r
-       public static Classification NewInstance(String name, Reference reference, Language language){\r
-               return new Classification(name, reference, language);\r
-       }\r
+\r
+    public static Classification NewInstance(String name){\r
+        return NewInstance(name, null, Language.DEFAULT());\r
+    }\r
+\r
+    public static Classification NewInstance(String name, Language language){\r
+        return NewInstance(name, null, language);\r
+    }\r
+\r
+    public static Classification NewInstance(String name, Reference reference){\r
+        return NewInstance(name, reference, Language.DEFAULT());\r
+    }\r
+\r
+    public static Classification NewInstance(String name, Reference reference, Language language){\r
+        return new Classification(name, reference, language);\r
+    }\r
 \r
 // **************************** CONSTRUCTOR *********************************/\r
-       \r
-       //for hibernate use only, protected required by Javassist\r
-       protected Classification(){super();}\r
-       \r
-       protected Classification(String name, Reference reference, Language language){\r
-               this();\r
-               LanguageString langName = LanguageString.NewInstance(name, language);\r
-               setName(langName);\r
-               setReference(reference);\r
-       }\r
-       \r
-//********************** xxxxxxxxxxxxx ******************************************/     \r
-       \r
-       @Override\r
-       public TaxonNode addChildNode(TaxonNode childNode, Reference citation, String microCitation) {\r
-               return addChildNode(childNode, rootNodes.size(), citation, microCitation);\r
-       }\r
-       \r
-       @Override\r
-       public TaxonNode addChildNode(TaxonNode childNode, int index, Reference citation, String microCitation) {\r
-               \r
-               childNode.setParentTreeNode(this, index);\r
-               \r
-               childNode.setReference(citation);\r
-               childNode.setMicroReference(microCitation);\r
+\r
+    //for hibernate use only, protected required by Javassist\r
+    protected Classification(){super();}\r
+\r
+    protected Classification(String name, Reference reference, Language language){\r
+        this();\r
+        LanguageString langName = LanguageString.NewInstance(name, language);\r
+        setName(langName);\r
+        setReference(reference);\r
+        this.rootNode = new TaxonNode();\r
+        rootNode.setClassification(this);\r
+    }\r
+\r
+//********************** xxxxxxxxxxxxx ******************************************/\r
+    /**\r
+     * Returns the topmost {@link TaxonNode taxon node} (root node) of <i>this</i>\r
+     * classification. The root node does not have any parent and no taxon. Since taxon nodes\r
+     * recursively point to their child nodes the complete classification is\r
+     * defined by its root node.\r
+     */\r
+    public TaxonNode getRootNode(){\r
+        return rootNode;\r
+    }\r
+\r
+    public void setRootNode(TaxonNode root){\r
+        this.rootNode = root;\r
+    }\r
+\r
+    @Override\r
+    public TaxonNode addChildNode(TaxonNode childNode, Reference citation, String microCitation) {\r
+        return addChildNode(childNode, rootNode.getCountChildren(), citation, microCitation);\r
+    }\r
+\r
+    @Override\r
+    public TaxonNode addChildNode(TaxonNode childNode, int index, Reference citation, String microCitation) {\r
+\r
+        childNode.setParentTreeNode(this.rootNode, index);\r
+\r
+        childNode.setReference(citation);\r
+        childNode.setMicroReference(microCitation);\r
 //             childNode.setSynonymToBeUsed(synonymToBeUsed);\r
-               \r
-               return childNode;\r
-       }\r
-\r
-       @Override\r
-       public TaxonNode addChildTaxon(Taxon taxon, Reference citation, String microCitation) {\r
-               return addChildTaxon(taxon, this.rootNodes.size() ,citation, microCitation);\r
-       }\r
-       \r
-       @Override\r
-       public TaxonNode addChildTaxon(Taxon taxon, int index, Reference citation, String microCitation) {\r
-               return addChildNode(new TaxonNode(taxon), index, citation, microCitation);\r
-       }\r
-       \r
-       @Override\r
-       public boolean deleteChildNode(TaxonNode node) {\r
-               boolean result = removeChildNode(node);\r
-               \r
-               node.getTaxon().removeTaxonNode(node);\r
-               node.setTaxon(null);    \r
-               \r
-               ArrayList<TaxonNode> childNodes = new ArrayList<TaxonNode>(node.getChildNodes()); \r
-               for (TaxonNode childNode : childNodes){\r
-                       node.deleteChildNode(childNode);\r
-               }\r
-               return result;\r
-       }\r
-       \r
-       /* (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#removeChildNode(eu.etaxonomy.cdm.model.taxon.TaxonNode)\r
-        */\r
-       public boolean deleteChildNode(TaxonNode node, boolean deleteChildren) {\r
-               boolean result = removeChildNode(node);\r
-               \r
-               //node.getTaxon().removeTaxonNode(node);\r
-               node.setTaxon(null);    \r
-               if (deleteChildren){\r
-                       ArrayList<TaxonNode> childNodes = new ArrayList<TaxonNode>(node.getChildNodes()); \r
-                       for (TaxonNode childNode : childNodes){\r
-                               node.deleteChildNode(childNode);\r
-                       }\r
-               }\r
-               return result;\r
-       }\r
-       \r
-       /**\r
-        * \r
-        * @param node\r
-        * @return\r
-        */\r
-       protected boolean removeChildNode(TaxonNode node){\r
-               boolean result = false;\r
-               if(!rootNodes.contains(node)){\r
-                       throw new IllegalArgumentException("TaxonNode is a not a root node of this classification");\r
-               }\r
-               \r
-               result = rootNodes.remove(node);\r
-\r
-               node.setParent(null);\r
-               node.setClassification(null);\r
-               \r
-               return result;\r
-       }\r
-       \r
-       /**\r
-        * Appends an existing topmost node to another node of this tree. The existing topmost node becomes \r
-        * an ordinary node.\r
-        * @param topmostNode\r
-        * @param otherNode\r
-        * @param ref\r
-        * @param microReference\r
-        * @throws IllegalArgumentException\r
-        */\r
-       public void makeTopmostNodeChildOfOtherNode(TaxonNode topmostNode, TaxonNode otherNode, Reference ref, String microReference)\r
-                               throws IllegalArgumentException{\r
-               if (otherNode == null){\r
-                       throw new NullPointerException("other node must not be null");\r
-               }\r
-               if (! getChildNodes().contains(topmostNode)){\r
-                       throw new IllegalArgumentException("root node to be added as child must already be root node within this tree");\r
-               }\r
-               if (otherNode.getClassification() == null || ! otherNode.getClassification().equals(this)){\r
-                       throw new IllegalArgumentException("other node must already be node within this tree");\r
-               }\r
-               if (otherNode.equals(topmostNode)){\r
-                       throw new IllegalArgumentException("root node and other node must not be the same");\r
-               }\r
-               otherNode.addChildNode(topmostNode, ref, microReference);\r
-               //getRootNodes().remove(root);\r
-       }\r
-       \r
-\r
-       /**\r
-        * Checks if the given taxon is part of <b>this</b> tree.\r
-        * @param taxon\r
-        * @return\r
-        */\r
-       public boolean isTaxonInTree(Taxon taxon){\r
-               return (getNode(taxon) != null);\r
-       }\r
-       \r
-       /**\r
-        * Checks if the given taxon is part of <b>this</b> tree. If so the according TaxonNode is returned.\r
-        * Otherwise null is returned.\r
-        * @param taxon\r
-        * @return\r
-        */\r
-       public TaxonNode getNode(Taxon taxon){\r
-               if (taxon == null){\r
-                       return null;\r
-               }\r
-               for (TaxonNode taxonNode: taxon.getTaxonNodes()){\r
-                       if (taxonNode.getClassification().equals(this)){\r
-                               return taxonNode;\r
-                       }\r
-               }\r
-               return null;\r
-       }\r
-       \r
-       /**\r
-        * Checks if the given taxon is one of the topmost taxa in <b>this</b> tree.\r
-        * @param taxon\r
-        * @return\r
-        */\r
-       public boolean isTopmostInTree(Taxon taxon){\r
-               return (getTopmostNode(taxon) != null);\r
-       }       \r
-       \r
-       \r
-       /**\r
-        * Checks if the taxon is a direct child of <b>this</b> tree and returns the according node if true.\r
-        * Returns null otherwise.\r
-        * @param taxon\r
-        * @return\r
-        */\r
-       public TaxonNode getTopmostNode(Taxon taxon){\r
-               if (taxon == null){\r
-                       return null;\r
-               }\r
-               for (TaxonNode taxonNode: taxon.getTaxonNodes()){\r
-                       if (taxonNode.getClassification().equals(this)){\r
-                               if (this.getChildNodes().contains(taxonNode)){\r
-                                       if (taxonNode.getParentTreeNode() instanceof TaxonNode){\r
-                                               logger.warn("A topmost node should have a Classification as parent but actually has a TaxonNode parent");\r
-                                       }\r
-                                       return taxonNode;\r
-                               }\r
-                       }\r
-               }\r
-               return null;\r
-       }\r
-\r
-       private boolean handleCitationOverwrite(TaxonNode childNode, Reference citation, String microCitation){\r
-               if (citation != null){\r
-                       if (childNode.getReference() != null && ! childNode.getReference().equals(citation)){\r
-                               logger.warn("ReferenceForParentChildRelation will be overwritten");\r
-                       }\r
-                       childNode.setReference(citation);\r
-               }\r
-               if (microCitation != null){\r
-                       if (childNode.getMicroReference() != null && ! childNode.getMicroReference().equals(microCitation)){\r
-                               logger.warn("MicroReferenceForParentChildRelation will be overwritten");\r
-                       }\r
-                       childNode.setMicroReference(microCitation);\r
-               }\r
-               return true;\r
-       }\r
-       \r
-       /**\r
-        * Relates two taxa as parent-child nodes within a classification. <BR>\r
-        * If the taxa are not yet part of the tree they are added to it.<Br>\r
-        * If the child taxon is a topmost node still it is added as child and deleted from the rootNode set.<Br>\r
-        * If the child is a child of another parent already an IllegalStateException is thrown because a child can have only \r
-        * one parent. <Br>\r
-        * If the parent-child relationship between these two taxa already exists nothing is changed. Only \r
-        * citation and microcitation are overwritten by the new values if these values are not null.\r
-        *  \r
-        * @param parent\r
-        * @param child\r
-        * @param citation\r
-        * @param microCitation\r
-        * @return the childNode\r
-        * @throws IllegalStateException If the child is a child of another parent already\r
-        */\r
-       public TaxonNode addParentChild (Taxon parent, Taxon child, Reference citation, String microCitation)\r
-                       throws IllegalStateException{\r
-               try {\r
-                       if (parent == null || child == null){\r
-                               logger.warn("Child or parent taxon is null.");\r
-                               return null;\r
-                       }\r
-                       if (parent == child){\r
-                               logger.warn("A taxon should never be its own child. Child not added");\r
-                               return null;\r
-                       }\r
-                       TaxonNode parentNode = this.getNode(parent);\r
-                       TaxonNode childNode = this.getNode(child);\r
-                       \r
-                       //if child exists in tree and has a parent \r
-                       //no multiple parents are allowed in the tree\r
-                       if (childNode != null && ! childNode.isTopmostNode()){\r
-                               //...different to the parent taxon  throw exception\r
-                               if ((childNode.getParentTreeNode() instanceof TaxonNode) && !((TaxonNode)childNode.getParent()).getTaxon().equals(parent) ){\r
-                                       throw new IllegalStateException("The child taxon is already part of the tree but has an other parent taxon than the one than the parent to be added. Child: " + child.toString() + ", new parent:" + parent.toString() + ", old parent: " + ((TaxonNode) childNode.getParent()).getTaxon().toString()) ;\r
-                               //... same as the parent taxon do nothing but overwriting citation and microCitation\r
-                               }else{\r
-                                       handleCitationOverwrite(childNode, citation, microCitation);\r
-                                       return childNode;\r
-                               }\r
-                       }\r
-                       \r
-                       //add parent node if not exist\r
-                       if (parentNode == null){\r
-                               parentNode = this.addChildTaxon(parent, null, null);\r
-                       }\r
-                       \r
-                       //add child if not exists\r
-                       if (childNode == null){\r
-                               childNode = parentNode.addChildTaxon(child, citation, microCitation);\r
-                       }else{\r
-                               //child is still topmost node\r
-                               //TODO test if child is topmostNode otherwise throw IllegalStateException\r
-                               if (! this.isTopmostInTree(child)){\r
-                                       throw new IllegalStateException("Child is not a topmost node but must be");\r
-                               }\r
-                               this.makeTopmostNodeChildOfOtherNode(childNode, parentNode, citation, microCitation);\r
-                       }\r
-                       return childNode;\r
-               } catch (IllegalStateException e) {\r
-                       throw e;\r
-               } catch (RuntimeException e){\r
-                       throw e;\r
-               }\r
-       }\r
-       \r
-       \r
-       @Transient\r
-       public Reference getCitation() {\r
-               return reference;\r
-       }\r
-       \r
-       public LanguageString getName() {\r
-               return name;\r
-       }\r
-\r
-       public void setName(LanguageString name) {\r
-               this.name = name;\r
-       }\r
-\r
-       /**\r
-        * Returns a set containing all nodes in this classification.\r
-        * \r
-        * Caution: Use this method with care. It can be very time and resource consuming and might\r
-        * run into OutOfMemoryExceptions for big trees. \r
-        * \r
-        * @return\r
-        */\r
-       @Transient\r
-       public Set<TaxonNode> getAllNodes() {\r
-               Set<TaxonNode> allNodes = new HashSet<TaxonNode>();\r
-               \r
-               for(TaxonNode rootNode : getChildNodes()){\r
-                       allNodes.addAll(rootNode.getDescendants());\r
-               }\r
-               \r
-               return allNodes;\r
-       }       \r
-       \r
-       @Override\r
-       public List<TaxonNode> getChildNodes() {\r
-               return rootNodes;\r
-       }\r
-\r
-       private void setRootNodes(List<TaxonNode> rootNodes) {\r
-               this.rootNodes = rootNodes;\r
-       }\r
-\r
-       @Override\r
-       public Reference getReference() {\r
-               return reference;\r
-       }\r
-\r
-       public void setReference(Reference reference) {\r
-               this.reference = reference;\r
-       }\r
-       \r
-\r
-       @Override\r
-       public String getMicroReference() {\r
-               return microReference;\r
-       }\r
-\r
-       /**\r
-        * @param microReference the microReference to set\r
-        */\r
-       public void setMicroReference(String microReference) {\r
-               this.microReference = microReference;\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()\r
-        */\r
-       @Override\r
-       public String generateTitle() {\r
-               return name.getText();\r
-       }\r
-\r
-       public int compareTo(Object o) {\r
-               return 0;\r
-       }\r
-\r
-\r
-       @Override\r
-       public boolean hasChildNodes() {\r
-               return getChildNodes().size() > 0;\r
-       }\r
-       \r
-       //*********************** CLONE ********************************************************/\r
-       /** \r
-        * Clones <i>this</i> classification. This is a shortcut that enables to create\r
-        * a new instance that differs only slightly from <i>this</i> classification by\r
-        * modifying only some of the attributes.<BR><BR>\r
-        \r
-        * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()\r
-        * @see java.lang.Object#clone()\r
-        */\r
-       @Override\r
-       public Object clone() {\r
-               Classification result;\r
-               try{\r
-                       result = (Classification)super.clone();\r
-                       result.rootNodes = new ArrayList<TaxonNode>();\r
-                       List<TaxonNode> rootNodes = new ArrayList<TaxonNode>();\r
-                       TaxonNode rootNodeClone;\r
-                       rootNodes = this.rootNodes;\r
-                       TaxonNode rootNode;\r
-                       Iterator<TaxonNode> iterator = rootNodes.iterator();\r
-                       \r
-                       while (iterator.hasNext()){\r
-                               rootNode = iterator.next();\r
-                               rootNodeClone = rootNode.cloneDescendants();\r
-                               rootNodeClone.setClassification(result);\r
-                               result.addChildNode(rootNodeClone, rootNode.getReference(), rootNode.getMicroReference());\r
-                               rootNodeClone.setSynonymToBeUsed(rootNode.getSynonymToBeUsed());\r
-                       }\r
-                       \r
-                       return result;\r
-               \r
-               }catch (CloneNotSupportedException e) {\r
-                       logger.warn("Object does not implement cloneable");\r
-                       e.printStackTrace();\r
-                       return null;\r
-               }\r
-               \r
-               \r
-               \r
-               \r
-       }\r
+\r
+        return childNode;\r
+    }\r
+\r
+    @Override\r
+    public TaxonNode addChildTaxon(Taxon taxon, Reference citation, String microCitation) {\r
+        return addChildTaxon(taxon, rootNode.getCountChildren(), citation, microCitation);\r
+    }\r
+\r
+    @Override\r
+    public TaxonNode addChildTaxon(Taxon taxon, int index, Reference citation, String microCitation) {\r
+        return addChildNode(new TaxonNode(taxon), index, citation, microCitation);\r
+    }\r
+\r
+    @Override\r
+    public boolean deleteChildNode(TaxonNode node) {\r
+        boolean result = removeChildNode(node);\r
+\r
+        if (node.hasTaxon()){\r
+            node.getTaxon().removeTaxonNode(node);\r
+            node.setTaxon(null);\r
+        }\r
+\r
+        ArrayList<TaxonNode> childNodes = new ArrayList<TaxonNode>(node.getChildNodes());\r
+        for (TaxonNode childNode : childNodes){\r
+            if (childNode != null){\r
+                node.deleteChildNode(childNode);\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+\r
+    public boolean deleteChildNode(TaxonNode node, boolean deleteChildren) {\r
+        boolean result = removeChildNode(node);\r
+\r
+        node.getTaxon().removeTaxonNode(node);\r
+        //node.setTaxon(null);\r
+        if (deleteChildren){\r
+            ArrayList<TaxonNode> childNodes = new ArrayList<TaxonNode>(node.getChildNodes());\r
+            for (TaxonNode childNode : childNodes){\r
+                node.deleteChildNode(childNode);\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     *\r
+     * @param node\r
+     * @return\r
+     */\r
+    protected boolean removeChildNode(TaxonNode node){\r
+        boolean result = false;\r
+        if(!rootNode.getChildNodes().contains(node)){\r
+            throw new IllegalArgumentException("TaxonNode is a not a root node of this classification");\r
+        }\r
+\r
+        result = rootNode.removeChildNode(node);\r
+\r
+        node.setParent(null);\r
+        node.setClassification(null);\r
+\r
+        return result;\r
+    }\r
+\r
+    public boolean removeRootNode(){\r
+        boolean result = false;\r
+\r
+        if (rootNode != null){\r
+            this.rootNode.setChildNodes(new ArrayList<TaxonNode>());\r
+            this.rootNode.setParent(null);\r
+            rootNode = null;\r
+            result = true;\r
+        }\r
+        return result;\r
+\r
+    }\r
+\r
+    /**\r
+     * Appends an existing topmost node to another node of this tree. The existing topmost node becomes\r
+     * an ordinary node.\r
+     * @param topmostNode\r
+     * @param otherNode\r
+     * @param ref\r
+     * @param microReference\r
+     * @throws IllegalArgumentException\r
+     */\r
+    public void makeTopmostNodeChildOfOtherNode(TaxonNode topmostNode, TaxonNode otherNode, Reference ref, String microReference)\r
+                throws IllegalArgumentException{\r
+        if (otherNode == null){\r
+            throw new NullPointerException("other node must not be null");\r
+        }\r
+        if (! getChildNodes().contains(topmostNode)){\r
+            throw new IllegalArgumentException("root node to be added as child must already be root node within this tree");\r
+        }\r
+        if (otherNode.getClassification() == null || ! otherNode.getClassification().equals(this)){\r
+            throw new IllegalArgumentException("other node must already be node within this tree");\r
+        }\r
+        if (otherNode.equals(topmostNode)){\r
+            throw new IllegalArgumentException("root node and other node must not be the same");\r
+        }\r
+        otherNode.addChildNode(topmostNode, ref, microReference);\r
+    }\r
+\r
+\r
+    /**\r
+     * Checks if the given taxon is part of <b>this</b> tree.\r
+     * @param taxon\r
+     * @return\r
+     */\r
+    public boolean isTaxonInTree(Taxon taxon){\r
+        return (getNode(taxon) != null);\r
+    }\r
+\r
+    /**\r
+     * Checks if the given taxon is part of <b>this</b> tree. If so the according TaxonNode is returned.\r
+     * Otherwise null is returned.\r
+     * @param taxon\r
+     * @return\r
+     */\r
+    public TaxonNode getNode(Taxon taxon){\r
+        if (taxon == null){\r
+            return null;\r
+        }\r
+        for (TaxonNode taxonNode: taxon.getTaxonNodes()){\r
+            if (taxonNode.getClassification().equals(this)){\r
+                return taxonNode;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * Checks if the given taxon is one of the topmost taxa in <b>this</b> tree.\r
+     * @param taxon\r
+     * @return\r
+     */\r
+    public boolean isTopmostInTree(Taxon taxon){\r
+        return (getTopmostNode(taxon) != null);\r
+    }\r
+\r
+\r
+    /**\r
+     * Checks if the taxon is a direct child of <b>this</b> tree and returns the according node if true.\r
+     * Returns null otherwise.\r
+     * @param taxon\r
+     * @return\r
+     */\r
+    public TaxonNode getTopmostNode(Taxon taxon){\r
+        if (taxon == null){\r
+            return null;\r
+        }\r
+        for (TaxonNode taxonNode: taxon.getTaxonNodes()){\r
+            if (taxonNode.getClassification().equals(this)){\r
+                if (this.getChildNodes().contains(taxonNode)){\r
+                    if (taxonNode.getParent() == null){\r
+                        logger.warn("A topmost node should always have the root node as parent but actually has no parent");\r
+                    }else if (taxonNode.getParent().getParent() != null){\r
+                        logger.warn("The root node should have not parent but actually has one");\r
+                    }else if (taxonNode.getParent().getTaxon() != null){\r
+                        logger.warn("The root node should have not taxon but actually has one");\r
+                    }\r
+                    return taxonNode;\r
+                }\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private boolean handleCitationOverwrite(TaxonNode childNode, Reference citation, String microCitation){\r
+        if (citation != null){\r
+            if (childNode.getReference() != null && ! childNode.getReference().equals(citation)){\r
+                logger.warn("ReferenceForParentChildRelation will be overwritten");\r
+            }\r
+            childNode.setReference(citation);\r
+        }\r
+        if (microCitation != null){\r
+            if (childNode.getMicroReference() != null && ! childNode.getMicroReference().equals(microCitation)){\r
+                logger.warn("MicroReferenceForParentChildRelation will be overwritten");\r
+            }\r
+            childNode.setMicroReference(microCitation);\r
+        }\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Relates two taxa as parent-child nodes within a classification. <BR>\r
+     * If the taxa are not yet part of the tree they are added to it.<Br>\r
+     * If the child taxon is a topmost node still it is added as child and deleted from the rootNode set.<Br>\r
+     * If the child is a child of another parent already an IllegalStateException is thrown because a child can have only\r
+     * one parent. <Br>\r
+     * If the parent-child relationship between these two taxa already exists nothing is changed. Only\r
+     * citation and microcitation are overwritten by the new values if these values are not null.\r
+     *\r
+     * @param parent\r
+     * @param child\r
+     * @param citation\r
+     * @param microCitation\r
+     * @return the childNode\r
+     * @throws IllegalStateException If the child is a child of another parent already\r
+     */\r
+    public TaxonNode addParentChild (Taxon parent, Taxon child, Reference citation, String microCitation)\r
+            throws IllegalStateException{\r
+        try {\r
+            if (parent == null || child == null){\r
+                logger.warn("Child or parent taxon is null.");\r
+                return null;\r
+            }\r
+            if (parent == child){\r
+                logger.warn("A taxon should never be its own child. Child not added");\r
+                return null;\r
+            }\r
+            TaxonNode parentNode = this.getNode(parent);\r
+            TaxonNode childNode = this.getNode(child);\r
+\r
+            //if child exists in tree and has a parent\r
+            //no multiple parents are allowed in the tree\r
+            if (childNode != null && ! childNode.isTopmostNode()){\r
+                //...different to the parent taxon  throw exception\r
+                if ( !(childNode.getParent().getTaxon().equals(parent) )){\r
+                    throw new IllegalStateException("The child taxon is already part of the tree but has an other parent taxon than the parent to be added. Child: " + child.toString() + ", new parent:" + parent.toString() + ", old parent: " + childNode.getParent().getTaxon().toString()) ;\r
+                //... same as the parent taxon do nothing but overwriting citation and microCitation\r
+                }else{\r
+                    handleCitationOverwrite(childNode, citation, microCitation);\r
+                    return childNode;\r
+                }\r
+            }\r
+\r
+            //add parent node if not exist\r
+            if (parentNode == null){\r
+                parentNode = this.addChildTaxon(parent, null, null);\r
+            }\r
+\r
+            //add child if not exists\r
+            if (childNode == null){\r
+                childNode = parentNode.addChildTaxon(child, citation, microCitation);\r
+            }else{\r
+                //child is still topmost node\r
+                //TODO test if child is topmostNode otherwise throw IllegalStateException\r
+                if (! this.isTopmostInTree(child)){\r
+                    //throw new IllegalStateException("Child is not a topmost node but must be");\r
+                    if (childNode.getClassification() != null){\r
+                        logger.warn("Child has no parent and is not a topmost node, child: " + child.getId() + " classification: " + childNode.getClassification().getId());\r
+                    }else{\r
+                        logger.warn("ChildNode has no classification: " + childNode.getId());\r
+                    }\r
+                    parentNode.addChildNode(childNode, citation, microCitation);\r
+                    if (!parentNode.isTopmostNode()){\r
+                        this.addChildNode(parentNode, citation, microCitation);\r
+                        logger.warn("parent is added as a topmost node");\r
+                    }else{\r
+                        logger.warn("parent is already a topmost node");\r
+                    }\r
+                }else{\r
+                    this.makeTopmostNodeChildOfOtherNode(childNode, parentNode, citation, microCitation);\r
+                }\r
+            }\r
+            return childNode;\r
+        } catch (IllegalStateException e) {\r
+            throw e;\r
+        } catch (RuntimeException e){\r
+            throw e;\r
+        }\r
+    }\r
+\r
+\r
+    @Override\r
+    @Transient\r
+    public Reference getCitation() {\r
+        return reference;\r
+    }\r
+\r
+    public LanguageString getName() {\r
+        return name;\r
+    }\r
+\r
+    public void setName(LanguageString name) {\r
+        this.name = name;\r
+    }\r
+\r
+    /**\r
+     * Returns a set containing all nodes in this classification.\r
+     *\r
+     * Caution: Use this method with care. It can be very time and resource consuming and might\r
+     * run into OutOfMemoryExceptions for big trees.\r
+     *\r
+     * @return\r
+     */\r
+    @Transient\r
+    public Set<TaxonNode> getAllNodes() {\r
+        Set<TaxonNode> allNodes = new HashSet<TaxonNode>();\r
+\r
+        for(TaxonNode rootNode : getChildNodes()){\r
+            allNodes.addAll(rootNode.getDescendants());\r
+        }\r
+\r
+        return allNodes;\r
+    }\r
+\r
+    @Override\r
+    @Transient\r
+    public List<TaxonNode> getChildNodes() {\r
+        return rootNode.getChildNodes();\r
+    }\r
+\r
+    @Override\r
+    public Reference getReference() {\r
+        return reference;\r
+    }\r
+\r
+    public void setReference(Reference reference) {\r
+        this.reference = reference;\r
+    }\r
+\r
+\r
+    @Override\r
+    public String getMicroReference() {\r
+        return microReference;\r
+    }\r
+\r
+    /**\r
+     * @param microReference the microReference to set\r
+     */\r
+    public void setMicroReference(String microReference) {\r
+        this.microReference = microReference;\r
+    }\r
+\r
+    @Override\r
+    public String generateTitle() {\r
+        return name.getText();\r
+    }\r
+\r
+    public int compareTo(Object o) {\r
+        return 0;\r
+    }\r
+\r
+\r
+    @Override\r
+    public boolean hasChildNodes() {\r
+        return getChildNodes().size() > 0;\r
+    }\r
+\r
+    //*********************** CLONE ********************************************************/\r
+    /**\r
+     * Clones <i>this</i> classification. This is a shortcut that enables to create\r
+     * a new instance that differs only slightly from <i>this</i> classification by\r
+     * modifying only some of the attributes.<BR><BR>\r
+\r
+     * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()\r
+     * @see java.lang.Object#clone()\r
+     */\r
+    @Override\r
+    public Object clone() {\r
+        Classification result;\r
+        try{\r
+            result = (Classification)super.clone();\r
+            //result.rootNode.childNodes = new ArrayList<TaxonNode>();\r
+            List<TaxonNode> rootNodes = new ArrayList<TaxonNode>();\r
+            TaxonNode rootNodeClone;\r
+\r
+\r
+            rootNodes.addAll(rootNode.getChildNodes());\r
+            TaxonNode rootNode;\r
+            Iterator<TaxonNode> iterator = rootNodes.iterator();\r
+\r
+            while (iterator.hasNext()){\r
+                rootNode = iterator.next();\r
+                rootNodeClone = rootNode.cloneDescendants();\r
+                rootNodeClone.setClassification(result);\r
+                result.addChildNode(rootNodeClone, rootNode.getReference(), rootNode.getMicroReference());\r
+                rootNodeClone.setSynonymToBeUsed(rootNode.getSynonymToBeUsed());\r
+            }\r
+\r
+            return result;\r
+\r
+        }catch (CloneNotSupportedException e) {\r
+            logger.warn("Object does not implement cloneable");\r
+            e.printStackTrace();\r
+            return null;\r
+        }\r
+\r
+\r
+\r
+\r
+\r
+    }\r
+\r
+\r
 }\r