Project

General

Profile

Download (13.9 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.HashSet;
14
import java.util.Set;
15

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

    
30
import org.apache.log4j.Logger;
31
import org.hibernate.annotations.Cascade;
32
import org.hibernate.annotations.CascadeType;
33
import org.hibernate.envers.Audited;
34

    
35
import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
36
import eu.etaxonomy.cdm.model.reference.ReferenceBase;
37

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

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

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

    
175
	/* (non-Javadoc)
176
	 * @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)
177
	 */
178
	public TaxonNode addChildTaxon(Taxon taxon, ReferenceBase citation,
179
			String microCitation, Synonym synonymToBeUsed) {
180
		if (this.getTaxonomicTree().isTaxonInTree(taxon)){
181
			throw new IllegalArgumentException("Taxon may not be in a taxonomic view twice");
182
		}
183
		
184
		return addChildNode(new TaxonNode(taxon), citation, microCitation, synonymToBeUsed);
185
	}
186
	
187
	/**
188
	 * 
189
	 * @param childNode
190
	 * @param ref
191
	 * @param microReference
192
	 * @param synonymUsed
193
	 * 
194
	 * @deprecated use addChildNode instead
195
	 */
196
	@Deprecated 
197
	protected void addChildNote(TaxonNode childNode, ReferenceBase ref, String microReference, Synonym synonymUsed){
198
		if (! childNode.getTaxonomicTree().equals(this.getTaxonomicTree())){
199
			throw new IllegalArgumentException("addChildNote(): both nodes must be part of the same view");
200
		}
201
		childNode.setParent(this);
202
		childNodes.add(childNode);
203
		this.countChildren++;
204
		childNode.setReferenceForParentChildRelation(ref);
205
		childNode.setMicroReferenceForParentChildRelation(microReference);
206
		childNode.setSynonymToBeUsed(synonymUsed);
207
	}
208
	
209
	/**
210
	 * Moves a taxon node to a new parent. Descendents of the node are moved as well 
211
	 * 
212
	 * @param childNode the taxon node to be moved to the new parent
213
	 * @return the child node in the state of having a new parent
214
	 */
215
	public TaxonNode addChildNode(TaxonNode childNode, ReferenceBase reference, String microReference, Synonym synonymToBeUsed){
216
		// check if this node is a descendant of the childNode 
217
		if(childNode.getParent() != this && childNode.isAscendant(this)){
218
			throw new IllegalAncestryException("New parent node is a descendant of the node to be moved.");
219
		}
220
		
221
		childNode.setParent(this);
222
		childNode.setTaxonomicView(this.getTaxonomicTree());
223
		childNodes.add(childNode);
224
		this.countChildren++;
225
		childNode.setReference(reference);
226
		childNode.setMicroReference(microReference);
227
		childNode.setSynonymToBeUsed(synonymToBeUsed);
228
		
229
		for(TaxonNode grandChildNode : childNode.getChildNodes()){
230
			childNode.addChildNode(grandChildNode, childNode.getReference(), childNode.getMicroReference(), childNode.getSynonymToBeUsed());
231
		}
232
		
233
		return childNode;
234
	}
235
	
236
	/**
237
	 * This removes recursively all child nodes from this node and from this taxonomic view.
238
	 * TODO remove orphan nodes completely 
239
	 * 
240
	 * @param node
241
	 * @return
242
	 * @deprecated use removeChildNode() instead
243
	 */
244
	@Deprecated
245
	public boolean removeChild(TaxonNode node){
246
		return removeChildNode(node);
247
	}	
248
	
249

    
250
	/* (non-Javadoc)
251
	 * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#removeChildNode(eu.etaxonomy.cdm.model.taxon.TaxonNode)
252
	 */
253
	public boolean removeChildNode(TaxonNode node) {
254
		boolean result = false;
255
		if (node != null){
256
			// two iterations because of ConcurrentModificationErrors
257
            Set<TaxonNode> removeNodes = new HashSet<TaxonNode>(); 
258
            for (TaxonNode grandChildNode : node.getChildNodes()) { 
259
                    removeNodes.add(grandChildNode); 
260
            } 
261
            for (TaxonNode childNode : removeNodes) { 
262
                    childNode.removeChildNode(node); 
263
            } 
264
            
265
			result = childNodes.remove(node);
266
			this.countChildren--;
267
			if (this.countChildren < 0){
268
				throw new IllegalStateException("children count must not be negative ");
269
			}
270
			node.getTaxon().removeTaxonNode(node);
271
			node.setParent(null);
272
			node.setTaxonomicView(null);
273
			node.setTaxon(null);
274
		}
275
		return result;
276
	}
277
	
278
	/**
279
	 * Remove this taxonNode From its taxonomic parent
280
	 * 
281
	 * @return true on success
282
	 */
283
	public boolean remove(){
284
		if(isTopmostNode()){
285
			return taxonomicTree.removeChildNode(this);
286
		}else{
287
			return getParent().removeChildNode(this);
288
		}		
289
	}
290
	
291
//*********** GETTER / SETTER ***********************************/
292
	
293
	public Taxon getTaxon() {
294
		return taxon;
295
	}
296
	protected void setTaxon(Taxon taxon) {
297
		this.taxon = taxon;
298
		if (taxon != null){
299
			taxon.addTaxonNode(this);
300
		}
301
	}
302
	public TaxonNode getParent() {
303
		return parent;
304
	}
305
	protected void setParent(TaxonNode parent) {
306
		this.parent = parent;
307
	}
308
	public TaxonomicTree getTaxonomicTree() {
309
		return taxonomicTree;
310
	}
311
	//invisible part of the bidirectional relationship, for public use TaxonomicView.addRoot() or TaxonNode.addChild()
312
	protected void setTaxonomicView(TaxonomicTree taxonomicTree) {
313
		this.taxonomicTree = taxonomicTree;
314
	}
315
	public Set<TaxonNode> getChildNodes() {
316
		return childNodes;
317
	}
318
	
319
	/**
320
	 * Returns a set containing this node and all nodes that are descendants of this node
321
	 * 
322
	 * @return 
323
	 */
324
	protected Set<TaxonNode> getDescendants(){
325
		Set<TaxonNode> nodeSet = new HashSet<TaxonNode>();
326
		
327
		nodeSet.add(this);
328
		
329
		for(TaxonNode childNode : getChildNodes()){
330
			nodeSet.addAll(childNode.getDescendants());
331
		}		
332
		
333
		return nodeSet;
334
	}
335
	
336
	/**
337
	 * Returns a 
338
	 * 
339
	 * @return
340
	 */
341
	protected Set<TaxonNode> getAscendants(){
342
		Set<TaxonNode> nodeSet = new HashSet<TaxonNode>();
343
		
344
		
345
		nodeSet.add(this);
346
		
347
		if(this.getParent() != null){
348
			nodeSet.addAll(this.getParent().getAscendants());
349
		}
350
		
351
		return nodeSet;
352
	}
353
	
354
//	protected void setChildNodes(List<TaxonNode> childNodes) {
355
//		this.childNodes = childNodes;
356
//	}
357
	/**
358
	 * The reference for the parent child relationship
359
	 * 
360
	 * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#getReference()
361
	 */
362
	public ReferenceBase getReference() {
363
		return referenceForParentChildRelation;
364
	}
365
	
366
	/* (non-Javadoc)
367
	 * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#setReference(eu.etaxonomy.cdm.model.reference.ReferenceBase)
368
	 */
369
	public void setReference(ReferenceBase reference) {
370
		this.referenceForParentChildRelation = reference;
371
	}
372
	
373
	/**
374
	 * @return
375
	 * @deprecated use getReference instead
376
	 */
377
	@Deprecated 
378
	public ReferenceBase getReferenceForParentChildRelation() {
379
		return referenceForParentChildRelation;
380
	}
381
	@Deprecated
382
	public void setReferenceForParentChildRelation(
383
			ReferenceBase referenceForParentChildRelation) {
384
		this.referenceForParentChildRelation = referenceForParentChildRelation;
385
	}
386
	
387
	/**
388
	 * 
389
	 * 
390
	 * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#getMicroReference()
391
	 */
392
	public String getMicroReference() {
393
		return microReferenceForParentChildRelation;
394
	}
395
	
396
	/* (non-Javadoc)
397
	 * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#setMicroReference(java.lang.String)
398
	 */
399
	public void setMicroReference(String microReference) {
400
		this.microReferenceForParentChildRelation = microReference;
401
	}
402
	
403
	@Deprecated
404
	public String getMicroReferenceForParentChildRelation() {
405
		return microReferenceForParentChildRelation;
406
	}
407
	
408
	@Deprecated
409
	public void setMicroReferenceForParentChildRelation(
410
			String microReferenceForParentChildRelation) {
411
		this.microReferenceForParentChildRelation = microReferenceForParentChildRelation;
412
	}
413

    
414
	
415
	/**
416
	 * @return the count of children this taxon node has
417
	 */
418
	public int getCountChildren() {
419
		return countChildren;
420
	}
421
	
422
	/**
423
	 * @param countChildren
424
	 */
425
	protected void setCountChildren(int countChildren) {
426
		this.countChildren = countChildren;
427
	}
428
//	public Taxon getOriginalConcept() {
429
//		return originalConcept;
430
//	}
431
//	public void setOriginalConcept(Taxon originalConcept) {
432
//		this.originalConcept = originalConcept;
433
//	}
434
	public Synonym getSynonymToBeUsed() {
435
		return synonymToBeUsed;
436
	}
437
	public void setSynonymToBeUsed(Synonym synonymToBeUsed) {
438
		this.synonymToBeUsed = synonymToBeUsed;
439
	}
440
	
441
	/**
442
	 * Whether this TaxonNode is a root node
443
	 * @return
444
	 * @deprecated use isTopmostNode() instead
445
	 */
446
	@Transient
447
	public boolean isRootNode(){
448
		return parent == null;
449
	}
450
	
451
	/**
452
	 * Whether this TaxonNode is a direct child of the taxonomic tree TreeNode
453
	 * @return
454
	 */
455
	@Transient
456
	public boolean isTopmostNode(){
457
		return parent == null;
458
	}
459
	
460
	/**
461
	 * Whether this TaxonNode is a descendant of the given TaxonNode
462
	 * 
463
	 * Caution: use this method with care on big branches. -> performance and memory hungry
464
	 * 
465
	 * Protip: Try solving your problem with the isAscendant method which traverses the tree in the 
466
	 * other direction (up). It will always result in a rather small set of consecutive parents beeing
467
	 * generated.
468
	 * 
469
	 * TODO implement more efficiently without generating the set of descendants first
470
	 * 
471
	 * @param possibleParent
472
	 * @return true if this is a descendant
473
	 */
474
	@Transient
475
	public boolean isDescendant(TaxonNode possibleParent){
476
		return possibleParent.getDescendants().contains(this);
477
	}
478
	
479
	/**
480
	 * Whether this TaxonNode is an ascendant of the given TaxonNode
481
	 * 
482
	 * 
483
	 * @param possibleChild
484
	 * @return true if there are ascendants
485
	 */
486
	@Transient
487
	public boolean isAscendant(TaxonNode possibleChild){
488
		return possibleChild.getAscendants().contains(this);
489
	}
490
	
491
	/**
492
	 * Whether this taxon has child nodes
493
	 * 
494
	 * @return true if the taxonNode has childNodes
495
	 */
496
	@Transient
497
	public boolean hasChildNodes(){
498
		return childNodes.size() > 0;
499
	}
500
}
(10-10/15)