Project

General

Profile

Download (15.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.ArrayList;
14
import java.util.HashSet;
15
import java.util.Iterator;
16
import java.util.List;
17
import java.util.Set;
18

    
19
import javax.persistence.Entity;
20
import javax.persistence.FetchType;
21
import javax.persistence.JoinColumn;
22
import javax.persistence.ManyToOne;
23
import javax.persistence.OneToMany;
24
import javax.persistence.OneToOne;
25
import javax.persistence.OrderColumn;
26
import javax.persistence.Transient;
27
import javax.validation.constraints.NotNull;
28
import javax.xml.bind.annotation.XmlAccessType;
29
import javax.xml.bind.annotation.XmlAccessorType;
30
import javax.xml.bind.annotation.XmlElement;
31
import javax.xml.bind.annotation.XmlElementWrapper;
32
import javax.xml.bind.annotation.XmlIDREF;
33
import javax.xml.bind.annotation.XmlRootElement;
34
import javax.xml.bind.annotation.XmlSchemaType;
35
import javax.xml.bind.annotation.XmlType;
36

    
37
import org.apache.log4j.Logger;
38
import org.hibernate.annotations.Cascade;
39
import org.hibernate.annotations.CascadeType;
40
import org.hibernate.envers.Audited;
41
import org.hibernate.search.annotations.Indexed;
42
import org.hibernate.search.annotations.IndexedEmbedded;
43

    
44
import eu.etaxonomy.cdm.model.common.IReferencedEntity;
45
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
46
import eu.etaxonomy.cdm.model.common.Language;
47
import eu.etaxonomy.cdm.model.common.LanguageString;
48
import eu.etaxonomy.cdm.model.reference.Reference;
49
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
50

    
51
/**
52
 * @author a.mueller
53
 * @created 31.03.2009
54
 */
55
@XmlAccessorType(XmlAccessType.FIELD)
56
@XmlType(name = "Classification", propOrder = {
57
    "name",
58
    "rootNodes",
59
    "reference",
60
    "microReference"
61
})
62
@XmlRootElement(name = "Classification")
63
@Entity
64
@Audited
65
@Indexed(index = "eu.etaxonomy.cdm.model.taxon.Classification")
66
public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheStrategy> implements IReferencedEntity, ITaxonTreeNode, Cloneable{
67
	private static final long serialVersionUID = -753804821474209635L;
68
	private static final Logger logger = Logger.getLogger(Classification.class);
69
	
70
	@XmlElement(name = "Name")
71
	@OneToOne(fetch = FetchType.LAZY)
72
	@Cascade({CascadeType.SAVE_UPDATE})
73
	@JoinColumn(name = "name_id", referencedColumnName = "id")
74
	@IndexedEmbedded
75
	private LanguageString name;
76
	
77
	@XmlElementWrapper(name = "rootNodes")
78
	@XmlElement(name = "rootNode")
79
    @XmlIDREF
80
    @XmlSchemaType(name = "IDREF")
81
	@OrderColumn(name="sortIndex")   //creates problems (#3820) 
82
	@OneToMany(fetch=FetchType.LAZY)
83
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
84
	//TODO
85
//	@NotNull // avoids creating a UNIQUE key for this field
86
	private List<TaxonNode> rootNodes = new ArrayList<TaxonNode>();
87

    
88
	@XmlElement(name = "reference")
89
	@XmlIDREF
90
	@XmlSchemaType(name = "IDREF")
91
	@ManyToOne(fetch = FetchType.LAZY)
92
	@Cascade({CascadeType.SAVE_UPDATE})
93
	private Reference<?> reference;
94
	
95
	@XmlElement(name = "microReference")
96
	private String microReference;
97
	
98
//	/**
99
//	 * If this classification is an alternative classification for a subclassification in 
100
//	 * an other classification(parent view),
101
//	 * the alternativeViewRoot is the connection node from this classification to the parent classification.
102
//	 * It replaces another node in the parent view.
103
//	 */
104
//	private AlternativeViewRoot alternativeViewRoot;
105
	
106
// ********************** FACTORY METHODS *********************************************/
107
	
108
	public static Classification NewInstance(String name){
109
		return NewInstance(name, null, Language.DEFAULT());
110
	}
111
	
112
	public static Classification NewInstance(String name, Language language){
113
		return NewInstance(name, null, language);
114
	}
115
	
116
	public static Classification NewInstance(String name, Reference reference){
117
		return NewInstance(name, reference, Language.DEFAULT());
118
	}
119
	
120
	public static Classification NewInstance(String name, Reference reference, Language language){
121
		return new Classification(name, reference, language);
122
	}
123

    
124
// **************************** CONSTRUCTOR *********************************/
125
	
126
	//for hibernate use only, protected required by Javassist
127
	protected Classification(){super();}
128
	
129
	protected Classification(String name, Reference reference, Language language){
130
		this();
131
		LanguageString langName = LanguageString.NewInstance(name, language);
132
		setName(langName);
133
		setReference(reference);
134
	}
135
	
136
//********************** xxxxxxxxxxxxx ******************************************/	
137
	
138
	@Override
139
	public TaxonNode addChildNode(TaxonNode childNode, Reference citation, String microCitation) {
140
		return addChildNode(childNode, rootNodes.size(), citation, microCitation);
141
	}
142
	
143
	@Override
144
	public TaxonNode addChildNode(TaxonNode childNode, int index, Reference citation, String microCitation) {
145
		
146
		childNode.setParentTreeNode(this, index);
147
		
148
		childNode.setReference(citation);
149
		childNode.setMicroReference(microCitation);
150
//		childNode.setSynonymToBeUsed(synonymToBeUsed);
151
		
152
		return childNode;
153
	}
154

    
155
	@Override
156
	public TaxonNode addChildTaxon(Taxon taxon, Reference citation, String microCitation) {
157
		return addChildTaxon(taxon, this.rootNodes.size(), citation, microCitation);
158
	}
159
	
160
	@Override
161
	public TaxonNode addChildTaxon(Taxon taxon, int index, Reference citation, String microCitation) {
162
		return addChildNode(new TaxonNode(taxon), index, citation, microCitation);
163
	}
164
	
165
	@Override
166
	public boolean deleteChildNode(TaxonNode node) {
167
		boolean result = removeChildNode(node);
168
		
169
		node.getTaxon().removeTaxonNode(node);
170
		node.setTaxon(null);	
171
		
172
		ArrayList<TaxonNode> childNodes = new ArrayList<TaxonNode>(node.getChildNodes()); 
173
		for (TaxonNode childNode : childNodes){
174
			node.deleteChildNode(childNode);
175
		}
176
		return result;
177
	}
178
	
179
	/* (non-Javadoc)
180
	 * @see eu.etaxonomy.cdm.model.taxon.ITreeNode#removeChildNode(eu.etaxonomy.cdm.model.taxon.TaxonNode)
181
	 */
182
	public boolean deleteChildNode(TaxonNode node, boolean deleteChildren) {
183
		boolean result = removeChildNode(node);
184
		
185
		//node.getTaxon().removeTaxonNode(node);
186
		node.setTaxon(null);	
187
		if (deleteChildren){
188
			ArrayList<TaxonNode> childNodes = new ArrayList<TaxonNode>(node.getChildNodes()); 
189
			for (TaxonNode childNode : childNodes){
190
				node.deleteChildNode(childNode);
191
			}
192
		}
193
		return result;
194
	}
195
	
196
	/**
197
	 * 
198
	 * @param node
199
	 * @return
200
	 */
201
	protected boolean removeChildNode(TaxonNode node){
202
		boolean result = false;
203
		if(!rootNodes.contains(node)){
204
			throw new IllegalArgumentException("TaxonNode is a not a root node of this classification");
205
		}
206
		
207
		result = rootNodes.remove(node);
208

    
209
		node.setParent(null);
210
		node.setClassification(null);
211
		
212
		return result;
213
	}
214
	
215
	/**
216
	 * Appends an existing topmost node to another node of this tree. The existing topmost node becomes 
217
	 * an ordinary node.
218
	 * @param topmostNode
219
	 * @param otherNode
220
	 * @param ref
221
	 * @param microReference
222
	 * @throws IllegalArgumentException
223
	 */
224
	public void makeTopmostNodeChildOfOtherNode(TaxonNode topmostNode, TaxonNode otherNode, Reference ref, String microReference)
225
				throws IllegalArgumentException{
226
		if (otherNode == null){
227
			throw new NullPointerException("other node must not be null");
228
		}
229
		if (! getChildNodes().contains(topmostNode)){
230
			throw new IllegalArgumentException("root node to be added as child must already be root node within this tree");
231
		}
232
		if (otherNode.getClassification() == null || ! otherNode.getClassification().equals(this)){
233
			throw new IllegalArgumentException("other node must already be node within this tree");
234
		}
235
		if (otherNode.equals(topmostNode)){
236
			throw new IllegalArgumentException("root node and other node must not be the same");
237
		}
238
		otherNode.addChildNode(topmostNode, ref, microReference);
239
		//getRootNodes().remove(root);
240
	}
241
	
242

    
243
	/**
244
	 * Checks if the given taxon is part of <b>this</b> tree.
245
	 * @param taxon
246
	 * @return
247
	 */
248
	public boolean isTaxonInTree(Taxon taxon){
249
		return (getNode(taxon) != null);
250
	}
251
	
252
	/**
253
	 * Checks if the given taxon is part of <b>this</b> tree. If so the according TaxonNode is returned.
254
	 * Otherwise null is returned.
255
	 * @param taxon
256
	 * @return
257
	 */
258
	public TaxonNode getNode(Taxon taxon){
259
		if (taxon == null){
260
			return null;
261
		}
262
		for (TaxonNode taxonNode: taxon.getTaxonNodes()){
263
			if (taxonNode.getClassification().equals(this)){
264
				return taxonNode;
265
			}
266
		}
267
		return null;
268
	}
269
	
270
	/**
271
	 * Checks if the given taxon is one of the topmost taxa in <b>this</b> tree.
272
	 * @param taxon
273
	 * @return
274
	 */
275
	public boolean isTopmostInTree(Taxon taxon){
276
		return (getTopmostNode(taxon) != null);
277
	}	
278
	
279
	
280
	/**
281
	 * Checks if the taxon is a direct child of <b>this</b> tree and returns the according node if true.
282
	 * Returns null otherwise.
283
	 * @param taxon
284
	 * @return
285
	 */
286
	public TaxonNode getTopmostNode(Taxon taxon){
287
		if (taxon == null){
288
			return null;
289
		}
290
		for (TaxonNode taxonNode: taxon.getTaxonNodes()){
291
			if (taxonNode.getClassification().equals(this)){
292
				if (this.getChildNodes().contains(taxonNode)){
293
					if (taxonNode.getParentTreeNode() instanceof TaxonNode){
294
						logger.warn("A topmost node should have a Classification as parent but actually has a TaxonNode parent");
295
					}
296
					return taxonNode;
297
				}
298
			}
299
		}
300
		return null;
301
	}
302

    
303
	private boolean handleCitationOverwrite(TaxonNode childNode, Reference citation, String microCitation){
304
		if (citation != null){
305
			if (childNode.getReference() != null && ! childNode.getReference().equals(citation)){
306
				logger.warn("ReferenceForParentChildRelation will be overwritten");
307
			}
308
			childNode.setReference(citation);
309
		}
310
		if (microCitation != null){
311
			if (childNode.getMicroReference() != null && ! childNode.getMicroReference().equals(microCitation)){
312
				logger.warn("MicroReferenceForParentChildRelation will be overwritten");
313
			}
314
			childNode.setMicroReference(microCitation);
315
		}
316
		return true;
317
	}
318
	
319
	/**
320
	 * Relates two taxa as parent-child nodes within a classification. <BR>
321
	 * If the taxa are not yet part of the tree they are added to it.<Br>
322
	 * If the child taxon is a topmost node still it is added as child and deleted from the rootNode set.<Br>
323
	 * If the child is a child of another parent already an IllegalStateException is thrown because a child can have only 
324
	 * one parent. <Br>
325
	 * If the parent-child relationship between these two taxa already exists nothing is changed. Only 
326
	 * citation and microcitation are overwritten by the new values if these values are not null.
327
	 *  
328
	 * @param parent
329
	 * @param child
330
	 * @param citation
331
	 * @param microCitation
332
	 * @return the childNode
333
	 * @throws IllegalStateException If the child is a child of another parent already
334
	 */
335
	public TaxonNode addParentChild (Taxon parent, Taxon child, Reference citation, String microCitation)
336
			throws IllegalStateException{
337
		try {
338
			if (parent == null || child == null){
339
				logger.warn("Child or parent taxon is null.");
340
				return null;
341
			}
342
			if (parent == child){
343
				logger.warn("A taxon should never be its own child. Child not added");
344
				return null;
345
			}
346
			TaxonNode parentNode = this.getNode(parent);
347
			TaxonNode childNode = this.getNode(child);
348
			
349
			//if child exists in tree and has a parent 
350
			//no multiple parents are allowed in the tree
351
			if (childNode != null && ! childNode.isTopmostNode()){
352
				//...different to the parent taxon  throw exception
353
				if ((childNode.getParentTreeNode() instanceof TaxonNode) && !((TaxonNode)childNode.getParent()).getTaxon().equals(parent) ){
354
					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()) ;
355
				//... same as the parent taxon do nothing but overwriting citation and microCitation
356
				}else{
357
					handleCitationOverwrite(childNode, citation, microCitation);
358
					return childNode;
359
				}
360
			}
361
			
362
			//add parent node if not exist
363
			if (parentNode == null){
364
				parentNode = this.addChildTaxon(parent, null, null);
365
			}
366
			
367
			//add child if not exists
368
			if (childNode == null){
369
				childNode = parentNode.addChildTaxon(child, citation, microCitation);
370
			}else{
371
				//child is still topmost node
372
				//TODO test if child is topmostNode otherwise throw IllegalStateException
373
				if (! this.isTopmostInTree(child)){
374
					throw new IllegalStateException("Child is not a topmost node but must be");
375
				}
376
				this.makeTopmostNodeChildOfOtherNode(childNode, parentNode, citation, microCitation);
377
			}
378
			return childNode;
379
		} catch (IllegalStateException e) {
380
			throw e;
381
		} catch (RuntimeException e){
382
			throw e;
383
		}
384
	}
385
	
386
	
387
	@Transient
388
	public Reference getCitation() {
389
		return reference;
390
	}
391
	
392
	public LanguageString getName() {
393
		return name;
394
	}
395

    
396
	public void setName(LanguageString name) {
397
		this.name = name;
398
	}
399

    
400
	/**
401
	 * Returns a set containing all nodes in this classification.
402
	 * 
403
	 * Caution: Use this method with care. It can be very time and resource consuming and might
404
	 * run into OutOfMemoryExceptions for big trees. 
405
	 * 
406
	 * @return
407
	 */
408
	@Transient
409
	public Set<TaxonNode> getAllNodes() {
410
		Set<TaxonNode> allNodes = new HashSet<TaxonNode>();
411
		
412
		for(TaxonNode rootNode : getChildNodes()){
413
			allNodes.addAll(rootNode.getDescendants());
414
		}
415
		
416
		return allNodes;
417
	}	
418
	
419
	@Override
420
	public List<TaxonNode> getChildNodes() {
421
		return rootNodes;
422
	}
423

    
424
	private void setRootNodes(List<TaxonNode> rootNodes) {
425
		this.rootNodes = rootNodes;
426
	}
427

    
428
	@Override
429
	public Reference getReference() {
430
		return reference;
431
	}
432

    
433
	public void setReference(Reference reference) {
434
		this.reference = reference;
435
	}
436
	
437

    
438
	@Override
439
	public String getMicroReference() {
440
		return microReference;
441
	}
442

    
443
	/**
444
	 * @param microReference the microReference to set
445
	 */
446
	public void setMicroReference(String microReference) {
447
		this.microReference = microReference;
448
	}
449

    
450
	/* (non-Javadoc)
451
	 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
452
	 */
453
	@Override
454
	public String generateTitle() {
455
		return name.getText();
456
	}
457

    
458
	public int compareTo(Object o) {
459
		return 0;
460
	}
461

    
462

    
463
	@Override
464
	public boolean hasChildNodes() {
465
		return getChildNodes().size() > 0;
466
	}
467
	
468
	//*********************** CLONE ********************************************************/
469
	/** 
470
	 * Clones <i>this</i> classification. This is a shortcut that enables to create
471
	 * a new instance that differs only slightly from <i>this</i> classification by
472
	 * modifying only some of the attributes.<BR><BR>
473
	 
474
	 * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
475
	 * @see java.lang.Object#clone()
476
	 */
477
	@Override
478
	public Object clone() {
479
		Classification result;
480
		try{
481
			result = (Classification)super.clone();
482
			result.rootNodes = new ArrayList<TaxonNode>();
483
			List<TaxonNode> rootNodes = new ArrayList<TaxonNode>();
484
			TaxonNode rootNodeClone;
485
			rootNodes = this.rootNodes;
486
			TaxonNode rootNode;
487
			Iterator<TaxonNode> iterator = rootNodes.iterator();
488
			
489
			while (iterator.hasNext()){
490
				rootNode = iterator.next();
491
				rootNodeClone = rootNode.cloneDescendants();
492
				rootNodeClone.setClassification(result);
493
				result.addChildNode(rootNodeClone, rootNode.getReference(), rootNode.getMicroReference());
494
				rootNodeClone.setSynonymToBeUsed(rootNode.getSynonymToBeUsed());
495
			}
496
			
497
			return result;
498
		
499
		}catch (CloneNotSupportedException e) {
500
			logger.warn("Object does not implement cloneable");
501
			e.printStackTrace();
502
			return null;
503
		}
504
		
505
		
506
		
507
		
508
	}
509
}
(2-2/18)