root/trunk/cdmlib/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/taxon/Classification.java

Revision 13049, 15.0 kB (checked in by a.mueller, 8 months ago)

minor

Line 
1// $Id$
2/**
3* Copyright (C) 2007 EDIT
4* European Distributed Institute of Taxonomy
5* http://www.e-taxonomy.eu
6*
7* The contents of this file are subject to the Mozilla Public License Version 1.1
8* See LICENSE.TXT at the top of this package for the full license terms.
9*/
10
11package eu.etaxonomy.cdm.model.taxon;
12
13import java.util.ArrayList;
14import java.util.HashSet;
15import java.util.Iterator;
16import java.util.Set;
17
18import javax.persistence.Entity;
19import javax.persistence.FetchType;
20import javax.persistence.JoinColumn;
21import javax.persistence.ManyToOne;
22import javax.persistence.OneToMany;
23import javax.persistence.OneToOne;
24import javax.persistence.Transient;
25import javax.xml.bind.annotation.XmlAccessType;
26import javax.xml.bind.annotation.XmlAccessorType;
27import javax.xml.bind.annotation.XmlElement;
28import javax.xml.bind.annotation.XmlElementWrapper;
29import javax.xml.bind.annotation.XmlIDREF;
30import javax.xml.bind.annotation.XmlRootElement;
31import javax.xml.bind.annotation.XmlSchemaType;
32import javax.xml.bind.annotation.XmlType;
33
34import org.apache.log4j.Logger;
35import org.hibernate.annotations.Cascade;
36import org.hibernate.annotations.CascadeType;
37import org.hibernate.envers.Audited;
38import org.hibernate.search.annotations.Indexed;
39import org.hibernate.search.annotations.IndexedEmbedded;
40
41import eu.etaxonomy.cdm.model.common.IReferencedEntity;
42import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
43import eu.etaxonomy.cdm.model.common.Language;
44import eu.etaxonomy.cdm.model.common.LanguageString;
45import eu.etaxonomy.cdm.model.reference.Reference;
46
47/**
48 * @author a.mueller
49 * @created 31.03.2009
50 * @version 1.0
51 */
52@XmlAccessorType(XmlAccessType.FIELD)
53@XmlType(name = "Classification", propOrder = {
54    "name",
55    "rootNodes",
56    "reference",
57    "microReference"
58})
59@XmlRootElement(name = "Classification")
60@Entity
61@Audited
62@Indexed(index = "eu.etaxonomy.cdm.model.taxon.Classification")
63public class Classification extends IdentifiableEntity implements IReferencedEntity, ITreeNode, Cloneable{
64        private static final long serialVersionUID = -753804821474209635L;
65        private static final Logger logger = Logger.getLogger(Classification.class);
66       
67        @XmlElement(name = "Name")
68        @OneToOne(fetch = FetchType.LAZY)
69        @Cascade({CascadeType.SAVE_UPDATE})
70        @JoinColumn(name = "name_id", referencedColumnName = "id")
71        @IndexedEmbedded
72        private LanguageString name;
73       
74        @XmlElementWrapper(name = "rootNodes")
75        @XmlElement(name = "rootNode")
76    @XmlIDREF
77    @XmlSchemaType(name = "IDREF")
78    @OneToMany(fetch=FetchType.LAZY)
79    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
80        //TODO
81//    @NotNull // avoids creating a UNIQUE key for this field
82        private Set<TaxonNode> rootNodes = new HashSet<TaxonNode>();
83
84        @XmlElement(name = "reference")
85        @XmlIDREF
86        @XmlSchemaType(name = "IDREF")
87        @ManyToOne(fetch = FetchType.LAZY)
88        @Cascade({CascadeType.SAVE_UPDATE})
89        private Reference reference;
90       
91        @XmlElement(name = "microReference")
92        private String microReference;
93       
94//      /**
95//       * If this classification is an alternative classification for a subclassification in
96//       * an other classification(parent view),
97//       * the alternativeViewRoot is the connection node from this classification to the parent classification.
98//       * It replaces another node in the parent view.
99//       */
100//      private AlternativeViewRoot alternativeViewRoot;
101       
102       
103        public static Classification NewInstance(String name){
104                return NewInstance(name, null, Language.DEFAULT());
105        }
106       
107        public static Classification NewInstance(String name, Language language){
108                return NewInstance(name, null, language);
109        }
110       
111        public static Classification NewInstance(String name, Reference reference){
112                return NewInstance(name, reference, Language.DEFAULT());
113        }
114       
115        public static Classification NewInstance(String name, Reference reference, Language language){
116                return new Classification(name, reference, language);
117        }
118       
119        protected Classification(String name, Reference reference, Language language){
120                this();
121                LanguageString langName = LanguageString.NewInstance(name, language);
122                setName(langName);
123                setReference(reference);
124        }
125       
126        protected Classification(){
127                super();
128        }
129       
130
131        /* (non-Javadoc)
132         * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#addChildNode(eu.etaxonomy.cdm.model.taxon.TaxonNode, eu.etaxonomy.cdm.model.reference.Reference, java.lang.String, eu.etaxonomy.cdm.model.taxon.Synonym)
133         */
134        public TaxonNode addChildNode(TaxonNode childNode, Reference citation,
135                        String microCitation, Synonym synonymToBeUsed) {
136               
137                childNode.setParentTreeNode(this);
138               
139                childNode.setReference(citation);
140                childNode.setMicroReference(microCitation);
141                childNode.setSynonymToBeUsed(synonymToBeUsed);
142               
143                return childNode;
144        }
145
146        /* (non-Javadoc)
147         * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#addChildTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.reference.Reference, java.lang.String, eu.etaxonomy.cdm.model.taxon.Synonym)
148         */
149        public TaxonNode addChildTaxon(Taxon taxon, Reference citation,
150                        String microCitation, Synonym synonymToBeUsed) {
151                return addChildNode(new TaxonNode(taxon), citation, microCitation, synonymToBeUsed);
152        }
153       
154
155        /* (non-Javadoc)
156         * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#removeChildNode(eu.etaxonomy.cdm.model.taxon.TaxonNode)
157         */
158        public boolean deleteChildNode(TaxonNode node) {
159                boolean result = removeChildNode(node);
160               
161                node.getTaxon().removeTaxonNode(node);
162                node.setTaxon(null);   
163               
164                ArrayList<TaxonNode> childNodes = new ArrayList<TaxonNode>(node.getChildNodes()); 
165                for (TaxonNode childNode : childNodes){
166                        node.deleteChildNode(childNode);
167                }
168                return result;
169        }
170       
171        /**
172         *
173         * @param node
174         * @return
175         */
176        protected boolean removeChildNode(TaxonNode node){
177                boolean result = false;
178               
179                if(!rootNodes.contains(node)){
180                        throw new IllegalArgumentException("TaxonNode is a not a root node of this classification");
181                }
182               
183                result = rootNodes.remove(node);
184
185                node.setParent(null);
186                node.setClassification(null);
187               
188                return result;
189        }
190       
191        /**
192         * Appends an existing topmost node to another node of this tree. The existing topmost node becomes
193         * an ordinary node.
194         * @param topmostNode
195         * @param otherNode
196         * @param ref
197         * @param microReference
198         * @throws IllegalArgumentException
199         */
200        public void makeTopmostNodeChildOfOtherNode(TaxonNode topmostNode, TaxonNode otherNode, Reference ref, String microReference)
201                                throws IllegalArgumentException{
202                if (otherNode == null){
203                        throw new NullPointerException("other node must not be null");
204                }
205                if (! getChildNodes().contains(topmostNode)){
206                        throw new IllegalArgumentException("root node to be added as child must already be root node within this tree");
207                }
208                if (otherNode.getClassification() == null || ! otherNode.getClassification().equals(this)){
209                        throw new IllegalArgumentException("other node must already be node within this tree");
210                }
211                if (otherNode.equals(topmostNode)){
212                        throw new IllegalArgumentException("root node and other node must not be the same");
213                }
214                otherNode.addChildNode(topmostNode, ref, microReference, null);
215                //getRootNodes().remove(root);
216        }
217       
218
219        /**
220         * Checks if the given taxon is part of <b>this</b> tree.
221         * @param taxon
222         * @return
223         */
224        public boolean isTaxonInTree(Taxon taxon){
225                return (getNode(taxon) != null);
226        }
227       
228        /**
229         * Checks if the given taxon is part of <b>this</b> tree. If so the according TaxonNode is returned.
230         * Otherwise null is returned.
231         * @param taxon
232         * @return
233         */
234        public TaxonNode getNode(Taxon taxon){
235                if (taxon == null){
236                        return null;
237                }
238                for (TaxonNode taxonNode: taxon.getTaxonNodes()){
239                        if (taxonNode.getClassification().equals(this)){
240                                return taxonNode;
241                        }
242                }
243                return null;
244        }
245       
246        /**
247         * Checks if the given taxon is one of the topmost taxa in <b>this</b> tree.
248         * @param taxon
249         * @return
250         */
251        public boolean isTopmostInTree(Taxon taxon){
252                return (getTopmostNode(taxon) != null);
253        }       
254       
255       
256        /**
257         * Checks if the taxon is a direct child of <b>this</b> tree and returns the according node if true.
258         * Returns null otherwise.
259         * @param taxon
260         * @return
261         */
262        public TaxonNode getTopmostNode(Taxon taxon){
263                if (taxon == null){
264                        return null;
265                }
266                for (TaxonNode taxonNode: taxon.getTaxonNodes()){
267                        if (taxonNode.getClassification().equals(this)){
268                                if (this.getChildNodes().contains(taxonNode)){
269                                        if (taxonNode.getParentTreeNode() instanceof TaxonNode){
270                                                logger.warn("A topmost node should have a Classification as parent but actually has a TaxonNode parent");
271                                        }
272                                        return taxonNode;
273                                }
274                        }
275                }
276                return null;
277        }
278
279        private boolean handleCitationOverwrite(TaxonNode childNode, Reference citation, String microCitation){
280                if (citation != null){
281                        if (childNode.getReference() != null && ! childNode.getReference().equals(citation)){
282                                logger.warn("ReferenceForParentChildRelation will be overwritten");
283                        }
284                        childNode.setReference(citation);
285                }
286                if (microCitation != null){
287                        if (childNode.getMicroReference() != null && ! childNode.getMicroReference().equals(microCitation)){
288                                logger.warn("MicroReferenceForParentChildRelation will be overwritten");
289                        }
290                        childNode.setMicroReference(microCitation);
291                }
292                return true;
293        }
294       
295        /**
296         * Relates two taxa as parent-child nodes within a classification. <BR>
297         * If the taxa are not yet part of the tree they are added to it.<Br>
298         * If the child taxon is a topmost node still it is added as child and deleted from the rootNode set.<Br>
299         * If the child is a child of another parent already an IllegalStateException is thrown because a child can have only
300         * one parent. <Br>
301         * If the parent-child relationship between these two taxa already exists nothing is changed. Only
302         * citation and microcitation are overwritten by the new values if these values are not null.
303         * 
304         * @param parent
305         * @param child
306         * @param citation
307         * @param microCitation
308         * @return the childNode
309         * @throws IllegalStateException If the child is a child of another parent already
310         */
311        public TaxonNode addParentChild (Taxon parent, Taxon child, Reference citation, String microCitation)
312                        throws IllegalStateException{
313                try {
314                        if (parent == null || child == null){
315                                logger.warn("Child or parent taxon is null.");
316                                return null;
317                        }
318                        if (parent == child){
319                                logger.warn("A taxon should never be its own child. Child not added");
320                                return null;
321                        }
322                        TaxonNode parentNode = this.getNode(parent);
323                        TaxonNode childNode = this.getNode(child);
324                       
325                        //if child exists in tree and has a parent
326                        //no multiple parents are allowed in the tree
327                        if (childNode != null && ! childNode.isTopmostNode()){
328                                //...different to the parent taxon  throw exception
329                                if ((childNode.getParentTreeNode() instanceof TaxonNode) && !((TaxonNode)childNode.getParent()).getTaxon().equals(parent) ){
330                                        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()) ;
331                                //... same as the parent taxon do nothing but overwriting citation and microCitation
332                                }else{
333                                        handleCitationOverwrite(childNode, citation, microCitation);
334                                        return childNode;
335                                }
336                        }
337                       
338                        //add parent node if not exist
339                        if (parentNode == null){
340                                parentNode = this.addChildTaxon(parent, null, null, null);
341                        }
342                       
343                        //add child if not exists
344                        if (childNode == null){
345                                childNode = parentNode.addChildTaxon(child, citation, microCitation, null);
346                        }else{
347                                //child is still topmost node
348                                //TODO test if child is topmostNode otherwise throw IllegalStateException
349                                if (! this.isTopmostInTree(child)){
350                                        throw new IllegalStateException("Child is not a topmost node but must be");
351                                }
352                                this.makeTopmostNodeChildOfOtherNode(childNode, parentNode, citation, microCitation);
353                        }
354                        return childNode;
355                } catch (IllegalStateException e) {
356                        throw e;
357                } catch (RuntimeException e){
358                        throw e;
359                }
360        }
361       
362       
363        @Transient
364        public Reference getCitation() {
365                return reference;
366        }
367       
368        public LanguageString getName() {
369                return name;
370        }
371
372        public void setName(LanguageString name) {
373                this.name = name;
374        }
375
376        /**
377         * Returns a set containing all nodes in this classification.
378         *
379         * Caution: Use this method with care. It can be very time and resource consuming and might
380         * run into OutOfMemoryExceptions for big trees.
381         *
382         * @return
383         */
384        @Transient
385        public Set<TaxonNode> getAllNodes() {
386                Set<TaxonNode> allNodes = new HashSet<TaxonNode>();
387               
388                for(TaxonNode rootNode : getChildNodes()){
389                        allNodes.addAll(rootNode.getDescendants());
390                }
391               
392                return allNodes;
393        }       
394       
395        public Set<TaxonNode> getChildNodes() {
396                return rootNodes;
397        }
398
399        private void setRootNodes(Set<TaxonNode> rootNodes) {
400                this.rootNodes = rootNodes;
401        }
402
403        public Reference getReference() {
404                return reference;
405        }
406
407        public void setReference(Reference reference) {
408                this.reference = reference;
409        }
410       
411
412        /**
413         * @return the microReference
414         */
415        public String getMicroReference() {
416                return microReference;
417        }
418
419        /**
420         * @param microReference the microReference to set
421         */
422        public void setMicroReference(String microReference) {
423                this.microReference = microReference;
424        }
425
426        /* (non-Javadoc)
427         * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
428         */
429        @Override
430        public String generateTitle() {
431                return name.getText();
432        }
433
434        public int compareTo(Object o) {
435                return 0;
436        }
437
438        /* (non-Javadoc)
439         * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#hasChildNodes()
440         */
441        public boolean hasChildNodes() {
442                return getChildNodes().size() > 0;
443        }
444       
445        //*********************** CLONE ********************************************************/
446        /**
447         * Clones <i>this</i> classification. This is a shortcut that enables to create
448         * a new instance that differs only slightly from <i>this</i> classification by
449         * modifying only some of the attributes.<BR><BR>
450         
451         * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
452         * @see java.lang.Object#clone()
453         */
454        @Override
455        public Object clone() {
456                Classification result;
457                try{
458                        result = (Classification)super.clone();
459                        result.rootNodes = new HashSet<TaxonNode>();
460                        Set<TaxonNode> rootNodes = new HashSet<TaxonNode>();
461                        TaxonNode rootNodeClone;
462                        rootNodes = this.rootNodes;
463                        TaxonNode rootNode;
464                        Iterator<TaxonNode> iterator = rootNodes.iterator();
465                       
466                        while (iterator.hasNext()){
467                                rootNode = iterator.next();
468                                rootNodeClone = rootNode.cloneDescendants();
469                                rootNodeClone.setClassification(result);
470                                result.addChildNode(rootNodeClone, rootNode.getReference(), rootNode.getMicroReference(), rootNode.getSynonymToBeUsed());
471                               
472                        }
473                       
474                        return result;
475               
476                }catch (CloneNotSupportedException e) {
477                        logger.warn("Object does not implement cloneable");
478                        e.printStackTrace();
479                        return null;
480                }
481               
482               
483               
484               
485        }
486}
Note: See TracBrowser for help on using the browser.