Project

General

Profile

Download (82.4 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.ClassBridge;
44
import org.hibernate.search.annotations.ClassBridges;
45
import org.hibernate.search.annotations.ContainedIn;
46
import org.hibernate.search.annotations.Indexed;
47
import org.hibernate.search.annotations.IndexedEmbedded;
48
import org.springframework.beans.factory.annotation.Configurable;
49
import org.springframework.util.ReflectionUtils;
50

    
51
import eu.etaxonomy.cdm.hibernate.search.GroupByTaxonClassBridge;
52
import eu.etaxonomy.cdm.hibernate.search.TaxonRelationshipClassBridge;
53
import eu.etaxonomy.cdm.model.common.IRelated;
54
import eu.etaxonomy.cdm.model.common.RelationshipBase;
55
import eu.etaxonomy.cdm.model.description.TaxonDescription;
56
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
57
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
58
import eu.etaxonomy.cdm.model.reference.Reference;
59
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
60
import eu.etaxonomy.cdm.strategy.cache.taxon.TaxonBaseDefaultCacheStrategy;
61

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

    
97
    @XmlElementWrapper(name = "Descriptions")
98
    @XmlElement(name = "Description")
99
    @OneToMany(mappedBy="taxon", fetch= FetchType.LAZY)
100
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
101
    @NotNull
102
    @ContainedIn
103
    private Set<TaxonDescription> descriptions = new HashSet<TaxonDescription>();
104

    
105
    // all related synonyms
106
    @XmlElementWrapper(name = "SynonymRelations")
107
    @XmlElement(name = "SynonymRelationship")
108
    @OneToMany(mappedBy="relatedTo", fetch=FetchType.LAZY, orphanRemoval=true)
109
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
110
    @NotNull
111
    @Valid
112
    private Set<SynonymRelationship> synonymRelations = new HashSet<SynonymRelationship>();
113

    
114
    // all taxa relations with rel.fromTaxon==this
115
    @XmlElementWrapper(name = "RelationsFromThisTaxon")
116
    @XmlElement(name = "FromThisTaxonRelationship")
117
    @OneToMany(mappedBy="relatedFrom", fetch=FetchType.LAZY, orphanRemoval=true)
118
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
119
    @NotNull
120
    @Valid
121
    private Set<TaxonRelationship> relationsFromThisTaxon = new HashSet<TaxonRelationship>();
122

    
123
    // all taxa relations with rel.toTaxon==this
124
    @XmlElementWrapper(name = "RelationsToThisTaxon")
125
    @XmlElement(name = "ToThisTaxonRelationship")
126
    @XmlIDREF
127
    @XmlSchemaType(name = "IDREF")
128
    @OneToMany(mappedBy="relatedTo", fetch=FetchType.LAZY, orphanRemoval=true)
129
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
130
    @NotNull
131
    @Valid
132
    private Set<TaxonRelationship> relationsToThisTaxon = new HashSet<TaxonRelationship>();
133

    
134
    @XmlAttribute(name= "taxonStatusUnknown")
135
    private boolean taxonStatusUnknown = false;
136

    
137
    @XmlAttribute(name= "unplaced")
138
    private boolean unplaced = false;
139

    
140
    @XmlAttribute(name= "excluded")
141
    private boolean excluded = false;
142

    
143
    // shortcut to the taxonomicIncluded (parent) taxon. Managed by the taxonRelations setter
144
    @XmlElement(name = "TaxonomicParentCache")
145
    @XmlIDREF
146
    @XmlSchemaType(name = "IDREF")
147
    @ManyToOne(fetch = FetchType.LAZY)
148
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
149
    private Taxon taxonomicParentCache;
150

    
151

    
152
    @XmlElementWrapper(name = "taxonNodes")
153
    @XmlElement(name = "taxonNode")
154
    @XmlIDREF
155
    @XmlSchemaType(name = "IDREF")
156
    @OneToMany(mappedBy="taxon", fetch=FetchType.LAZY)
157
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
158
    @IndexedEmbedded
159
    private Set<TaxonNode> taxonNodes = new HashSet<TaxonNode>();
160

    
161
    //cached number of taxonomic children
162
    @XmlElement(name = "TaxonomicChildrenCount")
163
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
164
    private int taxonomicChildrenCount;
165
    
166
// ************************* FACTORY METHODS ********************************/
167
    
168
    /**
169
     * Creates a new (accepted/correct) taxon instance with
170
     * the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
171
     * using it.
172
     *
173
     * @param  taxonNameBase	the taxon name used
174
     * @param  sec				the reference using the taxon name
175
     * @see    					#Taxon(TaxonNameBase, Reference)
176
     */
177
    public static Taxon NewInstance(TaxonNameBase taxonNameBase, Reference sec){
178
        Taxon result = new Taxon(taxonNameBase, sec);
179
        return result;
180
    }
181

    
182
    /**
183
     * Creates a new taxon instance with an unknown status (accepted/synonym) and with
184
     * the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
185
     * using it.
186
     *
187
     * @param  taxonNameBase	the taxon name used
188
     * @param  sec				the reference using the taxon name
189
     * @see    					#Taxon(TaxonNameBase, Reference)
190
     */
191
    public static Taxon NewUnknownStatusInstance(TaxonNameBase taxonNameBase, Reference sec){
192
        Taxon result = new Taxon(taxonNameBase, sec);
193
        result.setTaxonStatusUnknown(true);
194
        return result;
195
    }
196
// ************* CONSTRUCTORS *************/
197

    
198
    //TODO should be private, but still produces Spring init errors
199
    @Deprecated
200
    public Taxon(){
201
        this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Taxon>();
202
    }
203

    
204
    /**
205
     * Class constructor: creates a new (accepted/correct) taxon instance with
206
     * the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
207
     * using it.
208
     *
209
     * @param  taxonNameBase	the taxon name used
210
     * @param  sec				the reference using the taxon name
211
     * @see    					TaxonBase#TaxonBase(TaxonNameBase, Reference)
212
     */
213
    public Taxon(TaxonNameBase taxonNameBase, Reference sec){
214
        super(taxonNameBase, sec);
215
        this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Taxon>();
216
    }
217

    
218
//********* METHODS **************************************/
219

    
220

    
221

    
222
    /**
223
     * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon descriptions}
224
     * concerning <i>this</i> taxon.
225
     *
226
     * @see #removeDescription(TaxonDescription)
227
     * @see #addDescription(TaxonDescription)
228
     * @see eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
229
     */
230
    public Set<TaxonDescription> getDescriptions() {
231
        if(descriptions == null) {
232
            descriptions = new HashSet<TaxonDescription>();
233
        }
234
        return descriptions;
235
    }
236

    
237
    /**
238
     * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon description} to the set
239
     * of taxon descriptions assigned to <i>this</i> (accepted/correct) taxon.
240
     * Due to bidirectionality the content of the {@link eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon() taxon attribute} of the
241
     * taxon description itself will be replaced with <i>this</i> taxon. The taxon
242
     * description will also be removed from the set of taxon descriptions
243
     * assigned to its previous taxon.
244
     *
245
     * @param  description	the taxon description to be added for <i>this</i> taxon
246
     * @see     		  	#getDescriptions()
247
     * @see     		  	#removeDescription(TaxonDescription)
248
     * @see 			  	eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
249
     */
250
    public void addDescription(TaxonDescription description) {
251
        if (description.getTaxon() != null){
252
            description.getTaxon().removeDescription(description);
253
        }
254
        Field field = ReflectionUtils.findField(TaxonDescription.class, "taxon", Taxon.class);
255
        ReflectionUtils.makeAccessible(field);
256
        ReflectionUtils.setField(field, description, this);
257
        descriptions.add(description);
258

    
259
    }
260
    /**
261
     * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon descriptions} assigned
262
     * to <i>this</i> (accepted/correct) taxon. Due to bidirectionality the content of
263
     * the {@link eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon() taxon attribute} of the taxon description
264
     * itself will be set to "null".
265
     *
266
     * @param  description  the taxon description which should be removed
267
     * @see     		  	#getDescriptions()
268
     * @see     		  	#addDescription(TaxonDescription)
269
     * @see 			  	eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
270
     */
271
    public void removeDescription(TaxonDescription description) {
272
        //description.setTaxon(null) for not visible method
273
        Field field = ReflectionUtils.findField(TaxonDescription.class, "taxon", Taxon.class);
274
        ReflectionUtils.makeAccessible(field);
275
        ReflectionUtils.setField(field, description, null);
276
        descriptions.remove(description);
277
    }
278

    
279
    /**
280
     * Returns the image gallery for a taxon. If there are multiple taxon descriptions
281
     * marked as image galleries an arbitrary one is chosen.
282
     * If no image gallery exists, a new one is created if <code>createNewIfNotExists</code>
283
     * is <code>true</code>.
284
     * @param createNewIfNotExists
285
     * @return
286
     */
287
    public TaxonDescription getImageGallery(boolean createNewIfNotExists) {
288
        TaxonDescription result = null;
289
        Set<TaxonDescription> descriptions= getDescriptions();
290
        for (TaxonDescription description : descriptions){
291
            if (description.isImageGallery()){
292
                result = description;
293
                break;
294
            }
295
        }
296
        if (result == null && createNewIfNotExists){
297
            result = TaxonDescription.NewInstance(this);
298
            result.setImageGallery(true);
299
        }
300
        return result;
301
    }
302

    
303

    
304

    
305
    public Set<TaxonNode> getTaxonNodes() {
306
        return taxonNodes;
307
    }
308
    //	protected void setTaxonNodes(Set<TaxonNode> taxonNodes) {
309
//		this.taxonNodes = taxonNodes;
310
//	}
311
    protected void addTaxonNode(TaxonNode taxonNode){
312
        taxonNodes.add(taxonNode);
313
    }
314
    protected void removeTaxonNode(TaxonNode taxonNode){
315
        taxonNodes.remove(taxonNode);
316
    }
317

    
318

    
319

    
320

    
321
    /**
322
     * Returns the set of all {@link SynonymRelationship synonym relationships}
323
     * in which <i>this</i> ("accepted/correct") taxon is involved. <i>This</i> taxon can only
324
     * be the target of these synonym relationships.
325
     *
326
     * @see    #addSynonymRelation(SynonymRelationship)
327
     * @see    #removeSynonymRelation(SynonymRelationship)
328
     * @see    #getSynonyms()
329
     */
330
    public Set<SynonymRelationship> getSynonymRelations() {
331
        if(synonymRelations == null) {
332
            this.synonymRelations = new HashSet<SynonymRelationship>();
333
        }
334
        return synonymRelations;
335
    }
336

    
337
    /**
338
     * Adds an existing {@link SynonymRelationship synonym relationship} to the set of
339
     * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon. If
340
     * the target of the synonym relationship does not match with <i>this</i> taxon
341
     * no addition will be carried out.
342
     *
343
     * @param synonymRelation	the synonym relationship to be added to <i>this</i> taxon's
344
     * 							synonym relationships set
345
     * @see    	   				#getSynonymRelations()
346
     * @see    	   				#addSynonym(Synonym, SynonymRelationshipType)
347
     * @see    	   				#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
348
     * @see    	   				#addSynonymName(TaxonNameBase, SynonymRelationshipType)
349
     * @see    	   				#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
350
     */
351
    protected void addSynonymRelation(SynonymRelationship synonymRelation) {
352
        this.synonymRelations.add(synonymRelation);
353
    }
354
    /**
355
     * Removes one element from the set of {@link SynonymRelationship synonym relationships} assigned
356
     * to <i>this</i> (accepted/correct) taxon. Due to bidirectionality the given
357
     * synonym relationship will also be removed from the set of synonym
358
     * relationships assigned to the {@link Synonym#getSynonymRelations() synonym} involved in the
359
     * relationship. Furthermore the content of
360
     * the {@link SynonymRelationship#getAcceptedTaxon() accepted taxon} attribute and of the
361
     * {@link SynonymRelationship#getSynonym() synonym} attribute within the synonym relationship
362
     * itself will be set to "null".
363
     *
364
     * @param synonymRelation  	the synonym relationship which should be deleted
365
     * @param removeSynonymNameFromHomotypicalGroup
366
     * 				if <code>true</code> the synonym name will also be deleted from its homotypical group if the
367
     * 				group contains other names
368
     * @see    	#getSynonymRelations()
369
     * @see    	#addSynonymRelation(SynonymRelationship)
370
     * @see 	#removeSynonym(Synonym)
371
     */
372
    public void removeSynonymRelation(SynonymRelationship synonymRelation, boolean removeSynonymNameFromHomotypicalGroup) {
373
        synonymRelation.setAcceptedTaxon(null);
374
        Synonym synonym = synonymRelation.getSynonym();
375
        if (synonym != null){
376
            synonymRelation.setSynonym(null);
377
            synonym.removeSynonymRelation(synonymRelation);
378
            if(removeSynonymNameFromHomotypicalGroup){
379
                HomotypicalGroup synHG = synonym.getName().getHomotypicalGroup();
380
                if (synHG.getTypifiedNames().size() > 1){
381
                    synHG.removeTypifiedName(synonym.getName());
382
                }
383
            }
384
        }
385
        this.synonymRelations.remove(synonymRelation);
386
    }
387

    
388
    /**
389
     * Like {@link Taxon#removeSynonymRelation(SynonymRelationship, boolean)} but synonym name
390
     * will be deleted from homotypical group by default
391
     *
392
     * @param synonymRelation   the synonym relationship which should be deleted
393
     *
394
     * @see					#removeSynonymRelation(SynonymRelationship, boolean)
395
     */
396
    public void removeSynonymRelation(SynonymRelationship synonymRelation){
397
        removeSynonymRelation(synonymRelation, true);
398
    }
399

    
400

    
401
    /**
402
     * Returns the set of all {@link TaxonRelationship taxon relationships}
403
     * between two taxa in which <i>this</i> taxon is involved as a source.
404
     *
405
     * @see    #getRelationsToThisTaxon()
406
     * @see    #getTaxonRelations()
407
     */
408
    public Set<TaxonRelationship> getRelationsFromThisTaxon() {
409
        if(relationsFromThisTaxon == null) {
410
            this.relationsFromThisTaxon = new HashSet<TaxonRelationship>();
411
        }
412
        return relationsFromThisTaxon;
413
    }
414

    
415

    
416
    /**
417
     * Returns the set of all {@link TaxonRelationship taxon relationships}
418
     * between two taxa in which <i>this</i> taxon is involved as a target.
419
     *
420
     * @see    #getRelationsFromThisTaxon()
421
     * @see    #getTaxonRelations()
422
     */
423
    public Set<TaxonRelationship> getRelationsToThisTaxon() {
424
        if(relationsToThisTaxon == null) {
425
            this.relationsToThisTaxon = new HashSet<TaxonRelationship>();
426
        }
427
        return relationsToThisTaxon;
428
    }
429
    /**
430
     * @see    #getRelationsToThisTaxon()
431
     */
432
    protected void setRelationsToThisTaxon(Set<TaxonRelationship> relationsToThisTaxon) {
433
        this.relationsToThisTaxon = relationsToThisTaxon;
434
    }
435

    
436
    /**
437
     * @see    #getRelationsFromThisTaxon()
438
     */
439
    protected void setRelationsFromThisTaxon(Set<TaxonRelationship> relationsFromThisTaxon) {
440
        this.relationsFromThisTaxon = relationsFromThisTaxon;
441
    }
442

    
443
    /**
444
     * Returns the set of all {@link TaxonRelationship taxon relationships}
445
     * between two taxa in which <i>this</i> taxon is involved either as a source or
446
     * as a target.
447
     *
448
     * @see    #getRelationsFromThisTaxon()
449
     * @see    #getRelationsToThisTaxon()
450
     */
451
    @Transient
452
    public Set<TaxonRelationship> getTaxonRelations() {
453
        Set<TaxonRelationship> rels = new HashSet<TaxonRelationship>();
454
        rels.addAll(getRelationsToThisTaxon());
455
        rels.addAll(getRelationsFromThisTaxon());
456
        return rels;
457
    }
458

    
459
    /**
460
     * If a relationships between <i>this</i> and the given taxon exists they will be returned.
461
     * <i>This</i> taxon is involved either as a source or as a target in the relationships.
462
     * The method will return <code>null</code> if no relations exist between the two taxa.
463
     *
464
     * @param possiblyRelatedTaxon
465
     * 			a taxon to check for a relationship
466
     * @return
467
     * 			a set of <code>TaxonRelationship</code>s or <code>null</null> if none exists.
468
     */
469
    public Set<TaxonRelationship> getTaxonRelations(Taxon possiblyRelatedTaxon){
470
        Set<TaxonRelationship> relations = new HashSet<TaxonRelationship>();
471

    
472
        for(TaxonRelationship relationship : getTaxonRelations()){
473
            if(relationship.getFromTaxon().equals(possiblyRelatedTaxon)) {
474
                relations.add(relationship);
475
            }
476
            if(relationship.getToTaxon().equals(possiblyRelatedTaxon)) {
477
                relations.add(relationship);
478
            }
479
        }
480

    
481
        return relations.size() > 0 ? relations : null;
482
    }
483

    
484
    /**
485
     * Removes one {@link TaxonRelationship taxon relationship} from one of both sets of
486
     * {@link #getTaxonRelations() taxon relationships} in which <i>this</i> taxon is involved
487
     * either as a {@link #getRelationsFromThisTaxon() source} or as a {@link #getRelationsToThisTaxon() target}.
488
     * The taxon relationship will also be removed from one of both sets
489
     * belonging to the second taxon involved. Furthermore the inherited RelatedFrom and
490
     * RelatedTo attributes of the given taxon relationship will be nullified.<P>
491
     * If the taxon relationship concerns the classification possible
492
     * modifications of the {@link #getTaxonomicParent() parent taxon} or of the number of
493
     * {@link #getTaxonomicChildrenCount() childrens} will be stored.
494
     *
495
     * @param  rel  the taxon relationship which should be removed from one
496
     * 				of both sets
497
     * @see    		#getTaxonRelations()
498
     * @see    	    #getTaxonomicParent()
499
     * @see    	    #getTaxonomicChildrenCount()
500
     * @see    		eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom()
501
     * @see    		eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo()
502
     *
503
     */
504
    public void removeTaxonRelation(TaxonRelationship rel) {
505
        this.relationsToThisTaxon.remove(rel);
506
        this.relationsFromThisTaxon.remove(rel);
507
        Taxon fromTaxon = rel.getFromTaxon();
508
        Taxon toTaxon = rel.getToTaxon();
509
        // check if this removes the taxonomical parent. If so, also remove shortcut to the higher taxon
510
        if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) ){
511
            if (fromTaxon != null && fromTaxon.equals(this)){
512
                this.taxonomicParentCache = null;
513
            }else if (toTaxon != null && toTaxon.equals(this)){
514
                this.setTaxonomicChildrenCount(computeTaxonomicChildrenCount());
515
            }
516
        }
517
        //delete Relationship from other related Taxon
518
        if (fromTaxon != null && fromTaxon != this){
519
            rel.setToTaxon(null);  //remove this Taxon from relationship
520
            fromTaxon.removeTaxonRelation(rel);
521
        }
522
        if (toTaxon != null && toTaxon != this){
523
            rel.setFromTaxon(null); //remove this Taxon from relationship
524
            toTaxon.removeTaxonRelation(rel);
525
        }
526
    }
527

    
528
    /**
529
     * Adds an existing {@link TaxonRelationship taxon relationship} either to the set of
530
     * {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon} or to the set of
531
     * {@link #getRelationsFromThisTaxon() taxon relationships from <i>this</i> taxon}. If neither the
532
     * source nor the target of the taxon relationship match with <i>this</i> taxon
533
     * no addition will be carried out. The taxon relationship will also be
534
     * added to the second taxon involved in the given relationship.<P>
535
     * If the taxon relationship concerns the classification possible
536
     * modifications of the {@link #getTaxonomicParent() parent taxon} or of the number of
537
     * {@link #getTaxonomicChildrenCount() childrens} will be stored.
538
     *
539
     * @param rel  the taxon relationship to be added to one of <i>this</i> taxon's taxon relationships sets
540
     * @see    	   #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
541
     * @see    	   #getTaxonRelations()
542
     * @see    	   #getRelationsFromThisTaxon()
543
     * @see    	   #getRelationsToThisTaxon()
544
     * @see    	   #getTaxonomicParent()
545
     * @see    	   #getTaxonomicChildrenCount()
546
     */
547
    public void addTaxonRelation(TaxonRelationship rel) {
548
        if (rel!=null && rel.getType()!=null && !getTaxonRelations().contains(rel) ){
549
            Taxon toTaxon=rel.getToTaxon();
550
            Taxon fromTaxon=rel.getFromTaxon();
551
            if ( this.equals(toTaxon) || this.equals(fromTaxon) ){
552
                if (this.equals(fromTaxon)){
553
                    relationsFromThisTaxon.add(rel);
554
                    // also add relation to other taxon object
555
                    if (toTaxon!=null){
556
                        toTaxon.addTaxonRelation(rel);
557
                    }
558
                    // check if this sets the taxonomical parent. If so, remember a shortcut to this taxon
559
                    if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && toTaxon!=null ){
560
                        this.taxonomicParentCache = toTaxon;
561
                    }
562
                }else if (this.equals(toTaxon)){
563
                    relationsToThisTaxon.add(rel);
564
                    // also add relation to other taxon object
565
                    if (fromTaxon!=null){
566
                        fromTaxon.addTaxonRelation(rel);
567
                    }
568
                    if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && fromTaxon!=null ){
569
                        this.taxonomicChildrenCount++;
570
                    }
571

    
572
                }
573
            }else if (toTaxon == null || fromTaxon == null){
574
                if (toTaxon == null){
575
                    toTaxon = this;
576
                    relationsToThisTaxon.add(rel);
577
                    if (fromTaxon!= null){
578
                        fromTaxon.addTaxonRelation(rel);
579
                    }
580
                    if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && fromTaxon!=null ){
581
                        this.taxonomicChildrenCount++;
582
                    }
583
                }else if (fromTaxon == null && toTaxon != null){
584
                    fromTaxon = this;
585
                    relationsFromThisTaxon.add(rel);
586
                    if (toTaxon!=null){
587
                        toTaxon.addTaxonRelation(rel);
588
                    }
589
                    if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && toTaxon!=null ){
590
                        this.taxonomicParentCache = toTaxon;
591
                    }
592
                }
593
            }
594
        }
595
    }
596

    
597
    /* (non-Javadoc)
598
     * @see eu.etaxonomy.cdm.model.common.IRelated#addRelationship(eu.etaxonomy.cdm.model.common.RelationshipBase)
599
     */
600
    @Override
601
    @Deprecated //for inner use by RelationshipBase only
602
    public void addRelationship(RelationshipBase rel){
603
        if (rel instanceof TaxonRelationship){
604
            addTaxonRelation((TaxonRelationship)rel);
605
        }else if (rel instanceof SynonymRelationship){
606
            addSynonymRelation((SynonymRelationship)rel);
607
        }else{
608
            throw new ClassCastException("Wrong Relationsship type for Taxon.addRelationship");
609
        }
610
    }
611

    
612
    /**
613
     * Creates a new {@link TaxonRelationship taxon relationship} instance where <i>this</i> taxon
614
     * plays the source role and adds it to the set of
615
     * {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to <i>this</i> taxon.
616
     * The taxon relationship will also be added to the set of taxon
617
     * relationships to the second taxon involved in the created relationship.<P>
618
     * If the taxon relationship concerns the classification possible
619
     * modifications of the {@link #getTaxonomicParent() parent taxon} or of the number of
620
     * {@link #getTaxonomicChildrenCount() childrens} will be stored.
621
     *
622
     * @param toTaxon		the taxon which plays the target role in the new taxon relationship
623
     * @param type			the taxon relationship type for 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    	   			#addTaxonRelation(TaxonRelationship)
628
     * @see    	   			#getTaxonRelations()
629
     * @see    	   			#getRelationsFromThisTaxon()
630
     * @see    	   			#getRelationsToThisTaxon()
631
     * @see    	   			#getTaxonomicParent()
632
     * @see    	   			#getTaxonomicChildrenCount()
633
     */
634
    public TaxonRelationship addTaxonRelation(Taxon toTaxon, TaxonRelationshipType type, Reference citation, String microcitation) {
635
        return new TaxonRelationship(this, toTaxon, type, citation, microcitation);
636
    }
637
    /**
638
     * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
639
     * "misapplied name for") instance where <i>this</i> taxon plays the target role
640
     * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
641
     * The taxon relationship will also be added to the set of taxon
642
     * relationships to the other (misapplied name) taxon involved in the created relationship.
643
     *
644
     * @param misappliedNameTaxon	the taxon which plays the target role in the new taxon relationship
645
     * @param citation				the reference source for the new taxon relationship
646
     * @param microcitation			the string with the details describing the exact localisation within the reference
647
     * @return
648
     * @see    	   					#getMisappliedNames()
649
     * @see    	   					#addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
650
     * @see    	   					#addTaxonRelation(TaxonRelationship)
651
     * @see    	   					#getTaxonRelations()
652
     * @see    	   					#getRelationsFromThisTaxon()
653
     * @see    	   					#getRelationsToThisTaxon()
654
     */
655
    public TaxonRelationship addMisappliedName(Taxon misappliedNameTaxon, Reference citation, String microcitation) {
656
        return misappliedNameTaxon.addTaxonRelation(this, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), citation, microcitation);
657
    }
658

    
659
//	public void removeMisappliedName(Taxon misappliedNameTaxon){
660
//		Set<TaxonRelationship> taxRels = this.getTaxonRelations();
661
//		for (TaxonRelationship taxRel : taxRels ){
662
//			if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())
663
//				&& taxRel.getFromTaxon().equals(misappliedNameTaxon)){
664
//				this.removeTaxonRelation(taxRel);
665
//			}
666
//		}
667
//	}
668

    
669
    /**
670
     * TODO update documentation
671
     * Removes one {@link TaxonRelationship taxon relationship} with {@link TaxonRelationshipType taxon relationship type}
672
     * taxonRelType and with the given child taxon playing the
673
     * source role from the set of {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging
674
     * to <i>this</i> taxon. The taxon relationship will also be removed from the set
675
     * of {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to the other side taxon.
676
     * Furthermore, the inherited RelatedFrom and RelatedTo attributes of the
677
     * taxon relationship will be nullified.<P>
678
     *
679
     * @param taxon			the taxon which plays the source role in the taxon relationship
680
     * @param taxonRelType	the taxon relationship type
681
     */
682
    public void removeTaxon(Taxon taxon, TaxonRelationshipType taxonRelType){
683
        Set<TaxonRelationship> taxRels = this.getTaxonRelations();
684
        for (TaxonRelationship taxRel : taxRels ){
685
            if (taxRel.getType().equals(taxonRelType)
686
                && taxRel.getFromTaxon().equals(taxon)){
687
                this.removeTaxonRelation(taxRel);
688
            }
689
        }
690
    }
691

    
692
    /**
693
     * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
694
     * "taxonomically included in") instance where <i>this</i> taxon plays the target
695
     * role (parent) and adds it to the set of
696
     * {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging to <i>this</i> taxon.
697
     * The taxon relationship will also be added to the set of
698
     * {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to the second taxon
699
     * (child) involved in the created relationship.<P>
700
     * Since the taxon relationship concerns the modifications
701
     * of the number of {@link #getTaxonomicChildrenCount() childrens} for <i>this</i> taxon and
702
     * of the {@link #getTaxonomicParent() parent taxon} for the child taxon will be stored.
703
     * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
704
     * than the rank of the taxon name used as a child taxon.
705
     *
706
     * @param child			the taxon which plays the source role (child) in the new taxon relationship
707
     * @param citation		the reference source for the new taxon relationship
708
     * @param microcitation	the string with the details describing the exact localisation within the reference
709
     * @see    	   			#setTaxonomicParent(Taxon, Reference, String)
710
     * @see    	   			#addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
711
     * @see    	   			#addTaxonRelation(TaxonRelationship)
712
     * @see    	   			#getTaxonRelations()
713
     * @see    	   			#getRelationsFromThisTaxon()
714
     * @see    	   			#getRelationsToThisTaxon()
715
     * @see    	   			#getTaxonomicParent()
716
     * @see    	   			#getTaxonomicChildrenCount()
717
     */
718
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
719
    public void addTaxonomicChild(Taxon child, Reference citation, String microcitation){
720
        if (child == null){
721
            throw new NullPointerException("Child Taxon is 'null'");
722
        }else{
723
            child.setTaxonomicParent(this, citation, microcitation);
724
        }
725
    }
726

    
727
    /**
728
     * Removes one {@link TaxonRelationship taxon relationship} with {@link TaxonRelationshipType taxon relationship type}
729
     * "taxonomically included in" and with the given child taxon playing the
730
     * source role from the set of {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging
731
     * to <i>this</i> taxon. The taxon relationship will also be removed from the set
732
     * of {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to the child taxon.
733
     * Furthermore the inherited RelatedFrom and RelatedTo attributes of the
734
     * taxon relationship will be nullified.<P>
735
     * Since the taxon relationship concerns the classification modifications
736
     * of the number of {@link #getTaxonomicChildrenCount() childrens} for <i>this</i> taxon and
737
     * of the {@link #getTaxonomicParent() parent taxon} for the child taxon will be stored.
738
     *
739
     * @param  child	the taxon playing the source role in the relationship to be removed
740
     * @see    	    	#removeTaxonRelation(TaxonRelationship)
741
     * @see    			#getRelationsToThisTaxon()
742
     * @see    			#getRelationsFromThisTaxon()
743
     * @see    	    	#getTaxonomicParent()
744
     * @see    	    	#getTaxonomicChildrenCount()
745
     * @see    			eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom()
746
     * @see    			eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo()
747
     *
748
     */
749
    @Deprecated //will be removed in future versions. Use classification/TaxonNode instead
750
    public void removeTaxonomicChild(Taxon child){
751
        Set<TaxonRelationship> taxRels = this.getTaxonRelations();
752
        for (TaxonRelationship taxRel : taxRels ){
753
            if (taxRel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())
754
                && taxRel.getFromTaxon().equals(child)){
755
                this.removeTaxonRelation(taxRel);
756
            }
757
        }
758
    }
759

    
760
    /**
761
     * Returns the taxon which is the next higher taxon (parent) of <i>this</i> taxon
762
     * within the classification and which is stored in the
763
     * TaxonomicParentCache attribute. Each taxon can have only one parent taxon.
764
     * The child taxon and the parent taxon play the source respectively the
765
     * target role in one {@link TaxonRelationship taxon relationship} with
766
     * {@link TaxonRelationshipType taxon relationship type} "taxonomically included in".
767
     * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
768
     * than the rank of the taxon name used as a child taxon.
769
     *
770
     * @see  #setTaxonomicParent(Taxon, Reference, String)
771
     * @see  #getTaxonomicChildren()
772
     * @see  #getTaxonomicChildrenCount()
773
     * @see  #getRelationsFromThisTaxon()
774
     */
775
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
776
    public Taxon getTaxonomicParent() {
777
        return this.taxonomicParentCache;
778
    }
779

    
780
    /**
781
     * Sets the taxononomic parent of <i>this</i> taxon to null.
782
     * Note that this method does not handle taxonomic relationships.
783
     */
784
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
785
    public void nullifyTaxonomicParent() {
786
        this.taxonomicParentCache = null;
787
    }
788

    
789
    /**
790
     * Replaces both the taxonomic parent cache with the given new parent taxon
791
     * and the corresponding taxon relationship with a new {@link TaxonRelationship taxon relationship}
792
     * (with {@link TaxonRelationshipType taxon relationship type} "taxonomically included in") instance.
793
     * In the new taxon relationship <i>this</i> taxon plays the source role (child).
794
     * This method creates and adds the new taxon relationship to the set of
795
     * {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to <i>this</i> taxon.
796
     * The taxon relationship will also be added to the set of
797
     * {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging to the second taxon
798
     * (parent) involved in the new relationship.<P>
799
     * Since the taxon relationship concerns the classification modifications
800
     * of the {@link #getTaxonomicParent() parent taxon} for <i>this</i> taxon and of the number of
801
     * {@link #getTaxonomicChildrenCount() childrens} for the child taxon will be stored.
802
     *
803
     * @param newParent		the taxon which plays the target role (parent) in the new taxon relationship
804
     * @param citation		the reference source for the new taxon relationship
805
     * @param microcitation	the string with the details describing the exact localisation within the reference
806
     * @see    	   			#removeTaxonRelation(TaxonRelationship)
807
     * @see    	   			#getTaxonomicParent()
808
     * @see    	   			#addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
809
     * @see    	   			#addTaxonRelation(TaxonRelationship)
810
     * @see    	   			#getTaxonRelations()
811
     * @see    	   			#getRelationsFromThisTaxon()
812
     * @see    	   			#getRelationsToThisTaxon()
813
     * @see    	   			#getTaxonomicChildrenCount()
814
     */
815
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
816
    public void setTaxonomicParent(Taxon newParent, Reference citation, String microcitation){
817
        //remove previously existing parent relationship!!!
818
        Taxon oldParent = this.getTaxonomicParent();
819
        Set<TaxonRelationship> taxRels = this.getTaxonRelations();
820
        for (TaxonRelationship taxRel : taxRels ){
821
            if (taxRel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && taxRel.getToTaxon().equals(oldParent)){
822
                this.removeTaxonRelation(taxRel);
823
            }
824
        }
825
        //add new parent
826
        if (newParent != null){
827
            addTaxonRelation(newParent, TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN(),citation,microcitation);
828
        }
829
    }
830

    
831
    /**
832
     * Returns the set of taxa which have <i>this</i> taxon as next higher taxon
833
     * (parent) within the classification. Each taxon can have several child
834
     * taxa. The child taxon and the parent taxon play the source respectively
835
     * the target role in one {@link TaxonRelationship taxon relationship} with
836
     * {@link TaxonRelationshipType taxon relationship type} "taxonomically included in".
837
     * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
838
     * than the rank of the taxon name used as a child taxon.
839
     *
840
     * @see  #getTaxonomicParent()
841
     * @see  #addTaxonomicChild(Taxon, Reference, String)
842
     * @see  #getTaxonomicChildrenCount()
843
     * @see  #getRelationsToThisTaxon()
844
     */
845
    @Transient
846
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
847
    public Set<Taxon> getTaxonomicChildren() {
848
        Set<Taxon> taxa = new HashSet<Taxon>();
849
        Set<TaxonRelationship> rels = this.getRelationsToThisTaxon();
850
        for (TaxonRelationship rel: rels){
851
            TaxonRelationshipType tt = rel.getType();
852
            TaxonRelationshipType incl = TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN();
853
            if (tt.equals(incl)){
854
                taxa.add(rel.getFromTaxon());
855
            }
856
        }
857
        return taxa;
858
    }
859

    
860
    /**
861
     * Returns the number of taxa which have <i>this</i> taxon as next higher taxon
862
     * (parent) within the classification and the number of which is stored in
863
     * the TaxonomicChildrenCount attribute. Each taxon can have several child
864
     * taxa. The child taxon and the parent taxon play the source respectively
865
     * the target role in one {@link TaxonRelationship taxon relationship} with
866
     * {@link TaxonRelationshipType taxon relationship type} "taxonomically included in".
867
     * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
868
     * than the rank of the taxon name used as a child taxon.
869
     *
870
     * @see  #getTaxonomicChildren()
871
     * @see  #getRelationsToThisTaxon()
872
     */
873
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
874
    public int getTaxonomicChildrenCount(){
875
        return taxonomicChildrenCount;
876
    }
877

    
878
    /**
879
     * @see  #getTaxonomicChildrenCount()
880
     */
881
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
882
    public void setTaxonomicChildrenCount(int taxonomicChildrenCount) {
883
        this.taxonomicChildrenCount = taxonomicChildrenCount;
884
    }
885

    
886
    /**
887
     * Returns the boolean value indicating whether <i>this</i> taxon has at least one
888
     * taxonomic child taxon within the classification (true) or not (false).
889
     *
890
     * @see  #getTaxonomicChildrenCount()
891
     * @see  #getTaxonomicChildren()
892
     */
893
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
894
    public boolean hasTaxonomicChildren(){
895
        return this.taxonomicChildrenCount > 0;
896
    }
897

    
898
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
899
    private int computeTaxonomicChildrenCount(){
900
        int count = 0;
901
        for (TaxonRelationship rel: this.getRelationsToThisTaxon()){
902
            if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())){
903
                count++;
904
            }
905
        }
906
        return count;
907
    }
908

    
909

    
910
    /**
911
     * Returns the boolean value indicating whether <i>this</i> taxon is a misaplication
912
     * (misapplied name) for at least one other taxon.
913
     */
914
    // TODO cache as for #hasTaxonomicChildren
915
    @Transient
916
    public boolean isMisapplication(){
917
        return computeMisapliedNameRelations() > 0;
918
    }
919

    
920
    /**
921
     * Counts the number of misaplied names relationships where this taxon represents the
922
     * misaplied name for another taxon.
923
     * @return
924
     */
925
    private int computeMisapliedNameRelations(){
926
        int count = 0;
927
        for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
928
            if (rel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
929
                count++;
930
            }
931
        }
932
        return count;
933
    }
934

    
935
    /**
936
     * Returns the boolean value indicating whether <i>this</i> taxon is a related
937
     * concept for at least one other taxon.
938
     */
939
    @Transient
940
    public boolean isRelatedConcept(){
941
        return computeConceptRelations() > 0;
942
    }
943

    
944
    /**
945
     * Counts the number of concept relationships where this taxon represents the
946
     * related concept for another taxon.
947
     * @return
948
     */
949
    private int computeConceptRelations(){
950
        int count = 0;
951
        for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
952
            TaxonRelationshipType type = rel.getType();
953
            if (type.isConceptRelationship()){
954
                count++;
955
            }
956
        }
957
        return count;
958
    }
959

    
960
    /**
961
     * Returns the boolean value indicating whether <i>this</i> taxon has at least one
962
     * {@link Synonym synonym} (true) or not (false). If true the {@link #getSynonymRelations() set of synonym relationships}
963
     * belonging to <i>this</i> ("accepted/correct") taxon is not empty .
964
     *
965
     * @see  #getSynonymRelations()
966
     * @see  #getSynonyms()
967
     * @see  #getSynonymNames()
968
     * @see  #removeSynonym(Synonym)
969
     * @see  SynonymRelationship
970
     */
971
    public boolean hasSynonyms(){
972
        return this.getSynonymRelations().size() > 0;
973
    }
974

    
975

    
976
    /**
977
     * Returns the boolean value indicating whether <i>this</i> taxon is at least
978
     * involved in one {@link #getTaxonRelations() taxon relationship} between
979
     * two taxa (true), either as a source or as a target, or not (false).
980
     *
981
     * @see  #getTaxonRelations()
982
     * @see  #getRelationsToThisTaxon()
983
     * @see  #getRelationsFromThisTaxon()
984
     * @see  #removeTaxonRelation(TaxonRelationship)
985
     * @see  TaxonRelationship
986
     */
987
    public boolean hasTaxonRelationships(){
988
        return this.getTaxonRelations().size() > 0;
989
    }
990

    
991
    /*
992
     * MISAPPLIED NAMES
993
     */
994
    /**
995
     * Returns the set of taxa playing the source role in {@link TaxonRelationship taxon relationships}
996
     * (with {@link TaxonRelationshipType taxon relationship type} "misapplied name for") where
997
     * <i>this</i> taxon plays the target role. A misapplied name is a taxon the
998
     * {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} of which has been erroneously used
999
     * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
1000
     * as the one meant by <i>this</i> ("accepted/correct") taxon.
1001
     *
1002
     * @see  #getTaxonRelations()
1003
     * @see  #getRelationsToThisTaxon()
1004
     * @see  #addMisappliedName(Taxon, Reference, String)
1005
     */
1006
    @Transient
1007
    public Set<Taxon> getMisappliedNames(){
1008
        Set<Taxon> taxa = new HashSet<Taxon>();
1009
        Set<TaxonRelationship> rels = this.getRelationsToThisTaxon();
1010
        for (TaxonRelationship rel: rels){
1011
            TaxonRelationshipType tt = rel.getType();
1012
            TaxonRelationshipType incl = TaxonRelationshipType.MISAPPLIED_NAME_FOR();
1013
            if (tt.equals(incl)){
1014
                taxa.add(rel.getFromTaxon());
1015
            }
1016
        }
1017
        return taxa;
1018
    }
1019
    /**
1020
     * Returns the set of taxa playing the target role in {@link TaxonRelationship taxon relationships}
1021
     * (with {@link TaxonRelationshipType taxon relationship type} "misapplied name for") where
1022
     * <i>this</i> taxon plays the source role. A misapplied name is a taxon the
1023
     * {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} of which has been erroneously used
1024
     * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
1025
     * as the one meant by <i>this</i> ("accepted/correct") taxon.
1026
     *
1027
     * @see  #getTaxonRelations()
1028
     * @see  #getRelationsToThisTaxon()
1029
     * @see  #addMisappliedName(Taxon, Reference, String)
1030
     */
1031
    @Transient
1032
    public Set<Taxon> getTaxonForMisappliedName(){
1033
        Set<Taxon> taxa = new HashSet<Taxon>();
1034
        Set<TaxonRelationship> rels = this.getRelationsFromThisTaxon();
1035
        for (TaxonRelationship rel: rels){
1036
            TaxonRelationshipType tt = rel.getType();
1037
            TaxonRelationshipType incl = TaxonRelationshipType.MISAPPLIED_NAME_FOR();
1038
            if (tt.equals(incl)){
1039
                taxa.add(rel.getToTaxon());
1040
            }
1041
        }
1042
        return taxa;
1043
    }
1044

    
1045

    
1046
    /*
1047
     * DEALING WITH SYNONYMS
1048
     */
1049
    /**
1050
     * Returns the set of all {@link Synonym synonyms} of <i>this</i> ("accepted/correct") taxon.
1051
     * Each synonym is the source and <i>this</i> taxon is the target of a {@link SynonymRelationship synonym relationship}
1052
     * belonging to the {@link #getSynonymRelations() set of synonym relationships} assigned to <i>this</i> taxon.
1053
     * For a particular synonym and for a particular ("accepted/correct") taxon
1054
     * there can be several synonym relationships (if two or more
1055
     * {@link SynonymRelationshipType synonym relationship types} - for instance
1056
     * "pro parte synonym of" and "is homotypic synonym of" - must be combined).
1057
     *
1058
     * @see    #getSynonymsSortedByType()
1059
     * @see    #getSynonymNames()
1060
     * @see    #getSynonymRelations()
1061
     * @see    #addSynonym(Synonym, SynonymRelationshipType)
1062
     * @see    #addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1063
     * @see    #removeSynonymRelation(SynonymRelationship)
1064
     * @see    #removeSynonym(Synonym)
1065
     */
1066
    @Transient
1067
    public Set<Synonym> getSynonyms(){
1068
        Set<Synonym> syns = new HashSet<Synonym>();
1069
        for (SynonymRelationship rel: this.getSynonymRelations()){
1070
            syns.add(rel.getSynonym());
1071
        }
1072
        return syns;
1073
    }
1074
    /**
1075
     * Returns the set of all {@link Synonym synonyms} of <i>this</i> ("accepted/correct") taxon
1076
     * sorted by the different {@link SynonymRelationshipType categories of synonym relationships}.
1077
     * Each synonym is the source and <i>this</i> taxon is the target of a {@link SynonymRelationship synonym relationship}
1078
     * belonging to the {@link #getSynonymRelations() set of synonym relationships} assigned to <i>this</i> taxon.
1079
     *
1080
     * @see    #getSynonyms()
1081
     * @see    #getSynonymNames()
1082
     * @see    #getSynonymRelations()
1083
     * @see    #addSynonym(Synonym, SynonymRelationshipType)
1084
     * @see    #addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1085
     * @see    #removeSynonymRelation(SynonymRelationship)
1086
     * @see    #removeSynonym(Synonym)
1087
     */
1088
    @Transient
1089
    public Set<Synonym> getSynonymsSortedByType(){
1090
        // FIXME: need to sort synonyms according to type!!!
1091
        logger.warn("getSynonymsSortedByType() not yet implemented");
1092
        return getSynonyms();
1093
    }
1094
    /**
1095
     * Returns the set of all {@link name.TaxonNameBase taxon names} used as {@link Synonym synonyms}
1096
     * of <i>this</i> ("accepted/correct") taxon. Each synonym is the source and
1097
     * <i>this</i> taxon is the target of a {@link SynonymRelationship synonym relationship} belonging
1098
     * to the {@link #getSynonymRelations() set of synonym relationships} assigned to <i>this</i> taxon.
1099
     *
1100
     * @see    #getSynonyms()
1101
     * @see    #getSynonymsSortedByType()
1102
     * @see    #getSynonymRelations()
1103
     * @see    #addSynonymName(TaxonNameBase, SynonymRelationshipType)
1104
     * @see    #addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1105
     * @see    #removeSynonymRelation(SynonymRelationship)
1106
     * @see    #removeSynonym(Synonym)
1107
     */
1108
    @Transient
1109
    public Set<TaxonNameBase> getSynonymNames(){
1110
        Set<TaxonNameBase> names = new HashSet<TaxonNameBase>();
1111
        for (SynonymRelationship rel: this.getSynonymRelations()){
1112
            names.add(rel.getSynonym().getName());
1113
        }
1114
        return names;
1115
    }
1116
    /**
1117
     * Creates a new {@link SynonymRelationship synonym relationship} (with the given {@link Synonym synonym}
1118
     * and with the given {@link SynonymRelationshipType synonym relationship type}), returns it and adds it
1119
     * to the set of {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon.
1120
     * The new synonym relationship will also be added to the set of
1121
     * {@link Synonym#getSynonymRelations() synonym relationships} belonging to the synonym
1122
     * involved in this synonym relationship.<BR>
1123
     * The returned synonym relationship allows to add further information to it.
1124
     *
1125
     * @param synonym		the synonym involved in the relationship to be created
1126
     * 						and added to <i>this</i> taxon's synonym relationships set
1127
     * @param synonymType	the synonym relationship category of the synonym
1128
     * 						relationship to be added
1129
     * @return 				the created synonym relationship
1130
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1131
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1132
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1133
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1134
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1135
     * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1136
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1137
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1138
     * @see    	   			#getSynonymRelations()
1139
     * @see    				#removeSynonym(Synonym)
1140
     * @see    	   			Synonym#getSynonymRelations()
1141
     */
1142
    public SynonymRelationship addSynonym(Synonym synonym, SynonymRelationshipType synonymType){
1143
        return addSynonym(synonym, synonymType, null, null);
1144
    }
1145
    /**
1146
     * Creates a new {@link SynonymRelationship synonym relationship} (with the given {@link Synonym synonym},
1147
     * with the given {@link SynonymRelationshipType synonym relationship type} and with the
1148
     * {@link eu.etaxonomy.cdm.model.reference.Reference reference source} on which the relationship assertion is based),
1149
     * returns it and adds it to the set of {@link #getSynonymRelations() synonym relationships}
1150
     * assigned to <i>this</i> taxon. The new synonym relationship will also be
1151
     * added to the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging to the synonym
1152
     * involved in this synonym relationship.<BR>
1153
     * The returned synonym relationship allows to add further information to it.
1154
     *
1155
     * @param synonym		the synonym involved in the relationship to be created
1156
     * 						and added to <i>this</i> taxon's synonym relationships set
1157
     * @param synonymType	the synonym relationship category of the synonym
1158
     * 						relationship to be added
1159
     * @param citation		the reference source for the new synonym relationship
1160
     * @param microcitation	the string with the details describing the exact localisation within the reference
1161
     * @return 				the created synonym relationship
1162
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1163
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1164
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1165
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1166
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1167
     * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1168
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1169
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1170
     * @see    	   			#getSynonymRelations()
1171
     * @see    				#removeSynonym(Synonym)
1172
     * @see    	   			Synonym#getSynonymRelations()
1173
     */
1174
    public SynonymRelationship addSynonym(Synonym synonym, SynonymRelationshipType synonymType, Reference citation, String citationMicroReference){
1175
        SynonymRelationship synonymRelationship = new SynonymRelationship(synonym, this, synonymType, citation, citationMicroReference);
1176
        return synonymRelationship;
1177
    }
1178

    
1179
    /**
1180
     * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1181
     * a new {@link SynonymRelationship synonym relationship} (with the new synonym and with the given
1182
     * {@link SynonymRelationshipType synonym relationship type}), returns the relationship and adds it
1183
     * to the set of {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon.
1184
     * The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1185
     * as <i>this</i> taxon. The new synonym relationship will also be added to
1186
     * the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1187
     * to the created synonym.<BR>
1188
     * The returned synonym relationship allows to add further information to it.
1189
     *
1190
     * @param synonymName	the taxon name to be used as a synonym to be added
1191
     * 						to <i>this</i> taxon's set of synonyms
1192
     * @param synonymType	the synonym relationship category of the synonym
1193
     * 						relationship to be added
1194
     * @return 				the created synonym relationship
1195
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1196
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1197
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1198
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1199
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1200
     * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1201
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1202
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1203
     * @see    	   			#getSynonymRelations()
1204
     * @see    				#removeSynonym(Synonym)
1205
     * @see    	   			Synonym#getSynonymRelations()
1206
     */
1207
    public SynonymRelationship addSynonymName(TaxonNameBase synonymName, SynonymRelationshipType synonymType){
1208
        return addSynonymName(synonymName, synonymType, null, null);
1209
    }
1210
    /**
1211
     * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1212
     * a new {@link SynonymRelationship synonym relationship} (with the new synonym, with the given
1213
     * {@link SynonymRelationshipType synonym relationship type} and with the {@link eu.etaxonomy.cdm.model.reference.Reference reference source}
1214
     * on which the relationship assertion is based), returns the relationship
1215
     * and adds it to the set of {@link #getSynonymRelations() synonym relationships} assigned
1216
     * to <i>this</i> taxon. The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1217
     * as <i>this</i> taxon. The new synonym relationship will also be added to
1218
     * the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1219
     * to the created synonym.<BR>
1220
     * The returned synonym relationship allows to add further information to it.
1221
     *
1222
     * @param synonymName	the taxon name to be used as a synonym to be added
1223
     * 						to <i>this</i> taxon's set of synonyms
1224
     * @param synonymType	the synonym relationship category of the synonym
1225
     * 						relationship to be added
1226
     * @param citation		the reference source for the new synonym relationship
1227
     * @param microcitation	the string with the details describing the exact localisation within the reference
1228
     * @return 				the created synonym relationship
1229
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1230
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1231
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1232
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1233
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1234
     * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1235
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1236
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1237
     * @see    	   			#getSynonymRelations()
1238
     * @see    				#removeSynonym(Synonym)
1239
     * @see    	   			Synonym#getSynonymRelations()
1240
     */
1241
    public SynonymRelationship addSynonymName(TaxonNameBase synonymName, SynonymRelationshipType synonymType, Reference citation, String citationMicroReference){
1242
        Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
1243
        return addSynonym(synonym, synonymType, citation, citationMicroReference);
1244
    }
1245

    
1246
    /**
1247
     * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1248
     * a new {@link SynonymRelationship synonym relationship} (with the new synonym and with the
1249
     * {@link SynonymRelationshipType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of" relationship type}),
1250
     * returns the relationship and adds it to the set of
1251
     * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon.
1252
     * The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1253
     * as <i>this</i> taxon. The new synonym relationship will also be added to
1254
     * the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1255
     * to the created synonym.<BR>
1256
     * The returned synonym relationship allows to add further information to it.
1257
     *
1258
     * @param synonymName	the taxon name to be used as an heterotypic synonym
1259
     * 						to be added to <i>this</i> taxon's set of synonyms
1260
     * @return 				the created synonym relationship
1261
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1262
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1263
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1264
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1265
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1266
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1267
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1268
     * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1269
     * @see    	   			#getSynonymRelations()
1270
     * @see    				#removeSynonym(Synonym)
1271
     * @see    	   			Synonym#getSynonymRelations()
1272
     */
1273
    public SynonymRelationship addHeterotypicSynonymName(TaxonNameBase synonymName){
1274
        return addHeterotypicSynonymName(synonymName, null, null, null);
1275
    }
1276

    
1277
    /**
1278
     * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1279
     * a new {@link SynonymRelationship synonym relationship} (with the new synonym, with the
1280
     * {@link SynonymRelationshipType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of" relationship type}
1281
     * and with the {@link eu.etaxonomy.cdm.model.reference.Reference reference source}
1282
     * on which the relationship assertion is based), returns the relationship
1283
     * and adds it to the set of {@link #getSynonymRelations() synonym relationships} assigned
1284
     * to <i>this</i> taxon. The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1285
     * as <i>this</i> taxon. Furthermore the new synonym relationship will be
1286
     * added to the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1287
     * to the created synonym and the taxon name used as synonym will be added
1288
     * to the given {@link name.HomotypicalGroup homotypical group}.<BR>
1289
     * The returned synonym relationship allows to add further information to it.
1290
     *
1291
     * @param synonymName		the taxon name to be used as an heterotypic synonym
1292
     * 							to be added to <i>this</i> taxon's set of synonyms
1293
     * @param homotypicalGroup	the homotypical group to which the taxon name
1294
     * 							of the synonym will be added
1295
     * @param citation			the reference source for the new synonym relationship
1296
     * @param microcitation		the string with the details describing the exact localisation
1297
     * 							within the reference
1298
     * @return 					the created synonym relationship
1299
     * @see    	   				#addHeterotypicSynonymName(TaxonNameBase)
1300
     * @see    	   				#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1301
     * @see    	   				#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1302
     * @see    	   				#addSynonym(Synonym, SynonymRelationshipType)
1303
     * @see    	   				#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1304
     * @see    	   				#addSynonymRelation(SynonymRelationship)
1305
     * @see    	   				#addHomotypicSynonym(Synonym, Reference, String)
1306
     * @see    	   				#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1307
     * @see    	   				#getSynonymRelations()
1308
     * @see    					#removeSynonym(Synonym)
1309
     * @see    	   				Synonym#getSynonymRelations()
1310
     */
1311
    public SynonymRelationship addHeterotypicSynonymName(TaxonNameBase synonymName, HomotypicalGroup homotypicalGroup, Reference citation, String microCitation){
1312
        Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
1313
        if (homotypicalGroup != null){
1314
            homotypicalGroup.addTypifiedName(synonymName);
1315
        }
1316
        return addSynonym(synonym, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), citation, microCitation);
1317
    }
1318

    
1319
    /**
1320
     * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1321
     * a new {@link SynonymRelationship synonym relationship} (with the new synonym, with the
1322
     * {@link SynonymRelationshipType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of" relationship type})
1323
     * and with the {@link eu.etaxonomy.cdm.model.reference.Reference reference source}
1324
     * on which the relationship assertion is based), returns the relationship
1325
     * and adds it to the set of {@link #getSynonymRelations() synonym relationships} assigned
1326
     * to <i>this</i> taxon. The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1327
     * as <i>this</i> taxon. Furthermore the new synonym relationship will be
1328
     * added to the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1329
     * to the created synonym and the taxon name used as synonym will be added
1330
     * to the same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group} to which the taxon name
1331
     * of <i>this</i> taxon belongs.<BR>
1332
     * The returned synonym relationship allows to add further information to it.
1333
     *
1334
     * @param synonymName	the taxon name to be used as an homotypic synonym
1335
     * 						to be added to <i>this</i> taxon's set of synonyms
1336
     * @param citation		the reference source for the new synonym relationship
1337
     * @param microcitation	the string with the details describing the exact localisation
1338
     * 						within the reference
1339
     * @return 				the created synonym relationship
1340
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1341
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1342
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1343
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1344
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1345
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1346
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1347
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1348
     * @see    	   			#getSynonymRelations()
1349
     * @see    				#removeSynonym(Synonym)
1350
     * @see    	   			Synonym#getSynonymRelations()
1351
     */
1352
    public SynonymRelationship addHomotypicSynonymName(TaxonNameBase synonymName, Reference citation, String microCitation){
1353
        Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
1354
        return addHomotypicSynonym(synonym, citation, microCitation);
1355
    }
1356

    
1357
    /**
1358
     * Creates a new {@link SynonymRelationship synonym relationship} (with the given {@link Synonym synonym},
1359
     * with the {@link SynonymRelationshipType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of" relationship type}
1360
     * and with the {@link eu.etaxonomy.cdm.model.reference.Reference reference source} on which the relationship
1361
     * assertion is based), returns it and adds it to the set of
1362
     * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon.
1363
     * Furthermore the new synonym relationship will be added to the set of
1364
     * {@link Synonym#getSynonymRelations() synonym relationships} belonging to the synonym
1365
     * involved in this synonym relationship and the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}
1366
     * used as synonym will be added to the same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group}
1367
     * to which the taxon name of <i>this</i> taxon belongs.<BR>
1368
     * The returned synonym relationship allows to add further information to it.
1369
     *
1370
     * @param synonym		the synonym involved in the "is homotypic synonym of" relationship to be created
1371
     * 						and added to <i>this</i> taxon's synonym relationships set
1372
     * @param citation		the reference source for the new synonym relationship
1373
     * @param microcitation	the string with the details describing the exact localisation within the reference
1374
     * @return 				the created synonym relationship
1375
     * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1376
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1377
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1378
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1379
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1380
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1381
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1382
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1383
     * @see    	   			#getSynonymRelations()
1384
     * @see    				#removeSynonym(Synonym)
1385
     * @see    	   			Synonym#getSynonymRelations()
1386
     */
1387
    public SynonymRelationship addHomotypicSynonym(Synonym synonym, Reference citation, String microCitation){
1388
    if (this.getName() != null){
1389
            if (this.getName().getHomotypicalGroup().getTypifiedNames().isEmpty()){
1390
                this.getName().getHomotypicalGroup().getTypifiedNames().add(this.getName());
1391

    
1392
            }
1393
            this.getName().getHomotypicalGroup().addTypifiedName(synonym.getName());
1394

    
1395
        }
1396
        SynonymRelationship synRel = addSynonym(synonym, SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF(), citation, microCitation);
1397
        return synRel;
1398
    }
1399

    
1400
    /**
1401
     * Like {@link #removeSynonym(Synonym, boolean)} with <code>removeSynonymNameFromHomotypicalGroup</code> set to true.
1402
     * @see #removeSynonym(Synonym, boolean)
1403
     */
1404
    public void removeSynonym(Synonym synonym){
1405
        removeSynonym(synonym, true);
1406
    }
1407

    
1408
    /**
1409
     * Removes the element(s) from the set of {@link SynonymRelationship synonym relationships}
1410
     * assigned to <i>this</i> ("accepted/valid") taxon in which the given synonym is involved.
1411
     * Due to bidirectionality the same synonym relationships will also be
1412
     * removed from the set of synonym relationships assigned to the
1413
     * {@link Synonym#getSynonymRelations() synonym} involved in the relationship. Furthermore the content of
1414
     * the {@link SynonymRelationship#getAcceptedTaxon() accepted taxon} attribute and of the
1415
     * {@link SynonymRelationship#getSynonym() synonym} attribute within the synonym relationships
1416
     * themselves will be set to "null".
1417
     *
1418
     * @param  synonym  the synonym involved in the synonym relationship which should be deleted
1419
     * @param removeSynonymNameFromHomotypicalGroup if <code>true</code> the removed synonyms
1420
     * 		name will get a new homotypic group in case it is together with other names in a group.
1421
     * @see     		#getSynonymRelations()
1422
     * @see     		#addSynonym(Synonym, SynonymRelationshipType)
1423
     * @see     		#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1424
     * @see 			#removeSynonymRelation(SynonymRelationship)
1425
     * @see				#removeSynonymRelation(SynonymRelationship, boolean)
1426
     */
1427
    public void removeSynonym(Synonym synonym, boolean removeSynonymNameFromHomotypicalGroup){
1428
        Set<SynonymRelationship> synonymRelationships = new HashSet<SynonymRelationship>();
1429
        synonymRelationships.addAll(this.getSynonymRelations());
1430
        for(SynonymRelationship synonymRelationship : synonymRelationships){
1431
            if (synonymRelationship.getAcceptedTaxon().equals(this) && synonymRelationship.getSynonym().equals(synonym)){
1432
                this.removeSynonymRelation(synonymRelationship, removeSynonymNameFromHomotypicalGroup);
1433
            }
1434
        }
1435
    }
1436

    
1437

    
1438
    /**
1439
     * Retrieves the ordered list (depending on the date of publication) of
1440
     * homotypic {@link Synonym synonyms} (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1441
     * as for <i>this</i> taxon) under the condition that the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon names}
1442
     * of these synonyms and the taxon name of <i>this</i> taxon belong to the
1443
     * same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group}.
1444
     *
1445
     * @return		the ordered list of homotypic synonyms
1446
     * @see			#getHomotypicSynonymsByHomotypicRelationship()
1447
     * @see			#getSynonyms()
1448
     * @see			#getHomotypicSynonymyGroups()
1449
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1450
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup#getSynonymsInGroup(Reference)
1451
     */
1452
    @Transient
1453
    public List<Synonym> getHomotypicSynonymsByHomotypicGroup(){
1454
        if (this.getHomotypicGroup() == null){
1455
            return null;
1456
        }else{
1457
            return this.getSynonymsInGroup(this.getHomotypicGroup());
1458
        }
1459
    }
1460

    
1461
    /**
1462
     * Retrieves the ordered list (depending on the date of publication) of
1463
     * homotypic {@link Synonym synonyms} (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1464
     * as for <i>this</i> taxon) under the condition that these synonyms and
1465
     * <i>this</i> taxon are involved in {@link SynonymRelationship synonym relationships} with an
1466
     * "is homotypic synonym of" {@link SynonymRelationshipType#HOMOTYPIC_SYNONYM_OF() synonym relationship type}.
1467
     *
1468
     * @return		the ordered list of homotypic synonyms
1469
     * @see			#getHomotypicSynonymsByHomotypicGroup()
1470
     * @see			#getSynonyms()
1471
     * @see			#getHomotypicSynonymyGroups()
1472
     * @see			SynonymRelationshipType
1473
     */
1474
    @Transient
1475
    public List<Synonym> getHomotypicSynonymsByHomotypicRelationship(){
1476
        Set<SynonymRelationship> synonymRelations = this.getSynonymRelations();
1477
        List<Synonym> result = new ArrayList<Synonym>();
1478
        for(SynonymRelationship synonymRelation : synonymRelations) {
1479
            if(synonymRelation.getType().equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){
1480
                result.add(synonymRelation.getSynonym());
1481
            }
1482
        }
1483
        return result;
1484
    }
1485

    
1486
    /**
1487
     * Returns the ordered list of all {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups} {@link Synonym synonyms} of
1488
     * <i>this</i> taxon belong to. {@link eu.etaxonomy.cdm.model.name.TaxonNameBase Taxon names} of homotypic synonyms
1489
     * belong to the same homotypical group as the taxon name of <i>this</i>
1490
     * taxon. Taxon names of heterotypic synonyms belong to at least one other
1491
     * homotypical group. <BR>
1492
     * The list returned is ordered according to the date of publication of the
1493
     * first published name within each homotypical group.
1494
     *
1495
     * @see			#getHeterotypicSynonymyGroups()
1496
     * @see			#getSynonyms()
1497
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1498
     */
1499
    @Transient
1500
    public List<HomotypicalGroup> getHomotypicSynonymyGroups(){
1501
        List<HomotypicalGroup> result = new ArrayList<HomotypicalGroup>();
1502
        result.add(this.getHomotypicGroup());
1503
        for (TaxonNameBase taxonNameBase :this.getSynonymNames()){
1504
            if (taxonNameBase != null) {
1505
                if (!result.contains(taxonNameBase.getHomotypicalGroup())){
1506
                    result.add(taxonNameBase.getHomotypicalGroup());
1507
                }
1508
            } // TODO: give error message to user
1509
        }
1510
        // TODO: sort list according to date of first published name within each group
1511
        return result;
1512
    }
1513

    
1514

    
1515

    
1516
    /**
1517
     * The status of this taxon is unknown it could also be some kind of synonym.
1518
     * @return the taxonStatusUnknown
1519
     */
1520
    public boolean isTaxonStatusUnknown() {
1521
        return taxonStatusUnknown;
1522
    }
1523

    
1524
    /**
1525
     * @param taxonStatusUnknown the taxonStatusUnknown to set
1526
     */
1527
    public void setTaxonStatusUnknown(boolean taxonStatusUnknown) {
1528
        this.taxonStatusUnknown = taxonStatusUnknown;
1529
    }
1530

    
1531

    
1532

    
1533

    
1534
    public boolean isUnplaced() {
1535
        return unplaced;
1536
    }
1537

    
1538
    @Override
1539
    @Transient
1540
    public boolean isOrphaned() {
1541

    
1542
        if(taxonNodes == null || taxonNodes.isEmpty()) {
1543
            if(getRelationsFromThisTaxon().isEmpty() && getRelationsToThisTaxon().isEmpty()) {
1544
                return true;
1545
            }
1546
        }
1547
        return false;
1548
    }
1549

    
1550
    public void setUnplaced(boolean unplaced) {
1551
        this.unplaced = unplaced;
1552
    }
1553

    
1554
    public boolean isExcluded() {
1555
        return excluded;
1556
    }
1557

    
1558
    public void setExcluded(boolean excluded) {
1559
        this.excluded = excluded;
1560
    }
1561

    
1562
    /**
1563
     * Returns the ordered list of all {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups}
1564
     * that contain {@link Synonym synonyms} that are heterotypic to <i>this</i> taxon.
1565
     * {@link eu.etaxonomy.cdm.model.name.TaxonNameBase Taxon names} of heterotypic synonyms
1566
     * belong to a homotypical group which cannot be the homotypical group to which the
1567
     * taxon name of <i>this</i> taxon belongs. This method returns the same
1568
     * list as the {@link #getHomotypicSynonymyGroups() getHomotypicSynonymyGroups} method
1569
     * but without the homotypical group to which the taxon name of <i>this</i> taxon
1570
     * belongs.<BR>
1571
     * The list returned is ordered according to the date of publication of the
1572
     * first published name within each homotypical group.
1573
     *
1574
     * @see			#getHeterotypicSynonymyGroups()
1575
     * @see			#getSynonyms()
1576
     * @see			SynonymRelationshipType#HETEROTYPIC_SYNONYM_OF()
1577
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1578
     */
1579
    @Transient
1580
    public List<HomotypicalGroup> getHeterotypicSynonymyGroups(){
1581
        List<HomotypicalGroup> list = getHomotypicSynonymyGroups();
1582
        list.remove(this.getHomotypicGroup());
1583
        //sort
1584
        Map<Synonym, HomotypicalGroup> map = new HashMap<Synonym, HomotypicalGroup>();
1585
        for (HomotypicalGroup homotypicalGroup: list){
1586
            List<Synonym> synonymList = getSynonymsInGroup(homotypicalGroup);
1587
            if (synonymList.size() > 0){
1588
                map.put(synonymList.get(0), homotypicalGroup);
1589
            }
1590
        }
1591
        List<Synonym> keyList = new ArrayList<Synonym>();
1592
        keyList.addAll(map.keySet());
1593
        Collections.sort(keyList, new TaxonComparator());
1594

    
1595
        List<HomotypicalGroup> result = new ArrayList<HomotypicalGroup>();
1596
        for(Synonym synonym: keyList){
1597
            result.add(map.get(synonym));
1598
        }
1599
        //sort end
1600
        return result;
1601
    }
1602

    
1603
    /**
1604
     * Retrieves the ordered list (depending on the date of publication) of
1605
     * {@link taxon.Synonym synonyms} (according to a given reference)
1606
     * the {@link TaxonNameBase taxon names} of which belong to the homotypical group.
1607
     * If other names are part of the group that are not considered synonyms of
1608
     * <i>this</i> taxon, then they will not be included in
1609
     * the result set.
1610
     *
1611
     * @param homoGroup
1612
     * @see			TaxonNameBase#getSynonyms()
1613
     * @see			TaxonNameBase#getTaxa()
1614
     * @see			taxon.Synonym
1615
     */
1616
    @Transient
1617
    public List<Synonym> getSynonymsInGroup(HomotypicalGroup homotypicGroup){
1618
        List<Synonym> result = new ArrayList<Synonym>();
1619

    
1620
        for (TaxonNameBase<?, ?>name : homotypicGroup.getTypifiedNames()){
1621
            for (Synonym synonym : name.getSynonyms()){
1622
                for(SynonymRelationship synRel : synonym.getSynonymRelations()){
1623
                    if (synRel.getAcceptedTaxon().equals(this)){
1624
                        result.add(synRel.getSynonym());
1625
                    }
1626
                }
1627
            }
1628
        }
1629
        Collections.sort(result, new TaxonComparator());
1630
        return result;
1631
    }
1632

    
1633

    
1634
    /**
1635
     * Returns the image gallery description. If no image gallery exists, a new one is created using the
1636
     * defined title and adds the string "-Image Gallery" to the title.</BR>
1637
     * If multiple image galleries exist an arbitrary one is choosen.
1638
     * @param title
1639
     * @return
1640
     */
1641
    @Transient
1642
    public TaxonDescription getOrCreateImageGallery(String title){
1643
        return getOrCreateImageGallery(title, true, false);
1644
    }
1645

    
1646
    /**
1647
     * Returns the image gallery description. If no image gallery exists, a new one is created using the
1648
     * defined title.</BR>
1649
     * If onlyTitle == true we look only for an image gallery with this title, create a new one otherwise.
1650
     * If multiple image galleries exist that match the conditions an arbitrary one is choosen.
1651
     * @param title
1652
     * @param onlyTitle
1653
     * @param if true, the String "Image Gallery
1654
     * @return
1655
     */
1656
    @Transient
1657
    public TaxonDescription getOrCreateImageGallery(String title, boolean addImageGalleryToTitle, boolean onlyTitle){
1658
        TaxonDescription result = null;
1659
        String titleCache = (title == null) ? "Image Gallery" : title;
1660
        if (title != null && addImageGalleryToTitle){
1661
            titleCache = titleCache+ "-Image Gallery";
1662
        }
1663
        Set<TaxonDescription> descriptionSet = this.getDescriptions();
1664
        for (TaxonDescription desc: descriptionSet){
1665
            if (desc.isImageGallery()){
1666
                if (onlyTitle && ! titleCache.equals(desc.getTitleCache())){
1667
                    continue;
1668
                }
1669
                result = desc;
1670
                if (onlyTitle && titleCache.equals(desc.getTitleCache())){
1671
                    break;
1672
                }
1673
            }
1674
        }
1675
        if (result == null){
1676
            result = TaxonDescription.NewInstance();
1677
            result.setTitleCache(titleCache, true);
1678
            this.addDescription(result);
1679
            result.setImageGallery(true);
1680
        }
1681
        return result;
1682
    }
1683
    //*********************** CLONE ********************************************************/
1684

    
1685

    
1686
    /**
1687
     * Clones <i>this</i> taxon. This is a shortcut that enables to create
1688
     * a new instance that differs only slightly from <i>this</i> taxon by
1689
     * modifying only some of the attributes.<BR><BR>
1690
     * The TaxonNodes are not cloned, the list is empty.<BR>
1691
     * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1692
     * The taxon relationships and synonym relationships are cloned <BR>
1693
     *
1694
     * @see eu.etaxonomy.cdm.model.taxon.TaxonBase#clone()
1695
     * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1696
     * @see java.lang.Object#clone()
1697
     */
1698
    @Override
1699
    public Object clone() {
1700
        Taxon result;
1701
        result = (Taxon)super.clone();
1702

    
1703
        result.setRelationsFromThisTaxon(new HashSet<TaxonRelationship>());
1704

    
1705
        for (TaxonRelationship fromRelationship : this.getRelationsFromThisTaxon()){
1706
            TaxonRelationship newRelationship = (TaxonRelationship)fromRelationship.clone();
1707
            newRelationship.setRelatedFrom(result);
1708
            result.relationsFromThisTaxon.add(newRelationship);
1709
        }
1710

    
1711
        result.setRelationsToThisTaxon(new HashSet<TaxonRelationship>());
1712
        for (TaxonRelationship toRelationship : this.getRelationsToThisTaxon()){
1713
            TaxonRelationship newRelationship = (TaxonRelationship)toRelationship.clone();
1714
            newRelationship.setRelatedTo(result);
1715
            result.relationsToThisTaxon.add(newRelationship);
1716
        }
1717

    
1718

    
1719
        result.synonymRelations = new HashSet<SynonymRelationship>();
1720
        for (SynonymRelationship synRelationship : this.getSynonymRelations()){
1721
            SynonymRelationship newRelationship = (SynonymRelationship)synRelationship.clone();
1722
            newRelationship.setRelatedTo(result);
1723
            result.synonymRelations.add(newRelationship);
1724
        }
1725

    
1726

    
1727
        result.taxonNodes = new HashSet<TaxonNode>();
1728

    
1729
        /*for (TaxonNode taxonNode : this.getTaxonNodes()){
1730
            TaxonNode newTaxonNode = (TaxonNode)taxonNode.clone();
1731
            newTaxonNode.setTaxon(result);
1732
            result.addTaxonNode(newTaxonNode);
1733
        }*/
1734

    
1735
        return result;
1736

    
1737
    }
1738
}
(9-9/18)