Project

General

Profile

Download (73 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 EDIT
3
* European Distributed Institute of Taxonomy 
4
* http://www.e-taxonomy.eu
5
* 
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9

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

    
12

    
13
import java.lang.reflect.Field;
14
import java.util.ArrayList;
15
import java.util.Collections;
16
import java.util.HashMap;
17
import java.util.HashSet;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.Set;
21

    
22
import javax.persistence.Entity;
23
import javax.persistence.FetchType;
24
import javax.persistence.ManyToOne;
25
import javax.persistence.OneToMany;
26
import javax.persistence.Transient;
27
import javax.validation.Valid;
28
import javax.validation.constraints.NotNull;
29
import javax.xml.bind.annotation.XmlAccessType;
30
import javax.xml.bind.annotation.XmlAccessorType;
31
import javax.xml.bind.annotation.XmlAttribute;
32
import javax.xml.bind.annotation.XmlElement;
33
import javax.xml.bind.annotation.XmlElementWrapper;
34
import javax.xml.bind.annotation.XmlIDREF;
35
import javax.xml.bind.annotation.XmlRootElement;
36
import javax.xml.bind.annotation.XmlSchemaType;
37
import javax.xml.bind.annotation.XmlType;
38

    
39
import org.apache.log4j.Logger;
40
import org.hibernate.annotations.Cascade;
41
import org.hibernate.annotations.CascadeType;
42
import org.hibernate.envers.Audited;
43
import org.hibernate.search.annotations.Indexed;
44
import org.springframework.beans.factory.annotation.Configurable;
45
import org.springframework.util.ReflectionUtils;
46

    
47
import eu.etaxonomy.cdm.model.common.IRelated;
48
import eu.etaxonomy.cdm.model.common.RelationshipBase;
49
import eu.etaxonomy.cdm.model.description.TaxonDescription;
50
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
51
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
52
import eu.etaxonomy.cdm.model.reference.Reference;
53
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
54
import eu.etaxonomy.cdm.strategy.cache.taxon.TaxonBaseDefaultCacheStrategy;
55

    
56
/**
57
 * The class for "accepted/correct" {@link TaxonBase taxa} (only these taxa according to
58
 * the opinion of the {@link eu.etaxonomy.cdm.model.reference.Reference reference} can build a classification).
59
 * An {@link java.lang.Iterable interface} is supported to iterate through taxonomic children.<BR>
60
 * Splitting taxa in "accepted/correct" and {@link Synonym "synonyms"} makes it easier to handle
61
 * particular relationships between ("accepted/correct") taxa on the one hand
62
 * and between ("synonym") taxa and ("accepted/correct") taxa on the other.
63
 * 
64
 * @author m.doering
65
 * @version 1.0
66
 * @created 08-Nov-2007 13:06:56
67
 */
68
@XmlAccessorType(XmlAccessType.FIELD)
69
@XmlType(name = "Taxon", propOrder = {
70
    "taxonomicParentCache",
71
    "taxonNodes",
72
    "taxonomicChildrenCount",
73
    "synonymRelations",
74
    "relationsFromThisTaxon",
75
    "relationsToThisTaxon",
76
    "descriptions"
77
})
78
@XmlRootElement(name = "Taxon")
79
@Entity
80
@Indexed(index = "eu.etaxonomy.cdm.model.taxon.TaxonBase")
81
@Audited
82
@Configurable
83
public class Taxon extends TaxonBase<IIdentifiableEntityCacheStrategy<Taxon>> implements IRelated<RelationshipBase>, Cloneable{
84
	private static final long serialVersionUID = -584946869762749006L;
85
	private static final Logger logger = Logger.getLogger(Taxon.class);
86

    
87
	@XmlElementWrapper(name = "Descriptions")
88
	@XmlElement(name = "Description")
89
	@OneToMany(mappedBy="taxon", fetch= FetchType.LAZY) 
90
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
91
	@NotNull
92
	private Set<TaxonDescription> descriptions = new HashSet<TaxonDescription>();
93

    
94
	// all related synonyms
95
	@XmlElementWrapper(name = "SynonymRelations")
96
	@XmlElement(name = "SynonymRelationship")
97
    @OneToMany(mappedBy="relatedTo", fetch=FetchType.LAZY)
98
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
99
    @NotNull
100
    @Valid
101
	private Set<SynonymRelationship> synonymRelations = new HashSet<SynonymRelationship>();
102

    
103
	// all taxa relations with rel.fromTaxon==this
104
	@XmlElementWrapper(name = "RelationsFromThisTaxon")
105
	@XmlElement(name = "FromThisTaxonRelationship")
106
    @OneToMany(mappedBy="relatedFrom", fetch=FetchType.LAZY)
107
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
108
	@NotNull
109
	@Valid
110
	private Set<TaxonRelationship> relationsFromThisTaxon = new HashSet<TaxonRelationship>();
111

    
112
	// all taxa relations with rel.toTaxon==this
113
	@XmlElementWrapper(name = "RelationsToThisTaxon")
114
	@XmlElement(name = "ToThisTaxonRelationship")
115
    @XmlIDREF
116
    @XmlSchemaType(name = "IDREF")
117
    @OneToMany(mappedBy="relatedTo", fetch=FetchType.LAZY)
118
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
119
	@NotNull
120
	@Valid
121
	private Set<TaxonRelationship> relationsToThisTaxon = new HashSet<TaxonRelationship>();
122

    
123
	@XmlAttribute(name= "taxonStatusUnknown")
124
	private boolean taxonStatusUnknown = false;
125

    
126
	@XmlAttribute(name= "unplaced")
127
	private boolean unplaced = false;
128

    
129
	@XmlAttribute(name= "excluded")
130
	private boolean excluded = false;
131
	
132
	// shortcut to the taxonomicIncluded (parent) taxon. Managed by the taxonRelations setter
133
	@XmlElement(name = "TaxonomicParentCache")
134
	@XmlIDREF
135
	@XmlSchemaType(name = "IDREF")
136
	@ManyToOne(fetch = FetchType.LAZY)
137
	@Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
138
	private Taxon taxonomicParentCache;
139
	
140
	
141
	
142
	@XmlElementWrapper(name = "taxonNodes")
143
	@XmlElement(name = "taxonNode")
144
    @XmlIDREF
145
    @XmlSchemaType(name = "IDREF")
146
    @OneToMany(mappedBy="taxon", fetch=FetchType.LAZY)
147
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
148
	private Set<TaxonNode> taxonNodes = new HashSet<TaxonNode>();
149

    
150
	//cached number of taxonomic children
151
	@XmlElement(name = "TaxonomicChildrenCount")
152
	@Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
153
	private int taxonomicChildrenCount;
154
	
155
// ************* CONSTRUCTORS *************/	
156

    
157
	//TODO should be private, but still produces Spring init errors
158
	@Deprecated
159
	public Taxon(){
160
		this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Taxon>();
161
	}
162
	
163
	/** 
164
	 * Class constructor: creates a new (accepted/correct) taxon instance with
165
	 * the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
166
	 * using it.
167
	 * 
168
	 * @param  taxonNameBase	the taxon name used
169
	 * @param  sec				the reference using the taxon name
170
	 * @see    					TaxonBase#TaxonBase(TaxonNameBase, Reference)
171
	 */
172
	public Taxon(TaxonNameBase taxonNameBase, Reference sec){
173
		super(taxonNameBase, sec);
174
		this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Taxon>();
175
	}
176
	 
177
//********* METHODS **************************************/
178

    
179
	/** 
180
	 * Creates a new (accepted/correct) taxon instance with
181
	 * the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
182
	 * using it.
183
	 * 
184
	 * @param  taxonNameBase	the taxon name used
185
	 * @param  sec				the reference using the taxon name
186
	 * @see    					#Taxon(TaxonNameBase, Reference)
187
	 */
188
	public static Taxon NewInstance(TaxonNameBase taxonNameBase, Reference sec){
189
		Taxon result = new Taxon(taxonNameBase, sec);
190
		return result;
191
	}
192

    
193
	/** 
194
	 * Creates a new taxon instance with an unknown status (accepted/synonym) and with
195
	 * the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
196
	 * using it.
197
	 * 
198
	 * @param  taxonNameBase	the taxon name used
199
	 * @param  sec				the reference using the taxon name
200
	 * @see    					#Taxon(TaxonNameBase, Reference)
201
	 */
202
	public static Taxon NewUnknownStatusInstance(TaxonNameBase taxonNameBase, Reference sec){
203
		Taxon result = new Taxon(taxonNameBase, sec);
204
		result.setTaxonStatusUnknown(true);
205
		return result;
206
	}
207
	
208
	/** 
209
	 * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon descriptions}
210
	 * concerning <i>this</i> taxon.
211
	 * 
212
	 * @see #removeDescription(TaxonDescription)
213
	 * @see #addDescription(TaxonDescription)
214
	 * @see eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
215
	 */
216
	public Set<TaxonDescription> getDescriptions() {
217
		if(descriptions == null) {
218
			descriptions = new HashSet<TaxonDescription>();
219
		}
220
		return descriptions;
221
	}
222

    
223
	/** 
224
	 * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon description} to the set
225
	 * of taxon descriptions assigned to <i>this</i> (accepted/correct) taxon.
226
	 * Due to bidirectionality the content of the {@link eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon() taxon attribute} of the
227
	 * taxon description itself will be replaced with <i>this</i> taxon. The taxon
228
	 * description will also be removed from the set of taxon descriptions
229
	 * assigned to its previous taxon. 
230
	 *
231
	 * @param  description	the taxon description to be added for <i>this</i> taxon
232
	 * @see     		  	#getDescriptions()
233
	 * @see     		  	#removeDescription(TaxonDescription)
234
	 * @see 			  	eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
235
	 */
236
	public void addDescription(TaxonDescription description) {
237
		if (description.getTaxon() != null){
238
			description.getTaxon().removeDescription(description);
239
		}
240
		Field field = ReflectionUtils.findField(TaxonDescription.class, "taxon", Taxon.class);
241
		ReflectionUtils.makeAccessible(field);
242
		ReflectionUtils.setField(field, description, this);
243
		descriptions.add(description);
244
		
245
	}
246
	/** 
247
	 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon descriptions} assigned
248
	 * to <i>this</i> (accepted/correct) taxon. Due to bidirectionality the content of
249
	 * the {@link eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon() taxon attribute} of the taxon description
250
	 * itself will be set to "null".
251
	 *
252
	 * @param  description  the taxon description which should be removed
253
	 * @see     		  	#getDescriptions()
254
	 * @see     		  	#addDescription(TaxonDescription)
255
	 * @see 			  	eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
256
	 */
257
	public void removeDescription(TaxonDescription description) {
258
		//description.setTaxon(null) for not visible method
259
		Field field = ReflectionUtils.findField(TaxonDescription.class, "taxon", Taxon.class);
260
		ReflectionUtils.makeAccessible(field);
261
		ReflectionUtils.setField(field, description, null);
262
		descriptions.remove(description);
263
	}
264
	
265
	/**
266
	 * Returns the image gallery for a taxon. If there are multiple taxon descriptions
267
	 * marked as image galleries an arbitrary one is chosen.
268
	 * If no image gallery exists, a new one is created if <code>createNewIfNotExists</code>
269
	 * is <code>true</code>.
270
	 * @param createNewIfNotExists
271
	 * @return
272
	 */
273
	public TaxonDescription getImageGallery(boolean createNewIfNotExists) {
274
		TaxonDescription result = null;
275
		Set<TaxonDescription> descriptions= getDescriptions();
276
		for (TaxonDescription description : descriptions){
277
			if (description.isImageGallery()){
278
				result = description;
279
				break;
280
			}
281
		}
282
		if (result == null && createNewIfNotExists){
283
			result = TaxonDescription.NewInstance(this);
284
			result.setImageGallery(true);
285
		}
286
		return result;
287
	}
288
	
289

    
290
	
291
	public Set<TaxonNode> getTaxonNodes() {
292
		return taxonNodes;
293
	}
294
	//	protected void setTaxonNodes(Set<TaxonNode> taxonNodes) {
295
//		this.taxonNodes = taxonNodes;
296
//	}
297
	protected void addTaxonNode(TaxonNode taxonNode){
298
		taxonNodes.add(taxonNode);
299
	}
300
	protected void removeTaxonNode(TaxonNode taxonNode){
301
		taxonNodes.remove(taxonNode);
302
	}
303

    
304

    
305
	
306
	
307
	/** 
308
	 * Returns the set of all {@link SynonymRelationship synonym relationships}
309
	 * in which <i>this</i> ("accepted/correct") taxon is involved. <i>This</i> taxon can only
310
	 * be the target of these synonym relationships. 
311
	 *  
312
	 * @see    #addSynonymRelation(SynonymRelationship)
313
	 * @see    #removeSynonymRelation(SynonymRelationship)
314
	 * @see    #getSynonyms()
315
	 */
316
	public Set<SynonymRelationship> getSynonymRelations() {
317
		if(synonymRelations == null) {
318
			this.synonymRelations = new HashSet<SynonymRelationship>();
319
		}
320
		return synonymRelations;
321
	}
322
	
323
	/**
324
	 * Adds an existing {@link SynonymRelationship synonym relationship} to the set of
325
	 * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon. If
326
	 * the target of the synonym relationship does not match with <i>this</i> taxon
327
	 * no addition will be carried out.
328
	 * 
329
	 * @param synonymRelation	the synonym relationship to be added to <i>this</i> taxon's
330
	 * 							synonym relationships set
331
	 * @see    	   				#getSynonymRelations()
332
	 * @see    	   				#addSynonym(Synonym, SynonymRelationshipType)
333
	 * @see    	   				#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
334
	 * @see    	   				#addSynonymName(TaxonNameBase, SynonymRelationshipType)
335
	 * @see    	   				#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
336
	 */
337
	protected void addSynonymRelation(SynonymRelationship synonymRelation) {
338
		this.synonymRelations.add(synonymRelation);
339
	}
340
	/** 
341
	 * Removes one element from the set of {@link SynonymRelationship synonym relationships} assigned
342
	 * to <i>this</i> (accepted/correct) taxon. Due to bidirectionality the given
343
	 * synonym relationship will also be removed from the set of synonym
344
	 * relationships assigned to the {@link Synonym#getSynonymRelations() synonym} involved in the
345
	 * relationship. Furthermore the content of
346
	 * the {@link SynonymRelationship#getAcceptedTaxon() accepted taxon} attribute and of the
347
	 * {@link SynonymRelationship#getSynonym() synonym} attribute within the synonym relationship
348
	 * itself will be set to "null".
349
	 *
350
	 * @param synonymRelation  	the synonym relationship which should be deleted
351
	 * @param removeSynonymNameFromHomotypicalGroup 
352
	 * 							if set to true the synonym name will also be deleted from its homotypical group
353
	 * @see     		  		#getSynonymRelations()
354
	 * @see     		  		#addSynonymRelation(SynonymRelationship)
355
	 * @see 			  		#removeSynonym(Synonym)
356
	 */
357
	public void removeSynonymRelation(SynonymRelationship synonymRelation, boolean removeSynonymNameFromHomotypicalGroup) {
358
		synonymRelation.setAcceptedTaxon(null);
359
		Synonym synonym = synonymRelation.getSynonym();
360
		if (synonym != null){
361
			synonymRelation.setSynonym(null);
362
			synonym.removeSynonymRelation(synonymRelation);
363
			if(removeSynonymNameFromHomotypicalGroup){
364
				synonym.getName().getHomotypicalGroup().removeTypifiedName(synonym.getName());
365
			}
366
		}
367
		this.synonymRelations.remove(synonymRelation);
368
	}
369
	
370
	/**
371
	 * Like {@link Taxon#removeSynonymRelation(SynonymRelationship, boolean)} but synonym name 
372
	 * will be deleted from homotypical group by default
373
	 * 
374
	 * @param synonymRelation   the synonym relationship which should be deleted
375
	 * 
376
	 * @see					#removeSynonymRelation(SynonymRelationship, boolean)
377
	 */
378
	public void removeSynonymRelation(SynonymRelationship synonymRelation){
379
		removeSynonymRelation(synonymRelation, true);
380
	}
381

    
382
	
383
	/** 
384
	 * Returns the set of all {@link TaxonRelationship taxon relationships}
385
	 * between two taxa in which <i>this</i> taxon is involved as a source.
386
	 *  
387
	 * @see    #getRelationsToThisTaxon()
388
	 * @see    #getTaxonRelations()
389
	 */
390
	public Set<TaxonRelationship> getRelationsFromThisTaxon() {
391
		if(relationsFromThisTaxon == null) {
392
			this.relationsFromThisTaxon = new HashSet<TaxonRelationship>();
393
		}
394
		return relationsFromThisTaxon;
395
	}
396

    
397

    
398
	/** 
399
	 * Returns the set of all {@link TaxonRelationship taxon relationships}
400
	 * between two taxa in which <i>this</i> taxon is involved as a target.
401
	 *  
402
	 * @see    #getRelationsFromThisTaxon()
403
	 * @see    #getTaxonRelations()
404
	 */
405
	public Set<TaxonRelationship> getRelationsToThisTaxon() {
406
		if(relationsToThisTaxon == null) {
407
			this.relationsToThisTaxon = new HashSet<TaxonRelationship>();
408
		}
409
		return relationsToThisTaxon;
410
	}
411
	/** 
412
	 * @see    #getRelationsToThisTaxon()
413
	 */
414
	protected void setRelationsToThisTaxon(Set<TaxonRelationship> relationsToThisTaxon) {
415
		this.relationsToThisTaxon = relationsToThisTaxon;
416
	}
417
	
418
	/** 
419
	 * @see    #getRelationsFromThisTaxon()
420
	 */
421
	protected void setRelationsFromThisTaxon(Set<TaxonRelationship> relationsFromThisTaxon) {
422
		this.relationsFromThisTaxon = relationsFromThisTaxon;
423
	}
424

    
425
	/** 
426
	 * Returns the set of all {@link TaxonRelationship taxon relationships}
427
	 * between two taxa in which <i>this</i> taxon is involved either as a source or
428
	 * as a target.
429
	 *  
430
	 * @see    #getRelationsFromThisTaxon()
431
	 * @see    #getRelationsToThisTaxon()
432
	 */
433
	@Transient
434
	public Set<TaxonRelationship> getTaxonRelations() {
435
		Set<TaxonRelationship> rels = new HashSet<TaxonRelationship>();
436
		rels.addAll(getRelationsToThisTaxon());
437
		rels.addAll(getRelationsFromThisTaxon());
438
		return rels;
439
	}
440
	
441
	/**
442
	 * If a relationships between <i>this</i> and the given taxon exists they will be returned.
443
	 * <i>This</i> taxon is involved either as a source or as a target in the relationships.
444
	 * The method will return <code>null</code> if no relations exist between the two taxa.
445
	 * 
446
	 * @param possiblyRelatedTaxon
447
	 * 			a taxon to check for a relationship
448
	 * @return
449
	 * 			a set of <code>TaxonRelationship</code>s or <code>null</null> if none exists.
450
	 */
451
	public Set<TaxonRelationship> getTaxonRelations(Taxon possiblyRelatedTaxon){
452
		Set<TaxonRelationship> relations = new HashSet<TaxonRelationship>();
453
		
454
		for(TaxonRelationship relationship : getTaxonRelations()){
455
			if(relationship.getFromTaxon().equals(possiblyRelatedTaxon))
456
				relations.add(relationship);
457
			if(relationship.getToTaxon().equals(possiblyRelatedTaxon))
458
				relations.add(relationship);
459
		}
460
		
461
		return relations.size() > 0 ? relations : null;
462
	}
463
	
464
	/** 
465
	 * Removes one {@link TaxonRelationship taxon relationship} from one of both sets of
466
	 * {@link #getTaxonRelations() taxon relationships} in which <i>this</i> taxon is involved
467
	 * either as a {@link #getRelationsFromThisTaxon() source} or as a {@link #getRelationsToThisTaxon() target}.
468
	 * The taxon relationship will also be removed from one of both sets
469
	 * belonging to the second taxon involved. Furthermore the inherited RelatedFrom and
470
	 * RelatedTo attributes of the given taxon relationship will be nullified.<P>
471
	 * If the taxon relationship concerns the classification possible
472
	 * modifications of the {@link #getTaxonomicParent() parent taxon} or of the number of
473
	 * {@link #getTaxonomicChildrenCount() childrens} will be stored.
474
	 *
475
	 * @param  rel  the taxon relationship which should be removed from one
476
	 * 				of both sets
477
	 * @see    		#getTaxonRelations()
478
	 * @see    	    #getTaxonomicParent()
479
	 * @see    	    #getTaxonomicChildrenCount()
480
	 * @see    		eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom()
481
	 * @see    		eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo()
482
	 * 
483
	 */
484
	public void removeTaxonRelation(TaxonRelationship rel) {
485
		this.relationsToThisTaxon.remove(rel);
486
		this.relationsFromThisTaxon.remove(rel);
487
		Taxon fromTaxon = rel.getFromTaxon();
488
		Taxon toTaxon = rel.getToTaxon();
489
		// check if this removes the taxonomical parent. If so, also remove shortcut to the higher taxon
490
		if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) ){
491
			if (fromTaxon != null && fromTaxon.equals(this)){
492
				this.taxonomicParentCache = null;
493
			}else if (toTaxon != null && toTaxon.equals(this)){
494
				this.setTaxonomicChildrenCount(computeTaxonomicChildrenCount());	
495
			}
496
		}
497
		//delete Relationship from other related Taxon
498
		if (fromTaxon != null && fromTaxon != this){
499
			rel.setToTaxon(null);  //remove this Taxon from relationship
500
			fromTaxon.removeTaxonRelation(rel);
501
		}
502
		if (toTaxon != null && toTaxon != this){
503
			rel.setFromTaxon(null); //remove this Taxon from relationship
504
			toTaxon.removeTaxonRelation(rel);
505
		}
506
	}
507

    
508
	/**
509
	 * Adds an existing {@link TaxonRelationship taxon relationship} either to the set of
510
	 * {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon} or to the set of
511
	 * {@link #getRelationsFromThisTaxon() taxon relationships from <i>this</i> taxon}. If neither the
512
	 * source nor the target of the taxon relationship match with <i>this</i> taxon
513
	 * no addition will be carried out. The taxon relationship will also be
514
	 * added to the second taxon involved in the given relationship.<P>
515
	 * If the taxon relationship concerns the classification possible
516
	 * modifications of the {@link #getTaxonomicParent() parent taxon} or of the number of
517
	 * {@link #getTaxonomicChildrenCount() childrens} will be stored.
518
	 * 
519
	 * @param rel  the taxon relationship to be added to one of <i>this</i> taxon's taxon relationships sets
520
	 * @see    	   #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
521
	 * @see    	   #getTaxonRelations()
522
	 * @see    	   #getRelationsFromThisTaxon()
523
	 * @see    	   #getRelationsToThisTaxon()
524
	 * @see    	   #getTaxonomicParent()
525
	 * @see    	   #getTaxonomicChildrenCount()
526
	 */
527
	public void addTaxonRelation(TaxonRelationship rel) {
528
		if (rel!=null && rel.getType()!=null && !getTaxonRelations().contains(rel) ){
529
			Taxon toTaxon=rel.getToTaxon();
530
			Taxon fromTaxon=rel.getFromTaxon();
531
			if ( this.equals(toTaxon) || this.equals(fromTaxon) ){
532
				if (this.equals(fromTaxon)){
533
					relationsFromThisTaxon.add(rel);
534
					// also add relation to other taxon object
535
					if (toTaxon!=null){
536
						toTaxon.addTaxonRelation(rel);
537
					}
538
					// check if this sets the taxonomical parent. If so, remember a shortcut to this taxon
539
					if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && toTaxon!=null ){
540
						this.taxonomicParentCache = toTaxon;
541
					}
542
				}else if (this.equals(toTaxon)){
543
					relationsToThisTaxon.add(rel);
544
					// also add relation to other taxon object
545
					if (fromTaxon!=null){
546
						fromTaxon.addTaxonRelation(rel);
547
					}
548
					if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && fromTaxon!=null ){
549
						this.taxonomicChildrenCount++;
550
					}
551
					
552
				}
553
			}else if (toTaxon == null || fromTaxon == null){
554
				if (toTaxon == null){
555
					toTaxon = this;
556
					relationsToThisTaxon.add(rel);
557
					if (fromTaxon!= null){
558
						fromTaxon.addTaxonRelation(rel);
559
					}
560
					if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && fromTaxon!=null ){
561
						this.taxonomicChildrenCount++;
562
					}
563
				}else if (fromTaxon == null && toTaxon != null){
564
					fromTaxon = this;
565
					relationsFromThisTaxon.add(rel);
566
					if (toTaxon!=null){
567
						toTaxon.addTaxonRelation(rel);
568
					}
569
					if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && toTaxon!=null ){
570
						this.taxonomicParentCache = toTaxon;
571
					}
572
				}
573
			}
574
		}	
575
	}
576
		
577
	/* (non-Javadoc)
578
	 * @see eu.etaxonomy.cdm.model.common.IRelated#addRelationship(eu.etaxonomy.cdm.model.common.RelationshipBase)
579
	 */
580
	@Deprecated //for inner use by RelationshipBase only
581
	public void addRelationship(RelationshipBase rel){
582
		if (rel instanceof TaxonRelationship){
583
			addTaxonRelation((TaxonRelationship)rel);
584
		}else if (rel instanceof SynonymRelationship){
585
			addSynonymRelation((SynonymRelationship)rel);
586
		}else{
587
			throw new ClassCastException("Wrong Relationsship type for Taxon.addRelationship");
588
		}
589
	}
590
	
591
	/**
592
	 * Creates a new {@link TaxonRelationship taxon relationship} instance where <i>this</i> taxon
593
	 * plays the source role and adds it to the set of
594
	 * {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to <i>this</i> taxon.
595
	 * The taxon relationship will also be added to the set of taxon
596
	 * relationships to the second taxon involved in the created relationship.<P>
597
	 * If the taxon relationship concerns the classification possible
598
	 * modifications of the {@link #getTaxonomicParent() parent taxon} or of the number of
599
	 * {@link #getTaxonomicChildrenCount() childrens} will be stored.
600
	 * 
601
	 * @param toTaxon		the taxon which plays the target role in the new taxon relationship
602
	 * @param type			the taxon relationship type for the new taxon relationship
603
	 * @param citation		the reference source for the new taxon relationship
604
	 * @param microcitation	the string with the details describing the exact localisation within the reference
605
	 * @return 
606
	 * @see    	   			#addTaxonRelation(TaxonRelationship)
607
	 * @see    	   			#getTaxonRelations()
608
	 * @see    	   			#getRelationsFromThisTaxon()
609
	 * @see    	   			#getRelationsToThisTaxon()
610
	 * @see    	   			#getTaxonomicParent()
611
	 * @see    	   			#getTaxonomicChildrenCount()
612
	 */
613
	public TaxonRelationship addTaxonRelation(Taxon toTaxon, TaxonRelationshipType type, Reference citation, String microcitation) {
614
		return new TaxonRelationship(this, toTaxon, type, citation, microcitation);
615
	}
616
	/**
617
	 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
618
	 * "misapplied name for") instance where <i>this</i> taxon plays the target role
619
	 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
620
	 * The taxon relationship will also be added to the set of taxon
621
	 * relationships to the other (misapplied name) taxon involved in the created relationship.
622
	 * 
623
	 * @param misappliedNameTaxon	the taxon which plays the target role in the new taxon relationship
624
	 * @param citation				the reference source for the new taxon relationship
625
	 * @param microcitation			the string with the details describing the exact localisation within the reference
626
	 * @return 
627
	 * @see    	   					#getMisappliedNames()
628
	 * @see    	   					#addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
629
	 * @see    	   					#addTaxonRelation(TaxonRelationship)
630
	 * @see    	   					#getTaxonRelations()
631
	 * @see    	   					#getRelationsFromThisTaxon()
632
	 * @see    	   					#getRelationsToThisTaxon()
633
	 */
634
	public TaxonRelationship addMisappliedName(Taxon misappliedNameTaxon, Reference citation, String microcitation) {
635
		return misappliedNameTaxon.addTaxonRelation(this, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), citation, microcitation);
636
	}
637

    
638
//	public void removeMisappliedName(Taxon misappliedNameTaxon){
639
//		Set<TaxonRelationship> taxRels = this.getTaxonRelations();
640
//		for (TaxonRelationship taxRel : taxRels ){
641
//			if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR()) 
642
//				&& taxRel.getFromTaxon().equals(misappliedNameTaxon)){
643
//				this.removeTaxonRelation(taxRel);
644
//			}
645
//		}
646
//	}
647
	
648
	/** 
649
	 * TODO update documentation 
650
	 * Removes one {@link TaxonRelationship taxon relationship} with {@link TaxonRelationshipType taxon relationship type}
651
	 * taxonRelType and with the given child taxon playing the
652
	 * source role from the set of {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging
653
	 * to <i>this</i> taxon. The taxon relationship will also be removed from the set
654
	 * of {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to the other side taxon.
655
	 * Furthermore, the inherited RelatedFrom and RelatedTo attributes of the
656
	 * taxon relationship will be nullified.<P>
657
	 *
658
	 * @param taxon			the taxon which plays the source role in the taxon relationship
659
	 * @param taxonRelType	the taxon relationship type
660
	 */
661
	public void removeTaxon(Taxon taxon, TaxonRelationshipType taxonRelType){
662
		Set<TaxonRelationship> taxRels = this.getTaxonRelations();
663
		for (TaxonRelationship taxRel : taxRels ){
664
			if (taxRel.getType().equals(taxonRelType) 
665
				&& taxRel.getFromTaxon().equals(taxon)){
666
				this.removeTaxonRelation(taxRel);
667
			}
668
		}
669
	}
670
	
671
	/**
672
	 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
673
	 * "taxonomically included in") instance where <i>this</i> taxon plays the target
674
	 * role (parent) and adds it to the set of
675
	 * {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging to <i>this</i> taxon.
676
	 * The taxon relationship will also be added to the set of
677
	 * {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to the second taxon
678
	 * (child) involved in the created relationship.<P>
679
	 * Since the taxon relationship concerns the modifications
680
	 * of the number of {@link #getTaxonomicChildrenCount() childrens} for <i>this</i> taxon and
681
	 * of the {@link #getTaxonomicParent() parent taxon} for the child taxon will be stored.
682
	 * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
683
	 * than the rank of the taxon name used as a child taxon.
684
	 * 
685
	 * @param child			the taxon which plays the source role (child) in the new taxon relationship
686
	 * @param citation		the reference source for the new taxon relationship
687
	 * @param microcitation	the string with the details describing the exact localisation within the reference
688
	 * @see    	   			#setTaxonomicParent(Taxon, Reference, String)
689
	 * @see    	   			#addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
690
	 * @see    	   			#addTaxonRelation(TaxonRelationship)
691
	 * @see    	   			#getTaxonRelations()
692
	 * @see    	   			#getRelationsFromThisTaxon()
693
	 * @see    	   			#getRelationsToThisTaxon()
694
	 * @see    	   			#getTaxonomicParent()
695
	 * @see    	   			#getTaxonomicChildrenCount()
696
	 */
697
	@Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
698
	public void addTaxonomicChild(Taxon child, Reference citation, String microcitation){
699
		if (child == null){
700
			throw new NullPointerException("Child Taxon is 'null'");
701
		}else{
702
			child.setTaxonomicParent(this, citation, microcitation);
703
		}
704
	}
705
	
706
	/** 
707
	 * Removes one {@link TaxonRelationship taxon relationship} with {@link TaxonRelationshipType taxon relationship type}
708
	 * "taxonomically included in" and with the given child taxon playing the
709
	 * source role from the set of {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging
710
	 * to <i>this</i> taxon. The taxon relationship will also be removed from the set
711
	 * of {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to the child taxon.
712
	 * Furthermore the inherited RelatedFrom and RelatedTo attributes of the
713
	 * taxon relationship will be nullified.<P>
714
	 * Since the taxon relationship concerns the classification modifications
715
	 * of the number of {@link #getTaxonomicChildrenCount() childrens} for <i>this</i> taxon and
716
	 * of the {@link #getTaxonomicParent() parent taxon} for the child taxon will be stored.
717
	 *
718
	 * @param  child	the taxon playing the source role in the relationship to be removed
719
	 * @see    	    	#removeTaxonRelation(TaxonRelationship)
720
	 * @see    			#getRelationsToThisTaxon()
721
	 * @see    			#getRelationsFromThisTaxon()
722
	 * @see    	    	#getTaxonomicParent()
723
	 * @see    	    	#getTaxonomicChildrenCount()
724
	 * @see    			eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom()
725
	 * @see    			eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo()
726
	 * 
727
	 */
728
	@Deprecated //will be removed in future versions. Use classification/TaxonNode instead
729
	public void removeTaxonomicChild(Taxon child){
730
		Set<TaxonRelationship> taxRels = this.getTaxonRelations();
731
		for (TaxonRelationship taxRel : taxRels ){
732
			if (taxRel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) 
733
				&& taxRel.getFromTaxon().equals(child)){
734
				this.removeTaxonRelation(taxRel);
735
			}
736
		}
737
	}
738
	
739
	/** 
740
	 * Returns the taxon which is the next higher taxon (parent) of <i>this</i> taxon
741
	 * within the classification and which is stored in the
742
	 * TaxonomicParentCache attribute. Each taxon can have only one parent taxon.
743
	 * The child taxon and the parent taxon play the source respectively the
744
	 * target role in one {@link TaxonRelationship taxon relationship} with
745
	 * {@link TaxonRelationshipType taxon relationship type} "taxonomically included in".
746
	 * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
747
	 * than the rank of the taxon name used as a child taxon.
748
	 * 
749
	 * @see  #setTaxonomicParent(Taxon, Reference, String)
750
	 * @see  #getTaxonomicChildren()
751
	 * @see  #getTaxonomicChildrenCount()
752
	 * @see  #getRelationsFromThisTaxon()
753
	 */
754
	@Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
755
	public Taxon getTaxonomicParent() {
756
		return this.taxonomicParentCache;
757
	}
758

    
759
	/** 
760
	 * Sets the taxononomic parent of <i>this</i> taxon to null.
761
	 * Note that this method does not handle taxonomic relationships.
762
	 */
763
	@Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
764
	public void nullifyTaxonomicParent() {
765
		this.taxonomicParentCache = null;
766
	}
767
		
768
	/**
769
	 * Replaces both the taxonomic parent cache with the given new parent taxon
770
	 * and the corresponding taxon relationship with a new {@link TaxonRelationship taxon relationship}
771
	 * (with {@link TaxonRelationshipType taxon relationship type} "taxonomically included in") instance.
772
	 * In the new taxon relationship <i>this</i> taxon plays the source role (child).
773
	 * This method creates and adds the new taxon relationship to the set of
774
	 * {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to <i>this</i> taxon.
775
	 * The taxon relationship will also be added to the set of
776
	 * {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging to the second taxon
777
	 * (parent) involved in the new relationship.<P>
778
	 * Since the taxon relationship concerns the classification modifications
779
	 * of the {@link #getTaxonomicParent() parent taxon} for <i>this</i> taxon and of the number of
780
	 * {@link #getTaxonomicChildrenCount() childrens} for the child taxon will be stored.
781
	 * 
782
	 * @param newParent		the taxon which plays the target role (parent) in the new taxon relationship
783
	 * @param citation		the reference source for the new taxon relationship
784
	 * @param microcitation	the string with the details describing the exact localisation within the reference
785
	 * @see    	   			#removeTaxonRelation(TaxonRelationship)
786
	 * @see    	   			#getTaxonomicParent()
787
	 * @see    	   			#addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
788
	 * @see    	   			#addTaxonRelation(TaxonRelationship)
789
	 * @see    	   			#getTaxonRelations()
790
	 * @see    	   			#getRelationsFromThisTaxon()
791
	 * @see    	   			#getRelationsToThisTaxon()
792
	 * @see    	   			#getTaxonomicChildrenCount()
793
	 */
794
	@Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
795
	public void setTaxonomicParent(Taxon newParent, Reference citation, String microcitation){
796
		//remove previously existing parent relationship!!!
797
		Taxon oldParent = this.getTaxonomicParent();
798
		Set<TaxonRelationship> taxRels = this.getTaxonRelations();
799
		for (TaxonRelationship taxRel : taxRels ){
800
			if (taxRel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && taxRel.getToTaxon().equals(oldParent)){
801
				this.removeTaxonRelation(taxRel);
802
			}
803
		}
804
		//add new parent
805
		if (newParent != null){
806
			addTaxonRelation(newParent, TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN(),citation,microcitation);
807
		}
808
	}
809

    
810
	/** 
811
	 * Returns the set of taxa which have <i>this</i> taxon as next higher taxon
812
	 * (parent) within the classification. Each taxon can have several child
813
	 * taxa. The child taxon and the parent taxon play the source respectively
814
	 * the target role in one {@link TaxonRelationship taxon relationship} with
815
	 * {@link TaxonRelationshipType taxon relationship type} "taxonomically included in".
816
	 * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
817
	 * than the rank of the taxon name used as a child taxon.
818
	 * 
819
	 * @see  #getTaxonomicParent()
820
	 * @see  #addTaxonomicChild(Taxon, Reference, String)
821
	 * @see  #getTaxonomicChildrenCount()
822
	 * @see  #getRelationsToThisTaxon()
823
	 */
824
	@Transient
825
	@Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
826
	public Set<Taxon> getTaxonomicChildren() {
827
		Set<Taxon> taxa = new HashSet<Taxon>();
828
		Set<TaxonRelationship> rels = this.getRelationsToThisTaxon();
829
		for (TaxonRelationship rel: rels){
830
			TaxonRelationshipType tt = rel.getType();
831
			TaxonRelationshipType incl = TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN(); 
832
			if (tt.equals(incl)){
833
				taxa.add(rel.getFromTaxon());
834
			}
835
		}
836
		return taxa;
837
	}
838
	
839
	/** 
840
	 * Returns the number of taxa which have <i>this</i> taxon as next higher taxon
841
	 * (parent) within the classification and the number of which is stored in
842
	 * the TaxonomicChildrenCount attribute. Each taxon can have several child
843
	 * taxa. The child taxon and the parent taxon play the source respectively
844
	 * the target role in one {@link TaxonRelationship taxon relationship} with
845
	 * {@link TaxonRelationshipType taxon relationship type} "taxonomically included in".
846
	 * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
847
	 * than the rank of the taxon name used as a child taxon.
848
	 * 
849
	 * @see  #getTaxonomicChildren()
850
	 * @see  #getRelationsToThisTaxon()
851
	 */
852
	@Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
853
	public int getTaxonomicChildrenCount(){
854
		return taxonomicChildrenCount;
855
	}	
856
	
857
	/**
858
	 * @see  #getTaxonomicChildrenCount()
859
	 */
860
	@Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
861
	public void setTaxonomicChildrenCount(int taxonomicChildrenCount) {
862
		this.taxonomicChildrenCount = taxonomicChildrenCount;
863
	}
864

    
865
	/**
866
	 * Returns the boolean value indicating whether <i>this</i> taxon has at least one
867
	 * taxonomic child taxon within the classification (true) or not (false).
868
	 * 
869
	 * @see  #getTaxonomicChildrenCount()
870
	 * @see  #getTaxonomicChildren()
871
	 */
872
	@Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
873
	public boolean hasTaxonomicChildren(){
874
		return this.taxonomicChildrenCount > 0;
875
	}
876

    
877
	@Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
878
	private int computeTaxonomicChildrenCount(){
879
		int count = 0;
880
		for (TaxonRelationship rel: this.getRelationsToThisTaxon()){
881
			if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())){
882
				count++;
883
			}
884
		}
885
		return count;
886
	}
887
	
888
	
889
	/**
890
	 * Returns the boolean value indicating whether <i>this</i> taxon is a misaplication
891
	 * (misapplied name) for at least one other taxon. 
892
	 */
893
	// TODO cache as for #hasTaxonomicChildren
894
	@Transient
895
	public boolean isMisapplication(){
896
		return computeMisapliedNameRelations() > 0;
897
	}
898
	
899
	/**
900
	 * Counts the number of misaplied names relationships where this taxon represents the
901
	 * misaplied name for another taxon.
902
	 * @return
903
	 */
904
	private int computeMisapliedNameRelations(){
905
		int count = 0;
906
		for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
907
			if (rel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
908
				count++;
909
			}
910
		}
911
		return count;
912
	}
913
	
914
	/**
915
	 * Returns the boolean value indicating whether <i>this</i> taxon is a related
916
	 * concept for at least one other taxon. 
917
	 */
918
	@Transient
919
	public boolean isRelatedConcept(){
920
		return computeConceptRelations() > 0;
921
	}
922
	
923
	/**
924
	 * Counts the number of concept relationships where this taxon represents the
925
	 * related concept for another taxon.
926
	 * @return
927
	 */
928
	private int computeConceptRelations(){
929
		int count = 0;
930
		for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
931
			TaxonRelationshipType type = rel.getType();
932
			if (type.isConceptRelationship()){
933
				count++;
934
			}
935
		}
936
		return count;
937
	}
938
	
939
	/**
940
	 * Returns the boolean value indicating whether <i>this</i> taxon has at least one
941
	 * {@link Synonym synonym} (true) or not (false). If true the {@link #getSynonymRelations() set of synonym relationships}
942
	 * belonging to <i>this</i> ("accepted/correct") taxon is not empty .
943
	 * 
944
	 * @see  #getSynonymRelations()
945
	 * @see  #getSynonyms()
946
	 * @see  #getSynonymNames()
947
	 * @see  #removeSynonym(Synonym)
948
	 * @see  SynonymRelationship
949
	 */
950
	public boolean hasSynonyms(){
951
		return this.getSynonymRelations().size() > 0;
952
	}
953

    
954
	
955
	/**
956
	 * Returns the boolean value indicating whether <i>this</i> taxon is at least
957
	 * involved in one {@link #getTaxonRelations() taxon relationship} between
958
	 * two taxa (true), either as a source or as a target, or not (false).
959
	 * 
960
	 * @see  #getTaxonRelations()
961
	 * @see  #getRelationsToThisTaxon()
962
	 * @see  #getRelationsFromThisTaxon()
963
	 * @see  #removeTaxonRelation(TaxonRelationship)
964
	 * @see  TaxonRelationship
965
	 */
966
	public boolean hasTaxonRelationships(){
967
		return this.getTaxonRelations().size() > 0;
968
	}
969

    
970
	/*
971
	 * MISAPPLIED NAMES
972
	 */
973
	/** 
974
	 * Returns the set of taxa playing the source role in {@link TaxonRelationship taxon relationships}
975
	 * (with {@link TaxonRelationshipType taxon relationship type} "misapplied name for") where
976
	 * <i>this</i> taxon plays the target role. A misapplied name is a taxon the
977
	 * {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} of which has been erroneously used
978
	 * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
979
	 * as the one meant by <i>this</i> ("accepted/correct") taxon. 
980
	 * 
981
	 * @see  #getTaxonRelations()
982
	 * @see  #getRelationsToThisTaxon()
983
	 * @see  #addMisappliedName(Taxon, Reference, String)
984
	 */
985
	@Transient
986
	public Set<Taxon> getMisappliedNames(){
987
		Set<Taxon> taxa = new HashSet<Taxon>();
988
		Set<TaxonRelationship> rels = this.getRelationsToThisTaxon();
989
		for (TaxonRelationship rel: rels){
990
			TaxonRelationshipType tt = rel.getType();
991
			TaxonRelationshipType incl = TaxonRelationshipType.MISAPPLIED_NAME_FOR(); 
992
			if (tt.equals(incl)){
993
				taxa.add(rel.getFromTaxon());
994
			}
995
		}
996
		return taxa;
997
	}
998
		
999
	
1000
	/*
1001
	 * DEALING WITH SYNONYMS
1002
	 */
1003
	/** 
1004
	 * Returns the set of all {@link Synonym synonyms} of <i>this</i> ("accepted/correct") taxon.
1005
	 * Each synonym is the source and <i>this</i> taxon is the target of a {@link SynonymRelationship synonym relationship}
1006
	 * belonging to the {@link #getSynonymRelations() set of synonym relationships} assigned to <i>this</i> taxon.
1007
	 * For a particular synonym and for a particular ("accepted/correct") taxon
1008
	 * there can be several synonym relationships (if two or more
1009
	 * {@link SynonymRelationshipType synonym relationship types} - for instance
1010
	 * "pro parte synonym of" and "is homotypic synonym of" - must be combined). 
1011
	 *  
1012
	 * @see    #getSynonymsSortedByType()
1013
	 * @see    #getSynonymNames()
1014
	 * @see    #getSynonymRelations()
1015
	 * @see    #addSynonym(Synonym, SynonymRelationshipType)
1016
	 * @see    #addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1017
	 * @see    #removeSynonymRelation(SynonymRelationship)
1018
	 * @see    #removeSynonym(Synonym)
1019
	 */
1020
	@Transient
1021
	public Set<Synonym> getSynonyms(){
1022
		Set<Synonym> syns = new HashSet<Synonym>();
1023
		for (SynonymRelationship rel: this.getSynonymRelations()){
1024
			syns.add(rel.getSynonym());
1025
		}
1026
		return syns;
1027
	}
1028
	/** 
1029
	 * Returns the set of all {@link Synonym synonyms} of <i>this</i> ("accepted/correct") taxon
1030
	 * sorted by the different {@link SynonymRelationshipType categories of synonym relationships}.
1031
	 * Each synonym is the source and <i>this</i> taxon is the target of a {@link SynonymRelationship synonym relationship}
1032
	 * belonging to the {@link #getSynonymRelations() set of synonym relationships} assigned to <i>this</i> taxon.
1033
	 *  
1034
	 * @see    #getSynonyms()
1035
	 * @see    #getSynonymNames()
1036
	 * @see    #getSynonymRelations()
1037
	 * @see    #addSynonym(Synonym, SynonymRelationshipType)
1038
	 * @see    #addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1039
	 * @see    #removeSynonymRelation(SynonymRelationship)
1040
	 * @see    #removeSynonym(Synonym)
1041
	 */
1042
	@Transient
1043
	public Set<Synonym> getSynonymsSortedByType(){
1044
		// FIXME: need to sort synonyms according to type!!!
1045
		logger.warn("getSynonymsSortedByType() not yet implemented");
1046
		return getSynonyms();
1047
	}
1048
	/** 
1049
	 * Returns the set of all {@link name.TaxonNameBase taxon names} used as {@link Synonym synonyms}
1050
	 * of <i>this</i> ("accepted/correct") taxon. Each synonym is the source and
1051
	 * <i>this</i> taxon is the target of a {@link SynonymRelationship synonym relationship} belonging
1052
	 * to the {@link #getSynonymRelations() set of synonym relationships} assigned to <i>this</i> taxon.
1053
	 *  
1054
	 * @see    #getSynonyms()
1055
	 * @see    #getSynonymsSortedByType()
1056
	 * @see    #getSynonymRelations()
1057
	 * @see    #addSynonymName(TaxonNameBase, SynonymRelationshipType)
1058
	 * @see    #addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1059
	 * @see    #removeSynonymRelation(SynonymRelationship)
1060
	 * @see    #removeSynonym(Synonym)
1061
	 */
1062
	@Transient
1063
	public Set<TaxonNameBase> getSynonymNames(){
1064
		Set<TaxonNameBase> names = new HashSet<TaxonNameBase>();
1065
		for (SynonymRelationship rel: this.getSynonymRelations()){
1066
			names.add(rel.getSynonym().getName());
1067
		}
1068
		return names;
1069
	}
1070
	/**
1071
	 * Creates a new {@link SynonymRelationship synonym relationship} (with the given {@link Synonym synonym}
1072
	 * and with the given {@link SynonymRelationshipType synonym relationship type}), returns it and adds it
1073
	 * to the set of {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon.
1074
	 * The new synonym relationship will also be added to the set of
1075
	 * {@link Synonym#getSynonymRelations() synonym relationships} belonging to the synonym
1076
	 * involved in this synonym relationship.<BR>
1077
	 * The returned synonym relationship allows to add further information to it.
1078
	 * 
1079
	 * @param synonym		the synonym involved in the relationship to be created
1080
	 * 						and added to <i>this</i> taxon's synonym relationships set
1081
	 * @param synonymType	the synonym relationship category of the synonym
1082
	 * 						relationship to be added
1083
	 * @return 				the created synonym relationship
1084
	 * @see    	   			#addSynonymRelation(SynonymRelationship)
1085
	 * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1086
	 * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1087
	 * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1088
	 * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1089
	 * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1090
	 * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1091
	 * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1092
	 * @see    	   			#getSynonymRelations()
1093
	 * @see    				#removeSynonym(Synonym)
1094
	 * @see    	   			Synonym#getSynonymRelations()
1095
	 */
1096
	public SynonymRelationship addSynonym(Synonym synonym, SynonymRelationshipType synonymType){
1097
		return addSynonym(synonym, synonymType, null, null);
1098
	}
1099
	/**
1100
	 * Creates a new {@link SynonymRelationship synonym relationship} (with the given {@link Synonym synonym},
1101
	 * with the given {@link SynonymRelationshipType synonym relationship type} and with the
1102
	 * {@link eu.etaxonomy.cdm.model.reference.Reference reference source} on which the relationship assertion is based),
1103
	 * returns it and adds it to the set of {@link #getSynonymRelations() synonym relationships}
1104
	 * assigned to <i>this</i> taxon. The new synonym relationship will also be
1105
	 * added to the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging to the synonym
1106
	 * involved in this synonym relationship.<BR>
1107
	 * The returned synonym relationship allows to add further information to it.
1108
	 * 
1109
	 * @param synonym		the synonym involved in the relationship to be created
1110
	 * 						and added to <i>this</i> taxon's synonym relationships set
1111
	 * @param synonymType	the synonym relationship category of the synonym
1112
	 * 						relationship to be added
1113
	 * @param citation		the reference source for the new synonym relationship
1114
	 * @param microcitation	the string with the details describing the exact localisation within the reference
1115
	 * @return 				the created synonym relationship
1116
	 * @see    	   			#addSynonymRelation(SynonymRelationship)
1117
	 * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1118
	 * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1119
	 * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1120
	 * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1121
	 * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1122
	 * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1123
	 * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1124
	 * @see    	   			#getSynonymRelations()
1125
	 * @see    				#removeSynonym(Synonym)
1126
	 * @see    	   			Synonym#getSynonymRelations()
1127
	 */
1128
	public SynonymRelationship addSynonym(Synonym synonym, SynonymRelationshipType synonymType, Reference citation, String citationMicroReference){
1129
		SynonymRelationship synonymRelationship = new SynonymRelationship(synonym, this, synonymType, citation, citationMicroReference);
1130
		return synonymRelationship;
1131
	}
1132
	
1133
	/**
1134
	 * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1135
	 * a new {@link SynonymRelationship synonym relationship} (with the new synonym and with the given 
1136
	 * {@link SynonymRelationshipType synonym relationship type}), returns the relationship and adds it
1137
	 * to the set of {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon.
1138
	 * The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1139
	 * as <i>this</i> taxon. The new synonym relationship will also be added to 
1140
	 * the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1141
	 * to the created synonym.<BR>
1142
	 * The returned synonym relationship allows to add further information to it.
1143
	 * 
1144
	 * @param synonymName	the taxon name to be used as a synonym to be added
1145
	 * 						to <i>this</i> taxon's set of synonyms
1146
	 * @param synonymType	the synonym relationship category of the synonym
1147
	 * 						relationship to be added
1148
	 * @return 				the created synonym relationship
1149
	 * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1150
	 * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1151
	 * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1152
	 * @see    	   			#addSynonymRelation(SynonymRelationship)
1153
	 * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1154
	 * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1155
	 * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1156
	 * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1157
	 * @see    	   			#getSynonymRelations()
1158
	 * @see    				#removeSynonym(Synonym)
1159
	 * @see    	   			Synonym#getSynonymRelations()
1160
	 */
1161
	public SynonymRelationship addSynonymName(TaxonNameBase synonymName, SynonymRelationshipType synonymType){
1162
		return addSynonymName(synonymName, synonymType, null, null);
1163
	}
1164
	/**
1165
	 * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1166
	 * a new {@link SynonymRelationship synonym relationship} (with the new synonym, with the given 
1167
	 * {@link SynonymRelationshipType synonym relationship type} and with the {@link eu.etaxonomy.cdm.model.reference.Reference reference source}
1168
	 * on which the relationship assertion is based), returns the relationship
1169
	 * and adds it to the set of {@link #getSynonymRelations() synonym relationships} assigned
1170
	 * to <i>this</i> taxon. The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1171
	 * as <i>this</i> taxon. The new synonym relationship will also be added to 
1172
	 * the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1173
	 * to the created synonym.<BR>
1174
	 * The returned synonym relationship allows to add further information to it.
1175
	 * 
1176
	 * @param synonymName	the taxon name to be used as a synonym to be added
1177
	 * 						to <i>this</i> taxon's set of synonyms
1178
	 * @param synonymType	the synonym relationship category of the synonym
1179
	 * 						relationship to be added
1180
	 * @param citation		the reference source for the new synonym relationship
1181
	 * @param microcitation	the string with the details describing the exact localisation within the reference
1182
	 * @return 				the created synonym relationship
1183
	 * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1184
	 * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1185
	 * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1186
	 * @see    	   			#addSynonymRelation(SynonymRelationship)
1187
	 * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1188
	 * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1189
	 * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1190
	 * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1191
	 * @see    	   			#getSynonymRelations()
1192
	 * @see    				#removeSynonym(Synonym)
1193
	 * @see    	   			Synonym#getSynonymRelations()
1194
	 */
1195
	public SynonymRelationship addSynonymName(TaxonNameBase synonymName, SynonymRelationshipType synonymType, Reference citation, String citationMicroReference){
1196
		Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
1197
		return addSynonym(synonym, synonymType, citation, citationMicroReference);
1198
	}
1199

    
1200
	/**
1201
	 * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1202
	 * a new {@link SynonymRelationship synonym relationship} (with the new synonym and with the 
1203
	 * {@link SynonymRelationshipType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of" relationship type}),
1204
	 * returns the relationship and adds it to the set of
1205
	 * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon.
1206
	 * The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1207
	 * as <i>this</i> taxon. The new synonym relationship will also be added to 
1208
	 * the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1209
	 * to the created synonym.<BR>
1210
	 * The returned synonym relationship allows to add further information to it.
1211
	 * 
1212
	 * @param synonymName	the taxon name to be used as an heterotypic synonym
1213
	 * 						to be added to <i>this</i> taxon's set of synonyms
1214
	 * @return 				the created synonym relationship
1215
	 * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1216
	 * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1217
	 * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1218
	 * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1219
	 * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1220
	 * @see    	   			#addSynonymRelation(SynonymRelationship)
1221
	 * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1222
	 * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1223
	 * @see    	   			#getSynonymRelations()
1224
	 * @see    				#removeSynonym(Synonym)
1225
	 * @see    	   			Synonym#getSynonymRelations()
1226
	 */
1227
	public SynonymRelationship addHeterotypicSynonymName(TaxonNameBase synonymName){
1228
		return addHeterotypicSynonymName(synonymName, null, null, null);
1229
	}
1230
	
1231
	/**
1232
	 * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1233
	 * a new {@link SynonymRelationship synonym relationship} (with the new synonym, with the 
1234
	 * {@link SynonymRelationshipType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of" relationship type}
1235
	 * and with the {@link eu.etaxonomy.cdm.model.reference.Reference reference source}
1236
	 * on which the relationship assertion is based), returns the relationship
1237
	 * and adds it to the set of {@link #getSynonymRelations() synonym relationships} assigned
1238
	 * to <i>this</i> taxon. The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1239
	 * as <i>this</i> taxon. Furthermore the new synonym relationship will be 
1240
	 * added to the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1241
	 * to the created synonym and the taxon name used as synonym will be added
1242
	 * to the given {@link name.HomotypicalGroup homotypical group}.<BR>
1243
	 * The returned synonym relationship allows to add further information to it.
1244
	 * 
1245
	 * @param synonymName		the taxon name to be used as an heterotypic synonym
1246
	 * 							to be added to <i>this</i> taxon's set of synonyms
1247
	 * @param homotypicalGroup	the homotypical group to which the taxon name
1248
	 * 							of the synonym will be added
1249
	 * @param citation			the reference source for the new synonym relationship
1250
	 * @param microcitation		the string with the details describing the exact localisation
1251
	 * 							within the reference
1252
	 * @return 					the created synonym relationship
1253
	 * @see    	   				#addHeterotypicSynonymName(TaxonNameBase)
1254
	 * @see    	   				#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1255
	 * @see    	   				#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1256
	 * @see    	   				#addSynonym(Synonym, SynonymRelationshipType)
1257
	 * @see    	   				#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1258
	 * @see    	   				#addSynonymRelation(SynonymRelationship)
1259
	 * @see    	   				#addHomotypicSynonym(Synonym, Reference, String)
1260
	 * @see    	   				#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1261
	 * @see    	   				#getSynonymRelations()
1262
	 * @see    					#removeSynonym(Synonym)
1263
	 * @see    	   				Synonym#getSynonymRelations()
1264
	 */
1265
	public SynonymRelationship addHeterotypicSynonymName(TaxonNameBase synonymName, HomotypicalGroup homotypicalGroup, Reference citation, String microCitation){
1266
		Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
1267
		if (homotypicalGroup != null){
1268
			homotypicalGroup.addTypifiedName(synonymName);
1269
		}
1270
		return addSynonym(synonym, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), citation, microCitation);
1271
	}
1272
	
1273
	/**
1274
	 * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1275
	 * a new {@link SynonymRelationship synonym relationship} (with the new synonym, with the 
1276
	 * {@link SynonymRelationshipType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of" relationship type})
1277
	 * and with the {@link eu.etaxonomy.cdm.model.reference.Reference reference source}
1278
	 * on which the relationship assertion is based), returns the relationship
1279
	 * and adds it to the set of {@link #getSynonymRelations() synonym relationships} assigned
1280
	 * to <i>this</i> taxon. The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1281
	 * as <i>this</i> taxon. Furthermore the new synonym relationship will be 
1282
	 * added to the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1283
	 * to the created synonym and the taxon name used as synonym will be added
1284
	 * to the same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group} to which the taxon name
1285
	 * of <i>this</i> taxon belongs.<BR>
1286
	 * The returned synonym relationship allows to add further information to it.
1287
	 * 
1288
	 * @param synonymName	the taxon name to be used as an homotypic synonym
1289
	 * 						to be added to <i>this</i> taxon's set of synonyms
1290
	 * @param citation		the reference source for the new synonym relationship
1291
	 * @param microcitation	the string with the details describing the exact localisation
1292
	 * 						within the reference
1293
	 * @return 				the created synonym relationship
1294
	 * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1295
	 * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1296
	 * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1297
	 * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1298
	 * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1299
	 * @see    	   			#addSynonymRelation(SynonymRelationship)
1300
	 * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1301
	 * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1302
	 * @see    	   			#getSynonymRelations()
1303
	 * @see    				#removeSynonym(Synonym)
1304
	 * @see    	   			Synonym#getSynonymRelations()
1305
	 */
1306
	public SynonymRelationship addHomotypicSynonymName(TaxonNameBase synonymName, Reference citation, String microCitation){
1307
		Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
1308
		return addHomotypicSynonym(synonym, citation, microCitation);
1309
	}
1310
	
1311
	/**
1312
	 * Creates a new {@link SynonymRelationship synonym relationship} (with the given {@link Synonym synonym},
1313
	 * with the {@link SynonymRelationshipType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of" relationship type}
1314
	 * and with the {@link eu.etaxonomy.cdm.model.reference.Reference reference source} on which the relationship
1315
	 * assertion is based), returns it and adds it to the set of
1316
	 * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon.
1317
	 * Furthermore the new synonym relationship will be added to the set of
1318
	 * {@link Synonym#getSynonymRelations() synonym relationships} belonging to the synonym
1319
	 * involved in this synonym relationship and the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}
1320
	 * used as synonym will be added to the same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group}
1321
	 * to which the taxon name of <i>this</i> taxon belongs.<BR>
1322
	 * The returned synonym relationship allows to add further information to it.
1323
	 * 
1324
	 * @param synonym		the synonym involved in the "is homotypic synonym of" relationship to be created
1325
	 * 						and added to <i>this</i> taxon's synonym relationships set
1326
	 * @param citation		the reference source for the new synonym relationship
1327
	 * @param microcitation	the string with the details describing the exact localisation within the reference
1328
	 * @return 				the created synonym relationship
1329
	 * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1330
	 * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1331
	 * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1332
	 * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1333
	 * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1334
	 * @see    	   			#addSynonymRelation(SynonymRelationship)
1335
	 * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1336
	 * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1337
	 * @see    	   			#getSynonymRelations()
1338
	 * @see    				#removeSynonym(Synonym)
1339
	 * @see    	   			Synonym#getSynonymRelations()
1340
	 */
1341
	public SynonymRelationship addHomotypicSynonym(Synonym synonym, Reference citation, String microCitation){
1342
	if (this.getName() != null){
1343
			if (this.getName().getHomotypicalGroup().getTypifiedNames().isEmpty()){
1344
				this.getName().getHomotypicalGroup().getTypifiedNames().add(this.getName());
1345
			
1346
			}
1347
			this.getName().getHomotypicalGroup().addTypifiedName(synonym.getName());
1348
			
1349
		}
1350
		SynonymRelationship synRel = addSynonym(synonym, SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF(), citation, microCitation);
1351
		return synRel;
1352
	}
1353
	
1354
	/**
1355
	 * Like {@link #removeSynonym(Synonym, boolean)} with <code>removeSynonymNameFromHomotypicalGroup</code> set to true.
1356
	 * @see #removeSynonym(Synonym, boolean)
1357
	 */
1358
	public void removeSynonym(Synonym synonym){
1359
		removeSynonym(synonym, true);
1360
	}
1361
	
1362
	/** 
1363
	 * Removes the element(s) from the set of {@link SynonymRelationship synonym relationships}
1364
	 * assigned to <i>this</i> ("accepted/valid") taxon in which the given synonym is involved.
1365
	 * Due to bidirectionality the same synonym relationships will also be
1366
	 * removed from the set of synonym relationships assigned to the
1367
	 * {@link Synonym#getSynonymRelations() synonym} involved in the relationship. Furthermore the content of
1368
	 * the {@link SynonymRelationship#getAcceptedTaxon() accepted taxon} attribute and of the
1369
	 * {@link SynonymRelationship#getSynonym() synonym} attribute within the synonym relationships
1370
	 * themselves will be set to "null".
1371
	 *
1372
	 * @param  synonym  the synonym involved in the synonym relationship which should be deleted
1373
	 * @param removeSynonymNameFromHomotypicalGroup if true the removed synonyms name will get a new homotypic group
1374
	 * to make sure that it is not together in a homotypic group with any other synonm of this taxon
1375
	 * anymore.
1376
	 * @see     		#getSynonymRelations()
1377
	 * @see     		#addSynonym(Synonym, SynonymRelationshipType)
1378
	 * @see     		#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1379
	 * @see 			#removeSynonymRelation(SynonymRelationship)
1380
	 */
1381
	public void removeSynonym(Synonym synonym, boolean removeSynonymNameFromHomotypicalGroup){
1382
		Set<SynonymRelationship> synonymRelationships = new HashSet<SynonymRelationship>();
1383
		synonymRelationships.addAll(this.getSynonymRelations());
1384
		for(SynonymRelationship synonymRelationship : synonymRelationships){
1385
			if (synonymRelationship.getAcceptedTaxon().equals(this) && synonymRelationship.getSynonym().equals(synonym)){
1386
				this.removeSynonymRelation(synonymRelationship, removeSynonymNameFromHomotypicalGroup);
1387
			}
1388
		}
1389
	}
1390
	
1391
	
1392
	/**
1393
	 * Retrieves the ordered list (depending on the date of publication) of
1394
	 * homotypic {@link Synonym synonyms} (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1395
	 * as for <i>this</i> taxon) under the condition that the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon names}
1396
	 * of these synonyms and the taxon name of <i>this</i> taxon belong to the
1397
	 * same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group}.
1398
	 * 
1399
	 * @return		the ordered list of homotypic synonyms
1400
	 * @see			#getHomotypicSynonymsByHomotypicRelationship()
1401
	 * @see			#getSynonyms()
1402
	 * @see			#getHomotypicSynonymyGroups()
1403
	 * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1404
	 * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup#getSynonymsInGroup(Reference)
1405
	 */
1406
	@Transient
1407
	public List<Synonym> getHomotypicSynonymsByHomotypicGroup(){
1408
		if (this.getHomotypicGroup() == null){
1409
			return null;
1410
		}else{
1411
			return this.getHomotypicGroup().getSynonymsInGroup(this.getSec());
1412
		}
1413
	}
1414
	
1415
	/**
1416
	 * Retrieves the ordered list (depending on the date of publication) of
1417
	 * homotypic {@link Synonym synonyms} (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1418
	 * as for <i>this</i> taxon) under the condition that these synonyms and
1419
	 * <i>this</i> taxon are involved in {@link SynonymRelationship synonym relationships} with an
1420
	 * "is homotypic synonym of" {@link SynonymRelationshipType#HOMOTYPIC_SYNONYM_OF() synonym relationship type}.
1421
	 * 
1422
	 * @return		the ordered list of homotypic synonyms
1423
	 * @see			#getHomotypicSynonymsByHomotypicGroup()
1424
	 * @see			#getSynonyms()
1425
	 * @see			#getHomotypicSynonymyGroups()
1426
	 * @see			SynonymRelationshipType
1427
	 */
1428
	@Transient
1429
	public List<Synonym> getHomotypicSynonymsByHomotypicRelationship(){
1430
		Set<SynonymRelationship> synonymRelations = this.getSynonymRelations(); 
1431
		List<Synonym> result = new ArrayList<Synonym>();
1432
		for(SynonymRelationship synonymRelation : synonymRelations) {
1433
    		if(synonymRelation.getType().equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){
1434
				result.add(synonymRelation.getSynonym());
1435
    		}
1436
		}
1437
		return result;
1438
	}
1439
	
1440
	/**
1441
	 * Returns the ordered list of all {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups} {@link Synonym synonyms} of
1442
	 * <i>this</i> taxon belong to. {@link eu.etaxonomy.cdm.model.name.TaxonNameBase Taxon names} of homotypic synonyms
1443
	 * belong to the same homotypical group as the taxon name of <i>this</i>
1444
	 * taxon. Taxon names of heterotypic synonyms belong to at least one other
1445
	 * homotypical group. <BR>
1446
	 * The list returned is ordered according to the date of publication of the
1447
	 * first published name within each homotypical group.
1448
	 * 
1449
	 * @see			#getHeterotypicSynonymyGroups()
1450
	 * @see			#getSynonyms()
1451
	 * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1452
	 */
1453
	@Transient
1454
	public List<HomotypicalGroup> getHomotypicSynonymyGroups(){
1455
		List<HomotypicalGroup> result = new ArrayList<HomotypicalGroup>();
1456
		result.add(this.getHomotypicGroup());
1457
		for (TaxonNameBase taxonNameBase :this.getSynonymNames()){
1458
			if (taxonNameBase != null) {
1459
				if (!result.contains(taxonNameBase.getHomotypicalGroup())){
1460
					result.add(taxonNameBase.getHomotypicalGroup());
1461
				}
1462
			} // TODO: give error message to user
1463
		}
1464
		// TODO: sort list according to date of first published name within each group
1465
		return result;
1466
	}
1467
	
1468
	
1469
	
1470
	/**
1471
	 * @return the taxonStatusUnknown
1472
	 */
1473
	public boolean isTaxonStatusUnknown() {
1474
		return taxonStatusUnknown;
1475
	}
1476

    
1477
	/**
1478
	 * @param taxonStatusUnknown the taxonStatusUnknown to set
1479
	 */
1480
	public void setTaxonStatusUnknown(boolean taxonStatusUnknown) {
1481
		this.taxonStatusUnknown = taxonStatusUnknown;
1482
	}
1483

    
1484
	
1485
	
1486

    
1487
	public boolean isUnplaced() {
1488
		return unplaced;
1489
	}
1490

    
1491
	public void setUnplaced(boolean unplaced) {
1492
		this.unplaced = unplaced;
1493
	}
1494

    
1495
	public boolean isExcluded() {
1496
		return excluded;
1497
	}
1498

    
1499
	public void setExcluded(boolean excluded) {
1500
		this.excluded = excluded;
1501
	}
1502

    
1503
	/**
1504
	 * Returns the ordered list of all {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups} heterotypic
1505
	 * {@link Synonym synonyms} of <i>this</i> taxon belongs to.
1506
	 * {@link eu.etaxonomy.cdm.model.name.TaxonNameBase Taxon names} of heterotypic synonyms belong to at least
1507
	 * one homotypical group which cannot be the homotypical group to which the
1508
	 * taxon name of <i>this</i> taxon belongs. This method returns the same
1509
	 * list as the {@link #getHomotypicSynonymyGroups() getHomotypicSynonymyGroups} method
1510
	 * but the homotypical group to which the taxon name of <i>this</i> taxon
1511
	 * belongs.<BR>
1512
	 * The list returned is ordered according to the date of publication of the
1513
	 * first published name within each homotypical group.
1514
	 * 
1515
	 * @see			#getHeterotypicSynonymyGroups()
1516
	 * @see			#getSynonyms()
1517
	 * @see			SynonymRelationshipType#HETEROTYPIC_SYNONYM_OF()
1518
	 * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1519
	 */
1520
	@Transient
1521
	public List<HomotypicalGroup> getHeterotypicSynonymyGroups(){
1522
		List<HomotypicalGroup> list = getHomotypicSynonymyGroups();
1523
		list.remove(this.getHomotypicGroup());
1524
		//sort
1525
		Map<Synonym, HomotypicalGroup> map = new HashMap<Synonym, HomotypicalGroup>();
1526
		for (HomotypicalGroup homotypicalGroup: list){
1527
			List<Synonym> synonymList = homotypicalGroup.getSynonymsInGroup(getSec());
1528
			if (synonymList.size() > 0){
1529
				map.put(synonymList.get(0), homotypicalGroup);
1530
			}
1531
		}
1532
		List<Synonym> keyList = new ArrayList<Synonym>();
1533
		keyList.addAll(map.keySet());
1534
		Collections.sort(keyList, new TaxonComparator());
1535
		
1536
		List<HomotypicalGroup> result = new ArrayList<HomotypicalGroup>();
1537
		for(Synonym synonym: keyList){
1538
			result.add(map.get(synonym));
1539
		}
1540
		//sort end
1541
		return result;
1542
	}
1543
	
1544
	/**
1545
	 * Returns the image gallery description. If no image gallery exists, a new one is created using the 
1546
	 * defined title and adds the string "-Image Gallery" to the title.</BR>
1547
	 * If multiple image galleries exist an arbitrary one is choosen.
1548
	 * @param title
1549
	 * @return
1550
	 */
1551
	@Transient
1552
	public TaxonDescription getOrCreateImageGallery(String title){
1553
		return getOrCreateImageGallery(title, true, false);
1554
	}
1555
	
1556
	/**
1557
	 * Returns the image gallery description. If no image gallery exists, a new one is created using the 
1558
	 * defined title.</BR>
1559
	 * If onlyTitle == true we look only for an image gallery with this title, create a new one otherwise.
1560
	 * If multiple image galleries exist that match the conditions an arbitrary one is choosen.
1561
	 * @param title
1562
	 * @param onlyTitle
1563
	 * @param if true, the String "Image Gallery
1564
	 * @return
1565
	 */
1566
	@Transient
1567
	public TaxonDescription getOrCreateImageGallery(String title, boolean addImageGalleryToTitle, boolean onlyTitle){
1568
		TaxonDescription result = null;
1569
		String titleCache = (title == null) ? "Image Gallery" : title;
1570
		if (title != null && addImageGalleryToTitle){
1571
			titleCache = titleCache+ "-Image Gallery";
1572
		}
1573
		Set<TaxonDescription> descriptionSet = this.getDescriptions();
1574
		for (TaxonDescription desc: descriptionSet){
1575
			if (desc.isImageGallery()){
1576
				if (onlyTitle && ! titleCache.equals(desc.getTitleCache())){
1577
					continue;
1578
				}
1579
				result = desc;
1580
				if (onlyTitle && titleCache.equals(desc.getTitleCache())){
1581
					break;
1582
				}
1583
			}
1584
		}
1585
		if (result == null){
1586
			result = TaxonDescription.NewInstance();
1587
			result.setTitleCache(titleCache, true);
1588
			this.addDescription(result);
1589
			result.setImageGallery(true);
1590
		}
1591
		return result;
1592
	}
1593
	//*********************** CLONE ********************************************************/
1594
	
1595
	
1596
	/** 
1597
	 * Clones <i>this</i> taxon. This is a shortcut that enables to create
1598
	 * a new instance that differs only slightly from <i>this</i> taxon by
1599
	 * modifying only some of the attributes.<BR><BR>
1600
	 * The TaxonNodes are not cloned, the list is empty.<BR>
1601
	 * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1602
	 * The taxon relationships and synonym relationships are cloned <BR>
1603
	 * 
1604
	 * @see eu.etaxonomy.cdm.model.taxon.TaxonBase#clone()
1605
	 * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1606
	 * @see java.lang.Object#clone()
1607
	 */
1608
	@Override
1609
	public Object clone() {
1610
		Taxon result;
1611
		result = (Taxon)super.clone();
1612
		
1613
		result.setRelationsFromThisTaxon(new HashSet<TaxonRelationship>());
1614
		
1615
		for (TaxonRelationship fromRelationship : this.getRelationsFromThisTaxon()){
1616
			TaxonRelationship newRelationship = (TaxonRelationship)fromRelationship.clone();
1617
			newRelationship.setRelatedFrom(result);
1618
			result.relationsFromThisTaxon.add(newRelationship);
1619
		}
1620
		
1621
		result.setRelationsToThisTaxon(new HashSet<TaxonRelationship>());
1622
		for (TaxonRelationship toRelationship : this.getRelationsToThisTaxon()){
1623
			TaxonRelationship newRelationship = (TaxonRelationship)toRelationship.clone();
1624
			newRelationship.setRelatedTo(result);
1625
			result.relationsToThisTaxon.add(newRelationship);
1626
		}
1627
		
1628
		
1629
		result.synonymRelations = new HashSet<SynonymRelationship>();
1630
		for (SynonymRelationship synRelationship : this.getSynonymRelations()){
1631
			SynonymRelationship newRelationship = (SynonymRelationship)synRelationship.clone();
1632
			newRelationship.setRelatedTo(result);
1633
			result.synonymRelations.add(newRelationship);
1634
		}
1635
		
1636
		
1637
		result.taxonNodes = new HashSet<TaxonNode>();
1638
		
1639
		/*for (TaxonNode taxonNode : this.getTaxonNodes()){
1640
			TaxonNode newTaxonNode = (TaxonNode)taxonNode.clone();
1641
			newTaxonNode.setTaxon(result);
1642
			result.addTaxonNode(newTaxonNode);
1643
		}*/
1644
		
1645
		return result;
1646
		
1647
	}
1648
}
(9-9/18)