// $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
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
@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") //creates problems (#3820) \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
- 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
- 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
+ //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.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