Project

General

Profile

Download (15.6 KB) Statistics
| Branch: | Tag: | Revision:
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

    
11
package eu.etaxonomy.cdm.model.taxon;
12

    
13
import java.util.ArrayList;
14
import java.util.HashSet;
15
import java.util.Set;
16

    
17
import javax.persistence.Entity;
18
import javax.persistence.FetchType;
19
import javax.persistence.ManyToOne;
20
import javax.persistence.OneToMany;
21
import javax.persistence.Transient;
22
import javax.xml.bind.annotation.XmlAccessType;
23
import javax.xml.bind.annotation.XmlAccessorType;
24
import javax.xml.bind.annotation.XmlElement;
25
import javax.xml.bind.annotation.XmlElementWrapper;
26
import javax.xml.bind.annotation.XmlIDREF;
27
import javax.xml.bind.annotation.XmlRootElement;
28
import javax.xml.bind.annotation.XmlSchemaType;
29
import javax.xml.bind.annotation.XmlType;
30

    
31
import org.apache.log4j.Logger;
32
import org.hibernate.annotations.Cascade;
33
import org.hibernate.annotations.CascadeType;
34
import org.hibernate.envers.Audited;
35
import org.springframework.util.Assert;
36

    
37
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
38
import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
39
import eu.etaxonomy.cdm.model.reference.ReferenceBase;
40

    
41
/**
42
 * @author a.mueller
43
 * @created 31.03.2009
44
 * @version 1.0
45
 */
46
@XmlAccessorType(XmlAccessType.FIELD)
47
@XmlType(name = "TaxonNode", propOrder = {
48
    "taxon",
49
    "parent",
50
    "taxonomicTree",
51
    "childNodes",
52
    "referenceForParentChildRelation",
53
    "microReferenceForParentChildRelation",
54
    "countChildren",
55
    "synonymToBeUsed"
56
})
57
@XmlRootElement(name = "TaxonNode")
58
@Entity
59
@Audited
60
public class TaxonNode  extends AnnotatableEntity implements ITreeNode{
61
	private static final long serialVersionUID = -4743289894926587693L;
62
	@SuppressWarnings("unused")
63
	private static final Logger logger = Logger.getLogger(TaxonNode.class);
64
	
65
	@XmlElement(name = "taxon")
66
	@XmlIDREF
67
	@XmlSchemaType(name = "IDREF")
68
	@ManyToOne(fetch = FetchType.LAZY)
69
	@Cascade({CascadeType.SAVE_UPDATE})
70
	private Taxon taxon;
71
	
72
	
73
	@XmlElement(name = "parent")
74
	@XmlIDREF
75
	@XmlSchemaType(name = "IDREF")
76
	@ManyToOne(fetch = FetchType.LAZY)
77
	@Cascade({CascadeType.SAVE_UPDATE})
78
	private TaxonNode parent;
79
	
80
	
81
	@XmlElement(name = "taxonomicTree")
82
	@XmlIDREF
83
	@XmlSchemaType(name = "IDREF")
84
	@ManyToOne(fetch = FetchType.LAZY)
85
	@Cascade({CascadeType.SAVE_UPDATE})
86
	private TaxonomicTree taxonomicTree;
87
	
88
	@XmlElementWrapper(name = "childNodes")
89
	@XmlElement(name = "childNode")
90
    @XmlIDREF
91
    @XmlSchemaType(name = "IDREF")
92
    @OneToMany(mappedBy="parent", fetch=FetchType.LAZY)
93
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
94
	private Set<TaxonNode> childNodes = new HashSet<TaxonNode>();
95
	
96
	@XmlElement(name = "reference")
97
	@XmlIDREF
98
	@XmlSchemaType(name = "IDREF")
99
	@ManyToOne(fetch = FetchType.LAZY)
100
	@Cascade({CascadeType.SAVE_UPDATE})
101
	private ReferenceBase referenceForParentChildRelation;
102
	
103
	@XmlElement(name = "microReference")
104
	private String microReferenceForParentChildRelation;
105
	
106
	@XmlElement(name = "countChildren")
107
	private int countChildren;
108
	
109
//	private Taxon originalConcept;
110
//	//or
111
	@XmlElement(name = "synonymToBeUsed")
112
	@XmlIDREF
113
	@XmlSchemaType(name = "IDREF")
114
	@ManyToOne(fetch = FetchType.LAZY)
115
	@Cascade({CascadeType.SAVE_UPDATE})
116
	private Synonym synonymToBeUsed;
117

    
118
	
119
	protected TaxonNode(){
120
		super();
121
	}
122
	
123
	/**
124
	 * to create nodes either use TaxonomicView.addRoot() or TaxonNode.addChild();
125
	 * @param taxon
126
	 * @param taxonomicTree
127
	 * @deprecated setting of taxonomic tree is handled in the addTaxonNode() method,
128
	 * use TaxonNode(taxon) instead
129
	 */
130
	protected TaxonNode (Taxon taxon, TaxonomicTree taxonomicTree){
131
		this(taxon);
132
		setTaxonomicTree(taxonomicTree);
133
	}
134
	
135
	/**
136
	 * to create nodes either use TaxonomicView.addChildTaxon() or TaxonNode.addChildTaxon();
137
	 * 
138
	 * @param taxon
139
	 */
140
	protected TaxonNode(Taxon taxon){
141
		setTaxon(taxon);
142
	}
143

    
144
	
145
	
146
//************************ METHODS **************************/
147
	/**
148
	 * @deprecated developers should be forced to pass in null values if they choose so.
149
	 */
150
	@Deprecated
151
	public TaxonNode addChild(Taxon taxon){
152
		return addChild(taxon, null, null, null);
153
	}
154
	
155
	/**
156
	 * @deprecated developers should be forced to pass in null values if they choose so.
157
	 */
158
	@Deprecated
159
	public TaxonNode addChild(Taxon taxon, ReferenceBase ref, String microReference){
160
		return addChild(taxon, ref, microReference, null);
161
	}	
162
	
163
	/**
164
	 * 
165
	 * @param taxon
166
	 * @param ref
167
	 * @param microReference
168
	 * @param synonymUsed
169
	 * @return
170
	 * @deprecated use addChildTaxon() instead
171
	 */
172
	@Deprecated
173
	public TaxonNode addChild(Taxon taxon, ReferenceBase ref, String microReference, Synonym synonymUsed){
174
		return addChildTaxon(taxon, ref, microReference, synonymUsed);
175
	}
176
	
177

    
178
	/* (non-Javadoc)
179
	 * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#addChildTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.reference.ReferenceBase, java.lang.String, eu.etaxonomy.cdm.model.taxon.Synonym)
180
	 */
181
	public TaxonNode addChildTaxon(Taxon taxon, ReferenceBase citation,
182
			String microCitation, Synonym synonymToBeUsed) {
183
		if (this.getTaxonomicTree().isTaxonInTree(taxon)){
184
			throw new IllegalArgumentException("Taxon may not be in a taxonomic view twice");
185
		}
186
		
187
		return addChildNode(new TaxonNode(taxon), citation, microCitation, synonymToBeUsed);
188
	}
189
	
190
	/**
191
	 * 
192
	 * @param childNode
193
	 * @param ref
194
	 * @param microReference
195
	 * @param synonymUsed
196
	 * 
197
	 * @deprecated use addChildNode instead
198
	 */
199
	@Deprecated 
200
	protected void addChildNote(TaxonNode childNode, ReferenceBase ref, String microReference, Synonym synonymUsed){
201
		if (! childNode.getTaxonomicTree().equals(this.getTaxonomicTree())){
202
			throw new IllegalArgumentException("addChildNote(): both nodes must be part of the same view");
203
		}
204
		childNode.setParent(this);
205
		childNodes.add(childNode);
206
		this.countChildren++;
207
		childNode.setReferenceForParentChildRelation(ref);
208
		childNode.setMicroReferenceForParentChildRelation(microReference);
209
		childNode.setSynonymToBeUsed(synonymUsed);
210
	}
211
	
212
	/**
213
	 * Moves a taxon node to a new parent. Descendents of the node are moved as well 
214
	 * 
215
	 * @param childNode the taxon node to be moved to the new parent
216
	 * @return the child node in the state of having a new parent
217
	 */
218
	public TaxonNode addChildNode(TaxonNode childNode, ReferenceBase reference, String microReference, Synonym synonymToBeUsed){
219
		// check if this node is a descendant of the childNode 
220
		if(childNode.getParent() != this && childNode.isAncestor(this)){
221
			throw new IllegalAncestryException("New parent node is a descendant of the node to be moved.");
222
		}
223

    
224
		TaxonomicTree oldChildTree = childNode.getTaxonomicTree();
225
		
226
		// remove this childNode from previous parents and trees
227
		if(childNode.getParent() instanceof TaxonNode){  //child is already child
228
			((TaxonNode) childNode.getParent()).removeChildNode(childNode);
229
		}else if(oldChildTree != null){     //child is root in old tree
230
			childNode.getTaxonomicTree().removeChildNode(childNode);
231
		}
232
		
233
		TaxonomicTree parentTree = this.getTaxonomicTree();
234
		if (oldChildTree != null && ! oldChildTree.equals(parentTree)){  //oldChildTree is null if the node was just created new
235
			childNode.setTaxonomicTreeRecursively(parentTree);
236
		}else{
237
			childNode.setTaxonomicTree(parentTree);
238
		}
239
		
240
		childNode.setParent(this);
241
		childNodes.add(childNode);
242
		this.countChildren++;
243
		childNode.setReference(reference);
244
		childNode.setMicroReference(microReference);
245
		childNode.setSynonymToBeUsed(synonymToBeUsed);
246
		
247
		return childNode;
248
	}
249
	
250
	/**
251
	 * @param parentTree
252
	 */
253
	private void setTaxonomicTreeRecursively(TaxonomicTree parentTree) {
254
		this.setTaxonomicTree(parentTree);
255
		for(TaxonNode childNode : this.getChildNodes()){
256
			childNode.setTaxonomicTreeRecursively(parentTree);
257
		}
258
	}
259

    
260
	/**
261
	 * This removes recursively all child nodes from this node and from this taxonomic view.
262
	 * TODO remove orphan nodes completely 
263
	 * 
264
	 * @param node
265
	 * @return
266
	 * @deprecated use removeChildNode() instead
267
	 */
268
	@Deprecated
269
	public boolean removeChild(TaxonNode node){
270
		return deleteChildNode(node);
271
	}	
272
	
273

    
274
	/* (non-Javadoc)
275
	 * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#removeChildNode(eu.etaxonomy.cdm.model.taxon.TaxonNode)
276
	 */
277
	public boolean deleteChildNode(TaxonNode node) {
278
		boolean result = removeChildNode(node);
279
		
280
		node.getTaxon().removeTaxonNode(node);
281
		node.setTaxon(null);
282
		
283
		ArrayList<TaxonNode> childNodes = new ArrayList<TaxonNode>(node.getChildNodes()); 
284
		for(TaxonNode childNode : childNodes){
285
			node.deleteChildNode(childNode);
286
		}
287
		
288
//		// two iterations because of ConcurrentModificationErrors
289
//        Set<TaxonNode> removeNodes = new HashSet<TaxonNode>(); 
290
//        for (TaxonNode grandChildNode : node.getChildNodes()) { 
291
//                removeNodes.add(grandChildNode); 
292
//        } 
293
//        for (TaxonNode childNode : removeNodes) { 
294
//                childNode.deleteChildNode(node); 
295
//        } 
296
            
297
		return result;
298
	}
299
	
300
	/**
301
	 * Removes the child node from this node. Sets the parent and the taxonomic tree of the child 
302
	 * node to null
303
	 * 
304
	 * @param childNode
305
	 * @return
306
	 */
307
	protected boolean removeChildNode(TaxonNode childNode){
308
		boolean result = false;
309
		
310
		if(childNode == null){
311
			throw new IllegalArgumentException("TaxonNode may not be null");
312
		}
313
		if(HibernateProxyHelper.deproxy(childNode.getParent(), TaxonNode.class) != this){
314
			throw new IllegalArgumentException("TaxonNode must be a child of this node");
315
		}
316
		
317
		result = childNodes.remove(childNode);
318
		this.countChildren--;
319
		if (this.countChildren < 0){
320
			throw new IllegalStateException("children count must not be negative ");
321
		}
322
		childNode.setParent(null);
323
		childNode.setTaxonomicTree(null);
324
		
325
		return result;
326
	}
327
	
328
	
329
	/**
330
	 * Remove this taxonNode From its taxonomic parent
331
	 * 
332
	 * @return true on success
333
	 */
334
	public boolean delete(){
335
		if(isTopmostNode()){
336
			return taxonomicTree.deleteChildNode(this);
337
		}else{
338
			return getParent().deleteChildNode(this);
339
		}		
340
	}
341
	
342
//*********** GETTER / SETTER ***********************************/
343
	
344
	public Taxon getTaxon() {
345
		return taxon;
346
	}
347
	protected void setTaxon(Taxon taxon) {
348
		this.taxon = taxon;
349
		if (taxon != null){
350
			taxon.addTaxonNode(this);
351
		}
352
	}
353
	public ITreeNode getParent() {
354
		if(isTopmostNode())
355
			return getTaxonomicTree();
356
		return parent;
357
	}
358
	protected void setParent(ITreeNode parent) {
359
		if(parent instanceof TaxonomicTree)
360
			this.parent = null;
361
		this.parent = (TaxonNode) parent;
362
	}
363
	public TaxonomicTree getTaxonomicTree() {
364
		return taxonomicTree;
365
	}
366
	//invisible part of the bidirectional relationship, for public use TaxonomicView.addRoot() or TaxonNode.addChild()
367
	protected void setTaxonomicTree(TaxonomicTree taxonomicTree) {
368
		this.taxonomicTree = taxonomicTree;
369
	}
370
	public Set<TaxonNode> getChildNodes() {
371
		return childNodes;
372
	}
373
	
374
	/**
375
	 * Returns a set containing this node and all nodes that are descendants of this node
376
	 * 
377
	 * @return 
378
	 */
379
	protected Set<TaxonNode> getDescendants(){
380
		Set<TaxonNode> nodeSet = new HashSet<TaxonNode>();
381
		
382
		nodeSet.add(this);
383
		
384
		for(TaxonNode childNode : getChildNodes()){
385
			nodeSet.addAll(childNode.getDescendants());
386
		}		
387
		
388
		return nodeSet;
389
	}
390
	
391
	/**
392
	 * Returns a 
393
	 * 
394
	 * @return
395
	 */
396
	protected Set<TaxonNode> getAncestors(){
397
		Set<TaxonNode> nodeSet = new HashSet<TaxonNode>();
398
		
399
		
400
		nodeSet.add(this);
401
		
402
		if(this.getParent() instanceof TaxonNode){
403
			nodeSet.addAll(((TaxonNode) this.getParent()).getAncestors());
404
		}
405
		
406
		return nodeSet;
407
	}
408
	
409
//	protected void setChildNodes(List<TaxonNode> childNodes) {
410
//		this.childNodes = childNodes;
411
//	}
412
	/**
413
	 * The reference for the parent child relationship
414
	 * 
415
	 * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#getReference()
416
	 */
417
	public ReferenceBase getReference() {
418
		return referenceForParentChildRelation;
419
	}
420
	
421
	/* (non-Javadoc)
422
	 * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#setReference(eu.etaxonomy.cdm.model.reference.ReferenceBase)
423
	 */
424
	public void setReference(ReferenceBase reference) {
425
		this.referenceForParentChildRelation = reference;
426
	}
427
	
428
	/**
429
	 * @return
430
	 * @deprecated use getReference instead
431
	 */
432
	@Deprecated 
433
	public ReferenceBase getReferenceForParentChildRelation() {
434
		return referenceForParentChildRelation;
435
	}
436
	@Deprecated
437
	public void setReferenceForParentChildRelation(
438
			ReferenceBase referenceForParentChildRelation) {
439
		this.referenceForParentChildRelation = referenceForParentChildRelation;
440
	}
441
	
442
	/**
443
	 * 
444
	 * 
445
	 * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#getMicroReference()
446
	 */
447
	public String getMicroReference() {
448
		return microReferenceForParentChildRelation;
449
	}
450
	
451
	/* (non-Javadoc)
452
	 * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#setMicroReference(java.lang.String)
453
	 */
454
	public void setMicroReference(String microReference) {
455
		this.microReferenceForParentChildRelation = microReference;
456
	}
457
	
458
	@Deprecated
459
	public String getMicroReferenceForParentChildRelation() {
460
		return microReferenceForParentChildRelation;
461
	}
462
	
463
	@Deprecated
464
	public void setMicroReferenceForParentChildRelation(
465
			String microReferenceForParentChildRelation) {
466
		this.microReferenceForParentChildRelation = microReferenceForParentChildRelation;
467
	}
468

    
469
	
470
	/**
471
	 * @return the count of children this taxon node has
472
	 */
473
	public int getCountChildren() {
474
		return countChildren;
475
	}
476
	
477
	/**
478
	 * @param countChildren
479
	 */
480
	protected void setCountChildren(int countChildren) {
481
		this.countChildren = countChildren;
482
	}
483
//	public Taxon getOriginalConcept() {
484
//		return originalConcept;
485
//	}
486
//	public void setOriginalConcept(Taxon originalConcept) {
487
//		this.originalConcept = originalConcept;
488
//	}
489
	public Synonym getSynonymToBeUsed() {
490
		return synonymToBeUsed;
491
	}
492
	public void setSynonymToBeUsed(Synonym synonymToBeUsed) {
493
		this.synonymToBeUsed = synonymToBeUsed;
494
	}
495
	
496
	/**
497
	 * Whether this TaxonNode is a root node
498
	 * @return
499
	 * @deprecated use isTopmostNode() instead
500
	 */
501
	@Transient
502
	public boolean isRootNode(){
503
		return parent == null;
504
	}
505
	
506
	/**
507
	 * Whether this TaxonNode is a direct child of the taxonomic tree TreeNode
508
	 * @return
509
	 */
510
	@Transient
511
	public boolean isTopmostNode(){
512
		return parent == null;
513
	}
514
	
515
	/**
516
	 * Whether this TaxonNode is a descendant of the given TaxonNode
517
	 * 
518
	 * Caution: use this method with care on big branches. -> performance and memory hungry
519
	 * 
520
	 * Protip: Try solving your problem with the isAscendant method which traverses the tree in the 
521
	 * other direction (up). It will always result in a rather small set of consecutive parents beeing
522
	 * generated.
523
	 * 
524
	 * TODO implement more efficiently without generating the set of descendants first
525
	 * 
526
	 * @param possibleParent
527
	 * @return true if this is a descendant
528
	 */
529
	@Transient
530
	public boolean isDescendant(TaxonNode possibleParent){
531
		return possibleParent.getDescendants().contains(this);
532
	}
533
	
534
	/**
535
	 * Whether this TaxonNode is an ascendant of the given TaxonNode
536
	 * 
537
	 * 
538
	 * @param possibleChild
539
	 * @return true if there are ascendants
540
	 */
541
	@Transient
542
	public boolean isAncestor(TaxonNode possibleChild){
543
		return possibleChild.getAncestors().contains(this);
544
	}
545
	
546
	/**
547
	 * Whether this taxon has child nodes
548
	 * 
549
	 * @return true if the taxonNode has childNodes
550
	 */
551
	@Transient
552
	public boolean hasChildNodes(){
553
		return childNodes.size() > 0;
554
	}
555
}
(10-10/15)