Project

General

Profile

Download (13.8 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.JoinColumn;
20
import javax.persistence.ManyToOne;
21
import javax.persistence.OneToMany;
22
import javax.persistence.OneToOne;
23
import javax.persistence.Transient;
24
import javax.validation.constraints.NotNull;
25
import javax.xml.bind.annotation.XmlAccessType;
26
import javax.xml.bind.annotation.XmlAccessorType;
27
import javax.xml.bind.annotation.XmlElement;
28
import javax.xml.bind.annotation.XmlElementWrapper;
29
import javax.xml.bind.annotation.XmlIDREF;
30
import javax.xml.bind.annotation.XmlRootElement;
31
import javax.xml.bind.annotation.XmlSchemaType;
32
import javax.xml.bind.annotation.XmlType;
33

    
34
import org.apache.log4j.Logger;
35
import org.hibernate.annotations.Cascade;
36
import org.hibernate.annotations.CascadeType;
37
import org.hibernate.envers.Audited;
38
import org.hibernate.search.annotations.Indexed;
39
import org.hibernate.search.annotations.IndexedEmbedded;
40

    
41
import eu.etaxonomy.cdm.model.common.IReferencedEntity;
42
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
43
import eu.etaxonomy.cdm.model.common.Language;
44
import eu.etaxonomy.cdm.model.common.LanguageString;
45
import eu.etaxonomy.cdm.model.reference.ReferenceBase;
46

    
47
/**
48
 * @author a.mueller
49
 * @created 31.03.2009
50
 * @version 1.0
51
 */
52
@XmlAccessorType(XmlAccessType.FIELD)
53
@XmlType(name = "TaxonomicTree", propOrder = {
54
    "name",
55
    "rootNodes",
56
    "reference",
57
    "microReference"
58
})
59
@XmlRootElement(name = "TaxonomicTree")
60
@Entity
61
@Audited
62
@Indexed(index = "eu.etaxonomy.cdm.model.taxon.TaxonomicTree")
63
public class TaxonomicTree extends IdentifiableEntity implements IReferencedEntity, ITreeNode{
64
	private static final long serialVersionUID = -753804821474209635L;
65
	private static final Logger logger = Logger.getLogger(TaxonomicTree.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 ReferenceBase 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 TaxonomicTree NewInstance(String name){
104
		return NewInstance(name, null, Language.DEFAULT());
105
	}
106
	
107
	public static TaxonomicTree NewInstance(String name, Language language){
108
		return NewInstance(name, null, language);
109
	}
110
	
111
	public static TaxonomicTree NewInstance(String name, ReferenceBase reference){
112
		return NewInstance(name, reference, Language.DEFAULT());
113
	}
114
	
115
	public static TaxonomicTree NewInstance(String name, ReferenceBase reference, Language language){
116
		return new TaxonomicTree(name, reference, language);
117
	}
118
	
119
	protected TaxonomicTree(String name, ReferenceBase reference, Language language){
120
		this();
121
		LanguageString langName = LanguageString.NewInstance(name, language);
122
		setName(langName);
123
		setReference(reference);
124
	}
125
	
126
	protected TaxonomicTree(){
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.ReferenceBase, java.lang.String, eu.etaxonomy.cdm.model.taxon.Synonym)
133
	 */
134
	public TaxonNode addChildNode(TaxonNode childNode, ReferenceBase 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.ReferenceBase, java.lang.String, eu.etaxonomy.cdm.model.taxon.Synonym)
148
	 */
149
	public TaxonNode addChildTaxon(Taxon taxon, ReferenceBase 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 taxonomic tree");
181
		}
182
		
183
		result = rootNodes.remove(node);
184

    
185
		node.setParent(null);
186
		node.setTaxonomicTree(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, ReferenceBase 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.getTaxonomicTree() == null || ! otherNode.getTaxonomicTree().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.getTaxonomicTree().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.getTaxonomicTree().equals(this)){
268
				if (this.getChildNodes().contains(taxonNode)){
269
					if (taxonNode.getParentTreeNode() instanceof TaxonNode){
270
						logger.warn("A topmost node should have a TaxonomicTree 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, ReferenceBase 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 taxonomic tree. <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, ReferenceBase 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. Chilc 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 ReferenceBase 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 taxonomic tree.
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 ReferenceBase getReference() {
404
		return reference;
405
	}
406

    
407
	public void setReference(ReferenceBase 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
}
(14-14/16)