Project

General

Profile

Download (86 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.Iterator;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Set;
22

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

    
40
import org.apache.log4j.Logger;
41
import org.hibernate.annotations.Cascade;
42
import org.hibernate.annotations.CascadeType;
43
import org.hibernate.envers.Audited;
44
import org.hibernate.search.annotations.ClassBridge;
45
import org.hibernate.search.annotations.ClassBridges;
46
import org.hibernate.search.annotations.ContainedIn;
47
import org.hibernate.search.annotations.Indexed;
48
import org.hibernate.search.annotations.IndexedEmbedded;
49
import org.springframework.beans.factory.annotation.Configurable;
50
import org.springframework.util.ReflectionUtils;
51

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

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

    
100
    private static final long serialVersionUID = -584946869762749006L;
101
    private static final Logger logger = Logger.getLogger(Taxon.class);
102

    
103
    @XmlElementWrapper(name = "Descriptions")
104
    @XmlElement(name = "Description")
105
    @OneToMany(mappedBy="taxon", fetch= FetchType.LAZY)
106
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
107
    @NotNull
108
    @ContainedIn
109
    private Set<TaxonDescription> descriptions = new HashSet<TaxonDescription>();
110

    
111
    // all related synonyms
112
    @XmlElementWrapper(name = "SynonymRelations")
113
    @XmlElement(name = "SynonymRelationship")
114
    @OneToMany(mappedBy="relatedTo", fetch=FetchType.LAZY, orphanRemoval=true)
115
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
116
    @NotNull
117
    @Valid
118
    private Set<SynonymRelationship> synonymRelations = new HashSet<SynonymRelationship>();
119

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

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

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

    
143
    @XmlAttribute(name= "unplaced")
144
    private boolean unplaced = false;
145

    
146
    @XmlAttribute(name= "excluded")
147
    private boolean excluded = false;
148

    
149
    // shortcut to the taxonomicIncluded (parent) taxon. Managed by the taxonRelations setter
150
    @XmlElement(name = "TaxonomicParentCache")
151
    @XmlIDREF
152
    @XmlSchemaType(name = "IDREF")
153
    @ManyToOne(fetch = FetchType.LAZY)
154
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
155
    private Taxon taxonomicParentCache;
156

    
157

    
158
    @XmlElementWrapper(name = "taxonNodes")
159
    @XmlElement(name = "taxonNode")
160
    @XmlIDREF
161
    @XmlSchemaType(name = "IDREF")
162
    @OneToMany(mappedBy="taxon", fetch=FetchType.LAZY)
163
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
164
    @IndexedEmbedded
165
    private Set<TaxonNode> taxonNodes = new HashSet<TaxonNode>();
166

    
167
    //cached number of taxonomic children
168
    @XmlElement(name = "TaxonomicChildrenCount")
169
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
170
    private int taxonomicChildrenCount;
171

    
172
// ************************* FACTORY METHODS ********************************/
173

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

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

    
204
    //TODO should be private, but still produces Spring init errors
205
    @Deprecated
206
    public Taxon(){
207
        this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Taxon>();
208
    }
209

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

    
224
//********* METHODS **************************************/
225

    
226

    
227

    
228
    /**
229
     * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon descriptions}
230
     * concerning <i>this</i> taxon.
231
     *
232
     * @see #removeDescription(TaxonDescription)
233
     * @see #addDescription(TaxonDescription)
234
     * @see eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
235
     */
236
    @Override
237
    public Set<TaxonDescription> getDescriptions() {
238
        if(descriptions == null) {
239
            descriptions = new HashSet<TaxonDescription>();
240
        }
241
        return descriptions;
242
    }
243

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

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

    
288

    
289
    public void removeDescription(TaxonDescription description, boolean removeElements){
290
    	if (removeElements){
291
    		Set<DescriptionElementBase> elements = new HashSet<DescriptionElementBase>(description.getElements());
292
            for (DescriptionElementBase el:elements){
293
            	description.getElements().remove(el);
294
            }
295
            removeDescription(description);
296
    	} else{
297
    		removeDescription(description);
298
    	}
299
    }
300

    
301
    /**
302
     * Returns the image gallery for a taxon. If there are multiple taxon descriptions
303
     * marked as image galleries an arbitrary one is chosen.
304
     * If no image gallery exists, a new one is created if <code>createNewIfNotExists</code>
305
     * is <code>true</code>.
306
     * @param createNewIfNotExists
307
     * @return
308
     */
309
    public TaxonDescription getImageGallery(boolean createNewIfNotExists) {
310
        TaxonDescription result = null;
311
        Set<TaxonDescription> descriptions= getDescriptions();
312
        for (TaxonDescription description : descriptions){
313
            if (description.isImageGallery()){
314
                result = description;
315
                break;
316
            }
317
        }
318
        if (result == null && createNewIfNotExists){
319
            result = TaxonDescription.NewInstance(this);
320
            result.setImageGallery(true);
321
        }
322
        return result;
323
    }
324

    
325

    
326

    
327
    public Set<TaxonNode> getTaxonNodes() {
328
        return taxonNodes;
329
    }
330
    //	protected void setTaxonNodes(Set<TaxonNode> taxonNodes) {
331
//		this.taxonNodes = taxonNodes;
332
//	}
333
    protected void addTaxonNode(TaxonNode taxonNode){
334
        taxonNodes.add(taxonNode);
335
    }
336

    
337
    public boolean removeTaxonNode(TaxonNode taxonNode){
338
        if (!taxonNodes.contains(taxonNode)){
339
            return false;
340
        }
341
        TaxonNode parent = taxonNode.getParent();
342
        if (parent != null){
343
            parent.removeChildNode(taxonNode);
344
        }
345
        taxonNode.setTaxon(null);
346
        return taxonNodes.remove(taxonNode);
347

    
348
    }
349

    
350
    public boolean removeTaxonNode(TaxonNode taxonNode, boolean deleteChildren){
351
        TaxonNode parent = taxonNode.getParent();
352
        boolean success = true;
353

    
354
        if ((!taxonNode.getChildNodes().isEmpty() && deleteChildren) || (taxonNode.getChildNodes().isEmpty()) ){
355

    
356
            taxonNode.delete();
357

    
358
        } else if (!taxonNode.isTopmostNode()){
359

    
360
            List<TaxonNode> nodes = new ArrayList<TaxonNode> (taxonNode.getChildNodes());
361
            for (TaxonNode childNode: nodes){
362
                taxonNode.getChildNodes().remove(childNode);
363
                parent.addChildNode(childNode, null, null);
364
            }
365

    
366
            taxonNode.delete();
367

    
368
        } else if (taxonNode.isTopmostNode()){
369
            success = false;
370
        }
371
        return success;
372
    }
373

    
374
    public boolean removeTaxonNodes(boolean deleteChildren){
375
        Iterator<TaxonNode> nodesIterator = taxonNodes.iterator();
376
        TaxonNode node;
377
        TaxonNode parent;
378
        boolean success = false;
379
        List<TaxonNode> removeNodes = new ArrayList<TaxonNode>();
380
        while (nodesIterator.hasNext()){
381
            node = nodesIterator.next();
382
            if (!deleteChildren){
383
                List<TaxonNode> children = node.getChildNodes();
384
                Iterator<TaxonNode> childrenIterator = children.iterator();
385
                parent = node.getParent();
386
                while (childrenIterator.hasNext()){
387
                    TaxonNode childNode = childrenIterator.next();
388
                    if (parent != null){
389
                        parent.addChildNode(childNode, null, null);
390
                    }else{
391
                        childNode.setParent(null);
392
                    }
393
                }
394

    
395
                for (int i = 0; i<node.getChildNodes().size(); i++){
396
                    node.removeChild(i);
397
                }
398

    
399

    
400
            }
401

    
402
            removeNodes.add(node);
403
         }
404
        for (int i = 0; i<removeNodes.size(); i++){
405
            TaxonNode removeNode = removeNodes.get(i);
406
            success = removeNode.delete(deleteChildren);
407
            removeNode.setTaxon(null);
408
            removeTaxonNode(removeNode);
409
        }
410
        return success;
411

    
412
    }
413

    
414

    
415

    
416

    
417
    /**
418
     * Returns the set of all {@link SynonymRelationship synonym relationships}
419
     * in which <i>this</i> ("accepted/correct") taxon is involved. <i>This</i> taxon can only
420
     * be the target of these synonym relationships.
421
     *
422
     * @see    #addSynonymRelation(SynonymRelationship)
423
     * @see    #removeSynonymRelation(SynonymRelationship)
424
     * @see    #getSynonyms()
425
     */
426
    public Set<SynonymRelationship> getSynonymRelations() {
427
        if(synonymRelations == null) {
428
            this.synonymRelations = new HashSet<SynonymRelationship>();
429
        }
430
        return synonymRelations;
431
    }
432

    
433
    /**
434
     * Adds an existing {@link SynonymRelationship synonym relationship} to the set of
435
     * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon. If
436
     * the target of the synonym relationship does not match with <i>this</i> taxon
437
     * no addition will be carried out.
438
     *
439
     * @param synonymRelation	the synonym relationship to be added to <i>this</i> taxon's
440
     * 							synonym relationships set
441
     * @see    	   				#getSynonymRelations()
442
     * @see    	   				#addSynonym(Synonym, SynonymRelationshipType)
443
     * @see    	   				#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
444
     * @see    	   				#addSynonymName(TaxonNameBase, SynonymRelationshipType)
445
     * @see    	   				#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
446
     */
447
    protected void addSynonymRelation(SynonymRelationship synonymRelation) {
448
        this.synonymRelations.add(synonymRelation);
449
    }
450
    /**
451
     * Removes one element from the set of {@link SynonymRelationship synonym relationships} assigned
452
     * to <i>this</i> (accepted/correct) taxon. Due to bidirectionality the given
453
     * synonym relationship will also be removed from the set of synonym
454
     * relationships assigned to the {@link Synonym#getSynonymRelations() synonym} involved in the
455
     * relationship. Furthermore the content of
456
     * the {@link SynonymRelationship#getAcceptedTaxon() accepted taxon} attribute and of the
457
     * {@link SynonymRelationship#getSynonym() synonym} attribute within the synonym relationship
458
     * itself will be set to "null".
459
     *
460
     * @param synonymRelation  	the synonym relationship which should be deleted
461
     * @param removeSynonymNameFromHomotypicalGroup
462
     * 				if <code>true</code> the synonym name will also be deleted from its homotypical group if the
463
     * 				group contains other names
464
     * @see    	#getSynonymRelations()
465
     * @see    	#addSynonymRelation(SynonymRelationship)
466
     * @see 	#removeSynonym(Synonym)
467
     */
468
    public void removeSynonymRelation(SynonymRelationship synonymRelation, boolean removeSynonymNameFromHomotypicalGroup) {
469
        synonymRelation.setAcceptedTaxon(null);
470
        Synonym synonym = synonymRelation.getSynonym();
471
        if (synonym != null){
472
            synonymRelation.setSynonym(null);
473
            synonym.removeSynonymRelation(synonymRelation);
474
            if(removeSynonymNameFromHomotypicalGroup){
475
                HomotypicalGroup synHG = synonym.getName().getHomotypicalGroup();
476
                if (synHG.getTypifiedNames().size() > 1){
477
                    synHG.removeTypifiedName(synonym.getName(), false);
478
                }
479
            }
480
        }
481
        this.synonymRelations.remove(synonymRelation);
482
    }
483

    
484
    /**
485
     * Like {@link Taxon#removeSynonymRelation(SynonymRelationship, boolean)} but synonym name
486
     * will be deleted from homotypical group by default
487
     *
488
     * @param synonymRelation   the synonym relationship which should be deleted
489
     *
490
     * @see					#removeSynonymRelation(SynonymRelationship, boolean)
491
     */
492
    public void removeSynonymRelation(SynonymRelationship synonymRelation){
493
        removeSynonymRelation(synonymRelation, true);
494
    }
495

    
496

    
497
    /**
498
     * Returns the set of all {@link TaxonRelationship taxon relationships}
499
     * between two taxa in which <i>this</i> taxon is involved as a source.
500
     *
501
     * @see    #getRelationsToThisTaxon()
502
     * @see    #getTaxonRelations()
503
     */
504
    public Set<TaxonRelationship> getRelationsFromThisTaxon() {
505
        if(relationsFromThisTaxon == null) {
506
            this.relationsFromThisTaxon = new HashSet<TaxonRelationship>();
507
        }
508
        return relationsFromThisTaxon;
509
    }
510

    
511

    
512
    /**
513
     * Returns the set of all {@link TaxonRelationship taxon relationships}
514
     * between two taxa in which <i>this</i> taxon is involved as a target.
515
     *
516
     * @see    #getRelationsFromThisTaxon()
517
     * @see    #getTaxonRelations()
518
     */
519
    public Set<TaxonRelationship> getRelationsToThisTaxon() {
520
        if(relationsToThisTaxon == null) {
521
            this.relationsToThisTaxon = new HashSet<TaxonRelationship>();
522
        }
523
        return relationsToThisTaxon;
524
    }
525
    /**
526
     * Returns the set of all {@link TaxonRelationship taxon relationships}
527
     * between two taxa in which <i>this</i> taxon is involved either as a source or
528
     * as a target.
529
     *
530
     * @see    #getRelationsFromThisTaxon()
531
     * @see    #getRelationsToThisTaxon()
532
     */
533
    @Transient
534
    public Set<TaxonRelationship> getTaxonRelations() {
535
        Set<TaxonRelationship> rels = new HashSet<TaxonRelationship>();
536
        rels.addAll(getRelationsToThisTaxon());
537
        rels.addAll(getRelationsFromThisTaxon());
538
        return rels;
539
    }
540

    
541
    /**
542
     * @see    #getRelationsToThisTaxon()
543
     */
544
    protected void setRelationsToThisTaxon(Set<TaxonRelationship> relationsToThisTaxon) {
545
        this.relationsToThisTaxon = relationsToThisTaxon;
546
    }
547

    
548
    /**
549
     * @see    #getRelationsFromThisTaxon()
550
     */
551
    protected void setRelationsFromThisTaxon(Set<TaxonRelationship> relationsFromThisTaxon) {
552
        this.relationsFromThisTaxon = relationsFromThisTaxon;
553
    }
554

    
555
    /**
556
     * If a relationships between <i>this</i> and the given taxon exists they will be returned.
557
     * <i>This</i> taxon is involved either as a source or as a target in the relationships.
558
     * The method will return <code>null</code> if no relations exist between the two taxa.
559
     *
560
     * @param possiblyRelatedTaxon
561
     * 			a taxon to check for a relationship
562
     * @return
563
     * 			a set of <code>TaxonRelationship</code>s or <code>null</null> if none exists.
564
     */
565
    public Set<TaxonRelationship> getTaxonRelations(Taxon possiblyRelatedTaxon){
566
        Set<TaxonRelationship> relations = new HashSet<TaxonRelationship>();
567

    
568
        for(TaxonRelationship relationship : getTaxonRelations()){
569
            if(relationship.getFromTaxon().equals(possiblyRelatedTaxon)) {
570
                relations.add(relationship);
571
            }
572
            if(relationship.getToTaxon().equals(possiblyRelatedTaxon)) {
573
                relations.add(relationship);
574
            }
575
        }
576

    
577
        return relations.size() > 0 ? relations : null;
578
    }
579

    
580
    /**
581
     * Removes one {@link TaxonRelationship taxon relationship} from one of both sets of
582
     * {@link #getTaxonRelations() taxon relationships} in which <i>this</i> taxon is involved
583
     * either as a {@link #getRelationsFromThisTaxon() source} or as a {@link #getRelationsToThisTaxon() target}.
584
     * The taxon relationship will also be removed from one of both sets
585
     * belonging to the second taxon involved. Furthermore the inherited RelatedFrom and
586
     * RelatedTo attributes of the given taxon relationship will be nullified.<P>
587
     * If the taxon relationship concerns the classification possible
588
     * modifications of the {@link #getTaxonomicParent() parent taxon} or of the number of
589
     * {@link #getTaxonomicChildrenCount() childrens} will be stored.
590
     *
591
     * @param  rel  the taxon relationship which should be removed from one
592
     * 				of both sets
593
     * @see    		#getTaxonRelations()
594
     * @see    	    #getTaxonomicParent()
595
     * @see    	    #getTaxonomicChildrenCount()
596
     * @see    		eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom()
597
     * @see    		eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo()
598
     *
599
     */
600
    public void removeTaxonRelation(TaxonRelationship rel) {
601
        this.relationsToThisTaxon.remove(rel);
602
        this.relationsFromThisTaxon.remove(rel);
603
        Taxon fromTaxon = rel.getFromTaxon();
604
        Taxon toTaxon = rel.getToTaxon();
605
        // check if this removes the taxonomical parent. If so, also remove shortcut to the higher taxon
606
        if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) ){
607
            if (fromTaxon != null && fromTaxon.equals(this)){
608
                this.taxonomicParentCache = null;
609
            }else if (toTaxon != null && toTaxon.equals(this)){
610
                this.setTaxonomicChildrenCount(computeTaxonomicChildrenCount());
611
            }
612
        }
613
        //delete Relationship from other related Taxon
614
        if (fromTaxon != this){
615
            rel.setToTaxon(null);  //remove this Taxon from relationship
616
            if (fromTaxon != null){
617
                if (fromTaxon.getTaxonRelations().contains(rel)){
618
                    fromTaxon.removeTaxonRelation(rel);
619
                }
620
            }
621
        }
622
        if (toTaxon != this ){
623
            rel.setFromTaxon(null); //remove this Taxon from relationship
624
           if (toTaxon != null){
625
               if (toTaxon.getTaxonRelations().contains(rel)) {
626
                   toTaxon.removeTaxonRelation(rel);
627
               }
628
           }
629
        }
630
    }
631

    
632
    /**
633
     * Adds an existing {@link TaxonRelationship taxon relationship} either to the set of
634
     * {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon} or to the set of
635
     * {@link #getRelationsFromThisTaxon() taxon relationships from <i>this</i> taxon}. If neither the
636
     * source nor the target of the taxon relationship match with <i>this</i> taxon
637
     * no addition will be carried out. The taxon relationship will also be
638
     * added to the second taxon involved in the given relationship.<P>
639
     * If the taxon relationship concerns the classification possible
640
     * modifications of the {@link #getTaxonomicParent() parent taxon} or of the number of
641
     * {@link #getTaxonomicChildrenCount() childrens} will be stored.
642
     *
643
     * @param rel  the taxon relationship to be added to one of <i>this</i> taxon's taxon relationships sets
644
     * @see    	   #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
645
     * @see    	   #getTaxonRelations()
646
     * @see    	   #getRelationsFromThisTaxon()
647
     * @see    	   #getRelationsToThisTaxon()
648
     * @see    	   #getTaxonomicParent()
649
     * @see    	   #getTaxonomicChildrenCount()
650
     */
651
    public void addTaxonRelation(TaxonRelationship rel) {
652
        if (rel!=null && rel.getType()!=null && !getTaxonRelations().contains(rel) ){
653
            Taxon toTaxon=rel.getToTaxon();
654
            Taxon fromTaxon=rel.getFromTaxon();
655
            if ( this.equals(toTaxon) || this.equals(fromTaxon) ){
656
                if (this.equals(fromTaxon)){
657
                    relationsFromThisTaxon.add(rel);
658
                    // also add relation to other taxon object
659
                    if (toTaxon!=null){
660
                        toTaxon.addTaxonRelation(rel);
661
                    }
662
                    // check if this sets the taxonomical parent. If so, remember a shortcut to this taxon
663
                    if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && toTaxon!=null ){
664
                        this.taxonomicParentCache = toTaxon;
665
                    }
666
                }else if (this.equals(toTaxon)){
667
                    relationsToThisTaxon.add(rel);
668
                    // also add relation to other taxon object
669
                    if (fromTaxon!=null){
670
                        fromTaxon.addTaxonRelation(rel);
671
                    }
672
                    if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && fromTaxon!=null ){
673
                        this.taxonomicChildrenCount++;
674
                    }
675

    
676
                }
677
            }else if (toTaxon == null || fromTaxon == null){
678
                if (toTaxon == null){
679
                    toTaxon = this;
680
                    relationsToThisTaxon.add(rel);
681
                    if (fromTaxon!= null){
682
                        fromTaxon.addTaxonRelation(rel);
683
                    }
684
                    if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && fromTaxon!=null ){
685
                        this.taxonomicChildrenCount++;
686
                    }
687
                }else if (fromTaxon == null && toTaxon != null){
688
                    fromTaxon = this;
689
                    relationsFromThisTaxon.add(rel);
690
                    if (toTaxon!=null){
691
                        toTaxon.addTaxonRelation(rel);
692
                    }
693
                    if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && toTaxon!=null ){
694
                        this.taxonomicParentCache = toTaxon;
695
                    }
696
                }
697
            }
698
        }
699
    }
700

    
701
    /* (non-Javadoc)
702
     * @see eu.etaxonomy.cdm.model.common.IRelated#addRelationship(eu.etaxonomy.cdm.model.common.RelationshipBase)
703
     */
704
    @Override
705
    @Deprecated //for inner use by RelationshipBase only
706
    public void addRelationship(RelationshipBase rel){
707
        if (rel instanceof TaxonRelationship){
708
            addTaxonRelation((TaxonRelationship)rel);
709
        }else if (rel instanceof SynonymRelationship){
710
            addSynonymRelation((SynonymRelationship)rel);
711
        }else{
712
            throw new ClassCastException("Wrong Relationsship type for Taxon.addRelationship");
713
        }
714
    }
715

    
716
    /**
717
     * Creates a new {@link TaxonRelationship taxon relationship} instance where <i>this</i> taxon
718
     * plays the source role and adds it to the set of
719
     * {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to <i>this</i> taxon.
720
     * The taxon relationship will also be added to the set of taxon
721
     * relationships to the second taxon involved in the created relationship.<P>
722
     * If the taxon relationship concerns the classification possible
723
     * modifications of the {@link #getTaxonomicParent() parent taxon} or of the number of
724
     * {@link #getTaxonomicChildrenCount() childrens} will be stored.
725
     *
726
     * @param toTaxon		the taxon which plays the target role in the new taxon relationship
727
     * @param type			the taxon relationship type for the new taxon relationship
728
     * @param citation		the reference source for the new taxon relationship
729
     * @param microcitation	the string with the details describing the exact localisation within the reference
730
     * @return
731
     * @see    	   			#addTaxonRelation(TaxonRelationship)
732
     * @see    	   			#getTaxonRelations()
733
     * @see    	   			#getRelationsFromThisTaxon()
734
     * @see    	   			#getRelationsToThisTaxon()
735
     * @see    	   			#getTaxonomicParent()
736
     * @see    	   			#getTaxonomicChildrenCount()
737
     */
738
    public TaxonRelationship addTaxonRelation(Taxon toTaxon, TaxonRelationshipType type, Reference citation, String microcitation) {
739
        return new TaxonRelationship(this, toTaxon, type, citation, microcitation);
740
    }
741
    /**
742
     * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
743
     * "misapplied name for") instance where <i>this</i> taxon plays the target role
744
     * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
745
     * The taxon relationship will also be added to the set of taxon
746
     * relationships to the other (misapplied name) taxon involved in the created relationship.
747
     *
748
     * @param misappliedNameTaxon	the taxon which plays the target role in the new taxon relationship
749
     * @param citation				the reference source for the new taxon relationship
750
     * @param microcitation			the string with the details describing the exact localisation within the reference
751
     * @return
752
     * @see    	   					#getMisappliedNames()
753
     * @see    	   					#addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
754
     * @see    	   					#addTaxonRelation(TaxonRelationship)
755
     * @see    	   					#getTaxonRelations()
756
     * @see    	   					#getRelationsFromThisTaxon()
757
     * @see    	   					#getRelationsToThisTaxon()
758
     */
759
    public TaxonRelationship addMisappliedName(Taxon misappliedNameTaxon, Reference citation, String microcitation) {
760
        return misappliedNameTaxon.addTaxonRelation(this, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), citation, microcitation);
761
    }
762

    
763
//	public void removeMisappliedName(Taxon misappliedNameTaxon){
764
//		Set<TaxonRelationship> taxRels = this.getTaxonRelations();
765
//		for (TaxonRelationship taxRel : taxRels ){
766
//			if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())
767
//				&& taxRel.getFromTaxon().equals(misappliedNameTaxon)){
768
//				this.removeTaxonRelation(taxRel);
769
//			}
770
//		}
771
//	}
772

    
773
    /**
774
     * TODO update documentation
775
     * Removes one {@link TaxonRelationship taxon relationship} with {@link TaxonRelationshipType taxon relationship type}
776
     * taxonRelType and with the given child taxon playing the
777
     * source role from the set of {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging
778
     * to <i>this</i> taxon. The taxon relationship will also be removed from the set
779
     * of {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to the other side taxon.
780
     * Furthermore, the inherited RelatedFrom and RelatedTo attributes of the
781
     * taxon relationship will be nullified.<P>
782
     *
783
     * @param taxon			the taxon which plays the source role in the taxon relationship
784
     * @param taxonRelType	the taxon relationship type
785
     */
786
    public void removeTaxon(Taxon taxon, TaxonRelationshipType taxonRelType){
787
        Set<TaxonRelationship> taxRels = this.getTaxonRelations();
788
        for (TaxonRelationship taxRel : taxRels ){
789
            if (taxRel.getType().equals(taxonRelType)
790
                && taxRel.getFromTaxon().equals(taxon)){
791
                this.removeTaxonRelation(taxRel);
792
            }
793
        }
794
    }
795

    
796
    /**
797
     * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
798
     * "taxonomically included in") instance where <i>this</i> taxon plays the target
799
     * role (parent) and adds it to the set of
800
     * {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging to <i>this</i> taxon.
801
     * The taxon relationship will also be added to the set of
802
     * {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to the second taxon
803
     * (child) involved in the created relationship.<P>
804
     * Since the taxon relationship concerns the modifications
805
     * of the number of {@link #getTaxonomicChildrenCount() childrens} for <i>this</i> taxon and
806
     * of the {@link #getTaxonomicParent() parent taxon} for the child taxon will be stored.
807
     * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
808
     * than the rank of the taxon name used as a child taxon.
809
     *
810
     * @param child			the taxon which plays the source role (child) in the new taxon relationship
811
     * @param citation		the reference source for the new taxon relationship
812
     * @param microcitation	the string with the details describing the exact localisation within the reference
813
     * @see    	   			#setTaxonomicParent(Taxon, Reference, String)
814
     * @see    	   			#addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
815
     * @see    	   			#addTaxonRelation(TaxonRelationship)
816
     * @see    	   			#getTaxonRelations()
817
     * @see    	   			#getRelationsFromThisTaxon()
818
     * @see    	   			#getRelationsToThisTaxon()
819
     * @see    	   			#getTaxonomicParent()
820
     * @see    	   			#getTaxonomicChildrenCount()
821
     */
822
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
823
    public void addTaxonomicChild(Taxon child, Reference citation, String microcitation){
824
        if (child == null){
825
            throw new NullPointerException("Child Taxon is 'null'");
826
        }else{
827
            child.setTaxonomicParent(this, citation, microcitation);
828
        }
829
    }
830

    
831
    /**
832
     * Removes one {@link TaxonRelationship taxon relationship} with {@link TaxonRelationshipType taxon relationship type}
833
     * "taxonomically included in" and with the given child taxon playing the
834
     * source role from the set of {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging
835
     * to <i>this</i> taxon. The taxon relationship will also be removed from the set
836
     * of {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to the child taxon.
837
     * Furthermore the inherited RelatedFrom and RelatedTo attributes of the
838
     * taxon relationship will be nullified.<P>
839
     * Since the taxon relationship concerns the classification modifications
840
     * of the number of {@link #getTaxonomicChildrenCount() childrens} for <i>this</i> taxon and
841
     * of the {@link #getTaxonomicParent() parent taxon} for the child taxon will be stored.
842
     *
843
     * @param  child	the taxon playing the source role in the relationship to be removed
844
     * @see    	    	#removeTaxonRelation(TaxonRelationship)
845
     * @see    			#getRelationsToThisTaxon()
846
     * @see    			#getRelationsFromThisTaxon()
847
     * @see    	    	#getTaxonomicParent()
848
     * @see    	    	#getTaxonomicChildrenCount()
849
     * @see    			eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom()
850
     * @see    			eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo()
851
     *
852
     */
853
    @Deprecated //will be removed in future versions. Use classification/TaxonNode instead
854
    public void removeTaxonomicChild(Taxon child){
855
        Set<TaxonRelationship> taxRels = this.getTaxonRelations();
856
        for (TaxonRelationship taxRel : taxRels ){
857
            if (taxRel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())
858
                && taxRel.getFromTaxon().equals(child)){
859
                this.removeTaxonRelation(taxRel);
860
            }
861
        }
862
    }
863

    
864
    /**
865
     * Returns the taxon which is the next higher taxon (parent) of <i>this</i> taxon
866
     * within the classification and which is stored in the
867
     * TaxonomicParentCache attribute. Each taxon can have only one parent taxon.
868
     * The child taxon and the parent taxon play the source respectively the
869
     * target role in one {@link TaxonRelationship taxon relationship} with
870
     * {@link TaxonRelationshipType taxon relationship type} "taxonomically included in".
871
     * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
872
     * than the rank of the taxon name used as a child taxon.
873
     *
874
     * @see  #setTaxonomicParent(Taxon, Reference, String)
875
     * @see  #getTaxonomicChildren()
876
     * @see  #getTaxonomicChildrenCount()
877
     * @see  #getRelationsFromThisTaxon()
878
     */
879
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
880
    public Taxon getTaxonomicParent() {
881
        return this.taxonomicParentCache;
882
    }
883

    
884
    /**
885
     * Sets the taxononomic parent of <i>this</i> taxon to null.
886
     * Note that this method does not handle taxonomic relationships.
887
     */
888
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
889
    public void nullifyTaxonomicParent() {
890
        this.taxonomicParentCache = null;
891
    }
892

    
893
    /**
894
     * Replaces both the taxonomic parent cache with the given new parent taxon
895
     * and the corresponding taxon relationship with a new {@link TaxonRelationship taxon relationship}
896
     * (with {@link TaxonRelationshipType taxon relationship type} "taxonomically included in") instance.
897
     * In the new taxon relationship <i>this</i> taxon plays the source role (child).
898
     * This method creates and adds the new taxon relationship to the set of
899
     * {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to <i>this</i> taxon.
900
     * The taxon relationship will also be added to the set of
901
     * {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging to the second taxon
902
     * (parent) involved in the new relationship.<P>
903
     * Since the taxon relationship concerns the classification modifications
904
     * of the {@link #getTaxonomicParent() parent taxon} for <i>this</i> taxon and of the number of
905
     * {@link #getTaxonomicChildrenCount() childrens} for the child taxon will be stored.
906
     *
907
     * @param newParent		the taxon which plays the target role (parent) in the new taxon relationship
908
     * @param citation		the reference source for the new taxon relationship
909
     * @param microcitation	the string with the details describing the exact localisation within the reference
910
     * @see    	   			#removeTaxonRelation(TaxonRelationship)
911
     * @see    	   			#getTaxonomicParent()
912
     * @see    	   			#addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
913
     * @see    	   			#addTaxonRelation(TaxonRelationship)
914
     * @see    	   			#getTaxonRelations()
915
     * @see    	   			#getRelationsFromThisTaxon()
916
     * @see    	   			#getRelationsToThisTaxon()
917
     * @see    	   			#getTaxonomicChildrenCount()
918
     */
919
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
920
    public void setTaxonomicParent(Taxon newParent, Reference citation, String microcitation){
921
        //remove previously existing parent relationship!!!
922
        Taxon oldParent = this.getTaxonomicParent();
923
        Set<TaxonRelationship> taxRels = this.getTaxonRelations();
924
        for (TaxonRelationship taxRel : taxRels ){
925
            if (taxRel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && taxRel.getToTaxon().equals(oldParent)){
926
                this.removeTaxonRelation(taxRel);
927
            }
928
        }
929
        //add new parent
930
        if (newParent != null){
931
            addTaxonRelation(newParent, TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN(),citation,microcitation);
932
        }
933
    }
934

    
935
    /**
936
     * Returns the set of taxa which have <i>this</i> taxon as next higher taxon
937
     * (parent) within the classification. Each taxon can have several child
938
     * taxa. The child taxon and the parent taxon play the source respectively
939
     * the target role in one {@link TaxonRelationship taxon relationship} with
940
     * {@link TaxonRelationshipType taxon relationship type} "taxonomically included in".
941
     * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
942
     * than the rank of the taxon name used as a child taxon.
943
     *
944
     * @see  #getTaxonomicParent()
945
     * @see  #addTaxonomicChild(Taxon, Reference, String)
946
     * @see  #getTaxonomicChildrenCount()
947
     * @see  #getRelationsToThisTaxon()
948
     */
949
    @Transient
950
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
951
    public Set<Taxon> getTaxonomicChildren() {
952
        Set<Taxon> taxa = new HashSet<Taxon>();
953
        Set<TaxonRelationship> rels = this.getRelationsToThisTaxon();
954
        for (TaxonRelationship rel: rels){
955
            TaxonRelationshipType tt = rel.getType();
956
            TaxonRelationshipType incl = TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN();
957
            if (tt.equals(incl)){
958
                taxa.add(rel.getFromTaxon());
959
            }
960
        }
961
        return taxa;
962
    }
963

    
964
    /**
965
     * Returns the number of taxa which have <i>this</i> taxon as next higher taxon
966
     * (parent) within the classification and the number of which is stored in
967
     * the TaxonomicChildrenCount attribute. Each taxon can have several child
968
     * taxa. The child taxon and the parent taxon play the source respectively
969
     * the target role in one {@link TaxonRelationship taxon relationship} with
970
     * {@link TaxonRelationshipType taxon relationship type} "taxonomically included in".
971
     * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
972
     * than the rank of the taxon name used as a child taxon.
973
     *
974
     * @see  #getTaxonomicChildren()
975
     * @see  #getRelationsToThisTaxon()
976
     */
977
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
978
    public int getTaxonomicChildrenCount(){
979
        return taxonomicChildrenCount;
980
    }
981

    
982
    /**
983
     * @see  #getTaxonomicChildrenCount()
984
     */
985
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
986
    public void setTaxonomicChildrenCount(int taxonomicChildrenCount) {
987
        this.taxonomicChildrenCount = taxonomicChildrenCount;
988
    }
989

    
990
    /**
991
     * Returns the boolean value indicating whether <i>this</i> taxon has at least one
992
     * taxonomic child taxon within the classification (true) or not (false).
993
     *
994
     * @see  #getTaxonomicChildrenCount()
995
     * @see  #getTaxonomicChildren()
996
     */
997
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
998
    public boolean hasTaxonomicChildren(){
999
        return this.taxonomicChildrenCount > 0;
1000
    }
1001

    
1002
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
1003
    private int computeTaxonomicChildrenCount(){
1004
        int count = 0;
1005
        for (TaxonRelationship rel: this.getRelationsToThisTaxon()){
1006
            if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())){
1007
                count++;
1008
            }
1009
        }
1010
        return count;
1011
    }
1012

    
1013

    
1014
    /**
1015
     * Returns the boolean value indicating whether <i>this</i> taxon is a misaplication
1016
     * (misapplied name) for at least one other taxon.
1017
     */
1018
    // TODO cache as for #hasTaxonomicChildren
1019
    @Transient
1020
    public boolean isMisapplication(){
1021
        return computeMisapliedNameRelations() > 0;
1022
    }
1023

    
1024
    /**
1025
     * Counts the number of misaplied names relationships where this taxon represents the
1026
     * misaplied name for another taxon.
1027
     * @return
1028
     */
1029
    private int computeMisapliedNameRelations(){
1030
        int count = 0;
1031
        for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
1032
            if (rel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
1033
                count++;
1034
            }
1035
        }
1036
        return count;
1037
    }
1038

    
1039
    /**
1040
     * Returns the boolean value indicating whether <i>this</i> taxon is a related
1041
     * concept for at least one other taxon.
1042
     */
1043
    @Transient
1044
    public boolean isRelatedConcept(){
1045
        return computeConceptRelations() > 0;
1046
    }
1047

    
1048
    /**
1049
     * Counts the number of concept relationships where this taxon represents the
1050
     * related concept for another taxon.
1051
     * @return
1052
     */
1053
    private int computeConceptRelations(){
1054
        int count = 0;
1055
        for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
1056
            TaxonRelationshipType type = rel.getType();
1057
            if (type.isConceptRelationship()){
1058
                count++;
1059
            }
1060
        }
1061
        return count;
1062
    }
1063

    
1064
    /**
1065
     * Returns the boolean value indicating whether <i>this</i> taxon has at least one
1066
     * {@link Synonym synonym} (true) or not (false). If true the {@link #getSynonymRelations() set of synonym relationships}
1067
     * belonging to <i>this</i> ("accepted/correct") taxon is not empty .
1068
     *
1069
     * @see  #getSynonymRelations()
1070
     * @see  #getSynonyms()
1071
     * @see  #getSynonymNames()
1072
     * @see  #removeSynonym(Synonym)
1073
     * @see  SynonymRelationship
1074
     */
1075
    public boolean hasSynonyms(){
1076
        return this.getSynonymRelations().size() > 0;
1077
    }
1078

    
1079

    
1080
    /**
1081
     * Returns the boolean value indicating whether <i>this</i> taxon is at least
1082
     * involved in one {@link #getTaxonRelations() taxon relationship} between
1083
     * two taxa (true), either as a source or as a target, or not (false).
1084
     *
1085
     * @see  #getTaxonRelations()
1086
     * @see  #getRelationsToThisTaxon()
1087
     * @see  #getRelationsFromThisTaxon()
1088
     * @see  #removeTaxonRelation(TaxonRelationship)
1089
     * @see  TaxonRelationship
1090
     */
1091
    public boolean hasTaxonRelationships(){
1092
        return this.getTaxonRelations().size() > 0;
1093
    }
1094

    
1095
    /*
1096
     * MISAPPLIED NAMES
1097
     */
1098
    /**
1099
     * Returns the set of taxa playing the source role in {@link TaxonRelationship taxon relationships}
1100
     * (with {@link TaxonRelationshipType taxon relationship type} "misapplied name for") where
1101
     * <i>this</i> taxon plays the target role. A misapplied name is a taxon the
1102
     * {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} of which has been erroneously used
1103
     * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
1104
     * as the one meant by <i>this</i> ("accepted/correct") taxon.
1105
     *
1106
     * @see  #getTaxonRelations()
1107
     * @see  #getRelationsToThisTaxon()
1108
     * @see  #addMisappliedName(Taxon, Reference, String)
1109
     */
1110
    @Transient
1111
    public Set<Taxon> getMisappliedNames(){
1112
        Set<Taxon> taxa = new HashSet<Taxon>();
1113
        Set<TaxonRelationship> rels = this.getRelationsToThisTaxon();
1114
        for (TaxonRelationship rel: rels){
1115
            TaxonRelationshipType tt = rel.getType();
1116
            TaxonRelationshipType incl = TaxonRelationshipType.MISAPPLIED_NAME_FOR();
1117
            if (tt.equals(incl)){
1118
                taxa.add(rel.getFromTaxon());
1119
            }
1120
        }
1121
        return taxa;
1122
    }
1123
    /**
1124
     * Returns the set of taxa playing the target role in {@link TaxonRelationship taxon relationships}
1125
     * (with {@link TaxonRelationshipType taxon relationship type} "misapplied name for") where
1126
     * <i>this</i> taxon plays the source role. A misapplied name is a taxon the
1127
     * {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} of which has been erroneously used
1128
     * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
1129
     * as the one meant by <i>this</i> ("accepted/correct") taxon.
1130
     *
1131
     * @see  #getTaxonRelations()
1132
     * @see  #getRelationsToThisTaxon()
1133
     * @see  #addMisappliedName(Taxon, Reference, String)
1134
     */
1135
    @Transient
1136
    public Set<Taxon> getTaxonForMisappliedName(){
1137
        Set<Taxon> taxa = new HashSet<Taxon>();
1138
        Set<TaxonRelationship> rels = this.getRelationsFromThisTaxon();
1139
        for (TaxonRelationship rel: rels){
1140
            TaxonRelationshipType tt = rel.getType();
1141
            TaxonRelationshipType incl = TaxonRelationshipType.MISAPPLIED_NAME_FOR();
1142
            if (tt.equals(incl)){
1143
                taxa.add(rel.getToTaxon());
1144
            }
1145
        }
1146
        return taxa;
1147
    }
1148

    
1149

    
1150
    /*
1151
     * DEALING WITH SYNONYMS
1152
     */
1153
    /**
1154
     * Returns the set of all {@link Synonym synonyms} of <i>this</i> ("accepted/correct") taxon.
1155
     * Each synonym is the source and <i>this</i> taxon is the target of a {@link SynonymRelationship synonym relationship}
1156
     * belonging to the {@link #getSynonymRelations() set of synonym relationships} assigned to <i>this</i> taxon.
1157
     * For a particular synonym and for a particular ("accepted/correct") taxon
1158
     * there can be several synonym relationships (if two or more
1159
     * {@link SynonymRelationshipType synonym relationship types} - for instance
1160
     * "pro parte synonym of" and "is homotypic synonym of" - must be combined).
1161
     *
1162
     * @see    #getSynonymsSortedByType()
1163
     * @see    #getSynonymNames()
1164
     * @see    #getSynonymRelations()
1165
     * @see    #addSynonym(Synonym, SynonymRelationshipType)
1166
     * @see    #addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1167
     * @see    #removeSynonymRelation(SynonymRelationship)
1168
     * @see    #removeSynonym(Synonym)
1169
     */
1170
    @Transient
1171
    public Set<Synonym> getSynonyms(){
1172
        Set<Synonym> syns = new HashSet<Synonym>();
1173
        for (SynonymRelationship rel: this.getSynonymRelations()){
1174
            syns.add(rel.getSynonym());
1175
        }
1176
        return syns;
1177
    }
1178
    /**
1179
     * Returns the set of all {@link Synonym synonyms} of <i>this</i> ("accepted/correct") taxon
1180
     * sorted by the different {@link SynonymRelationshipType categories of synonym relationships}.
1181
     * Each synonym is the source and <i>this</i> taxon is the target of a {@link SynonymRelationship synonym relationship}
1182
     * belonging to the {@link #getSynonymRelations() set of synonym relationships} assigned to <i>this</i> taxon.
1183
     *
1184
     * @see    #getSynonyms()
1185
     * @see    #getSynonymNames()
1186
     * @see    #getSynonymRelations()
1187
     * @see    #addSynonym(Synonym, SynonymRelationshipType)
1188
     * @see    #addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1189
     * @see    #removeSynonymRelation(SynonymRelationship)
1190
     * @see    #removeSynonym(Synonym)
1191
     */
1192
    @Transient
1193
    public Set<Synonym> getSynonymsSortedByType(){
1194
        // FIXME: need to sort synonyms according to type!!!
1195
        logger.warn("getSynonymsSortedByType() not yet implemented");
1196
        return getSynonyms();
1197
    }
1198
    /**
1199
     * Returns the set of all {@link name.TaxonNameBase taxon names} used as {@link Synonym synonyms}
1200
     * of <i>this</i> ("accepted/correct") taxon. Each synonym is the source and
1201
     * <i>this</i> taxon is the target of a {@link SynonymRelationship synonym relationship} belonging
1202
     * to the {@link #getSynonymRelations() set of synonym relationships} assigned to <i>this</i> taxon.
1203
     *
1204
     * @see    #getSynonyms()
1205
     * @see    #getSynonymsSortedByType()
1206
     * @see    #getSynonymRelations()
1207
     * @see    #addSynonymName(TaxonNameBase, SynonymRelationshipType)
1208
     * @see    #addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1209
     * @see    #removeSynonymRelation(SynonymRelationship)
1210
     * @see    #removeSynonym(Synonym)
1211
     */
1212
    @Transient
1213
    public Set<TaxonNameBase> getSynonymNames(){
1214
        Set<TaxonNameBase> names = new HashSet<TaxonNameBase>();
1215
        for (SynonymRelationship rel: this.getSynonymRelations()){
1216
            names.add(rel.getSynonym().getName());
1217
        }
1218
        return names;
1219
    }
1220
    /**
1221
     * Creates a new {@link SynonymRelationship synonym relationship} (with the given {@link Synonym synonym}
1222
     * and with the given {@link SynonymRelationshipType synonym relationship type}), returns it and adds it
1223
     * to the set of {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon.
1224
     * The new synonym relationship will also be added to the set of
1225
     * {@link Synonym#getSynonymRelations() synonym relationships} belonging to the synonym
1226
     * involved in this synonym relationship.<BR>
1227
     * The returned synonym relationship allows to add further information to it.
1228
     *
1229
     * @param synonym		the synonym involved in the relationship to be created
1230
     * 						and added to <i>this</i> taxon's synonym relationships set
1231
     * @param synonymType	the synonym relationship category of the synonym
1232
     * 						relationship to be added
1233
     * @return 				the created synonym relationship
1234
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1235
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1236
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1237
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1238
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1239
     * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1240
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1241
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1242
     * @see    	   			#getSynonymRelations()
1243
     * @see    				#removeSynonym(Synonym)
1244
     * @see    	   			Synonym#getSynonymRelations()
1245
     */
1246
    public SynonymRelationship addSynonym(Synonym synonym, SynonymRelationshipType synonymType){
1247
        return addSynonym(synonym, synonymType, null, null);
1248
    }
1249
    /**
1250
     * Creates a new {@link SynonymRelationship synonym relationship} (with the given {@link Synonym synonym},
1251
     * with the given {@link SynonymRelationshipType synonym relationship type} and with the
1252
     * {@link eu.etaxonomy.cdm.model.reference.Reference reference source} on which the relationship assertion is based),
1253
     * returns it and adds it to the set of {@link #getSynonymRelations() synonym relationships}
1254
     * assigned to <i>this</i> taxon. The new synonym relationship will also be
1255
     * added to the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging to the synonym
1256
     * involved in this synonym relationship.<BR>
1257
     * The returned synonym relationship allows to add further information to it.
1258
     *
1259
     * @param synonym		the synonym involved in the relationship to be created
1260
     * 						and added to <i>this</i> taxon's synonym relationships set
1261
     * @param synonymType	the synonym relationship category of the synonym
1262
     * 						relationship to be added
1263
     * @param citation		the reference source for the new synonym relationship
1264
     * @param microcitation	the string with the details describing the exact localisation within the reference
1265
     * @return 				the created synonym relationship
1266
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1267
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1268
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1269
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1270
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1271
     * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1272
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1273
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1274
     * @see    	   			#getSynonymRelations()
1275
     * @see    				#removeSynonym(Synonym)
1276
     * @see    	   			Synonym#getSynonymRelations()
1277
     */
1278
    public SynonymRelationship addSynonym(Synonym synonym, SynonymRelationshipType synonymType, Reference citation, String citationMicroReference){
1279
        SynonymRelationship synonymRelationship = new SynonymRelationship(synonym, this, synonymType, citation, citationMicroReference);
1280
        return synonymRelationship;
1281
    }
1282

    
1283
    /**
1284
     * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1285
     * a new {@link SynonymRelationship synonym relationship} (with the new synonym and with the given
1286
     * {@link SynonymRelationshipType synonym relationship type}), returns the relationship and adds it
1287
     * to the set of {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon.
1288
     * The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1289
     * as <i>this</i> taxon. The new synonym relationship will also be added to
1290
     * the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1291
     * to the created synonym.<BR>
1292
     * The returned synonym relationship allows to add further information to it.
1293
     *
1294
     * @param synonymName	the taxon name to be used as a synonym to be added
1295
     * 						to <i>this</i> taxon's set of synonyms
1296
     * @param synonymType	the synonym relationship category of the synonym
1297
     * 						relationship to be added
1298
     * @return 				the created synonym relationship
1299
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1300
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1301
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1302
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1303
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1304
     * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1305
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1306
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1307
     * @see    	   			#getSynonymRelations()
1308
     * @see    				#removeSynonym(Synonym)
1309
     * @see    	   			Synonym#getSynonymRelations()
1310
     */
1311
    public SynonymRelationship addSynonymName(TaxonNameBase synonymName, SynonymRelationshipType synonymType){
1312
        return addSynonymName(synonymName, synonymType, null, null);
1313
    }
1314
    /**
1315
     * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1316
     * a new {@link SynonymRelationship synonym relationship} (with the new synonym, with the given
1317
     * {@link SynonymRelationshipType synonym relationship type} and with the {@link eu.etaxonomy.cdm.model.reference.Reference reference source}
1318
     * on which the relationship assertion is based), returns the relationship
1319
     * and adds it to the set of {@link #getSynonymRelations() synonym relationships} assigned
1320
     * to <i>this</i> taxon. The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1321
     * as <i>this</i> taxon. The new synonym relationship will also be added to
1322
     * the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1323
     * to the created synonym.<BR>
1324
     * The returned synonym relationship allows to add further information to it.
1325
     *
1326
     * @param synonymName	the taxon name to be used as a synonym to be added
1327
     * 						to <i>this</i> taxon's set of synonyms
1328
     * @param synonymType	the synonym relationship category of the synonym
1329
     * 						relationship to be added
1330
     * @param citation		the reference source for the new synonym relationship
1331
     * @param microcitation	the string with the details describing the exact localisation within the reference
1332
     * @return 				the created synonym relationship
1333
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1334
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1335
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1336
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1337
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1338
     * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1339
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1340
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1341
     * @see    	   			#getSynonymRelations()
1342
     * @see    				#removeSynonym(Synonym)
1343
     * @see    	   			Synonym#getSynonymRelations()
1344
     */
1345
    public SynonymRelationship addSynonymName(TaxonNameBase synonymName, SynonymRelationshipType synonymType, Reference citation, String citationMicroReference){
1346
        Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
1347
        return addSynonym(synonym, synonymType, citation, citationMicroReference);
1348
    }
1349

    
1350
    /**
1351
     * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1352
     * a new {@link SynonymRelationship synonym relationship} (with the new synonym and with the
1353
     * {@link SynonymRelationshipType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of" relationship type}),
1354
     * returns the relationship and adds it to the set of
1355
     * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon.
1356
     * The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1357
     * as <i>this</i> taxon. The new synonym relationship will also be added to
1358
     * the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1359
     * to the created synonym.<BR>
1360
     * The returned synonym relationship allows to add further information to it.
1361
     *
1362
     * @param synonymName	the taxon name to be used as an heterotypic synonym
1363
     * 						to be added to <i>this</i> taxon's set of synonyms
1364
     * @return 				the created synonym relationship
1365
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1366
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1367
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1368
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1369
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1370
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1371
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1372
     * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1373
     * @see    	   			#getSynonymRelations()
1374
     * @see    				#removeSynonym(Synonym)
1375
     * @see    	   			Synonym#getSynonymRelations()
1376
     */
1377
    public SynonymRelationship addHeterotypicSynonymName(TaxonNameBase synonymName){
1378
        return addHeterotypicSynonymName(synonymName, null, null, null);
1379
    }
1380

    
1381
    /**
1382
     * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1383
     * a new {@link SynonymRelationship synonym relationship} (with the new synonym, with the
1384
     * {@link SynonymRelationshipType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of" relationship type}
1385
     * and with the {@link eu.etaxonomy.cdm.model.reference.Reference reference source}
1386
     * on which the relationship assertion is based), returns the relationship
1387
     * and adds it to the set of {@link #getSynonymRelations() synonym relationships} assigned
1388
     * to <i>this</i> taxon. The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1389
     * as <i>this</i> taxon. Furthermore the new synonym relationship will be
1390
     * added to the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1391
     * to the created synonym and the taxon name used as synonym will be added
1392
     * to the given {@link name.HomotypicalGroup homotypical group}.<BR>
1393
     * The returned synonym relationship allows to add further information to it.
1394
     *
1395
     * @param synonymName		the taxon name to be used as an heterotypic synonym
1396
     * 							to be added to <i>this</i> taxon's set of synonyms
1397
     * @param homotypicalGroup	the homotypical group to which the taxon name
1398
     * 							of the synonym will be added
1399
     * @param citation			the reference source for the new synonym relationship
1400
     * @param microcitation		the string with the details describing the exact localisation
1401
     * 							within the reference
1402
     * @return 					the created synonym relationship
1403
     * @see    	   				#addHeterotypicSynonymName(TaxonNameBase)
1404
     * @see    	   				#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1405
     * @see    	   				#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1406
     * @see    	   				#addSynonym(Synonym, SynonymRelationshipType)
1407
     * @see    	   				#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1408
     * @see    	   				#addSynonymRelation(SynonymRelationship)
1409
     * @see    	   				#addHomotypicSynonym(Synonym, Reference, String)
1410
     * @see    	   				#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1411
     * @see    	   				#getSynonymRelations()
1412
     * @see    					#removeSynonym(Synonym)
1413
     * @see    	   				Synonym#getSynonymRelations()
1414
     */
1415
    public SynonymRelationship addHeterotypicSynonymName(TaxonNameBase synonymName, HomotypicalGroup homotypicalGroup, Reference citation, String microCitation){
1416
        Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
1417
        if (homotypicalGroup != null){
1418
            homotypicalGroup.addTypifiedName(synonymName);
1419
        }
1420
        return addSynonym(synonym, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), citation, microCitation);
1421
    }
1422

    
1423
    /**
1424
     * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1425
     * a new {@link SynonymRelationship synonym relationship} (with the new synonym, with the
1426
     * {@link SynonymRelationshipType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of" relationship type})
1427
     * and with the {@link eu.etaxonomy.cdm.model.reference.Reference reference source}
1428
     * on which the relationship assertion is based), returns the relationship
1429
     * and adds it to the set of {@link #getSynonymRelations() synonym relationships} assigned
1430
     * to <i>this</i> taxon. The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1431
     * as <i>this</i> taxon. Furthermore the new synonym relationship will be
1432
     * added to the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1433
     * to the created synonym and the taxon name used as synonym will be added
1434
     * to the same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group} to which the taxon name
1435
     * of <i>this</i> taxon belongs.<BR>
1436
     * The returned synonym relationship allows to add further information to it.
1437
     *
1438
     * @param synonymName	the taxon name to be used as an homotypic synonym
1439
     * 						to be added to <i>this</i> taxon's set of synonyms
1440
     * @param citation		the reference source for the new synonym relationship
1441
     * @param microcitation	the string with the details describing the exact localisation
1442
     * 						within the reference
1443
     * @return 				the created synonym relationship
1444
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1445
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1446
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1447
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1448
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1449
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1450
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1451
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1452
     * @see    	   			#getSynonymRelations()
1453
     * @see    				#removeSynonym(Synonym)
1454
     * @see    	   			Synonym#getSynonymRelations()
1455
     */
1456
    public SynonymRelationship addHomotypicSynonymName(TaxonNameBase synonymName, Reference citation, String microCitation){
1457
        Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
1458
        return addHomotypicSynonym(synonym, citation, microCitation);
1459
    }
1460

    
1461
    /**
1462
     * Creates a new {@link SynonymRelationship synonym relationship} (with the given {@link Synonym synonym},
1463
     * with the {@link SynonymRelationshipType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of" relationship type}
1464
     * and with the {@link eu.etaxonomy.cdm.model.reference.Reference reference source} on which the relationship
1465
     * assertion is based), returns it and adds it to the set of
1466
     * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon.
1467
     * Furthermore the new synonym relationship will be added to the set of
1468
     * {@link Synonym#getSynonymRelations() synonym relationships} belonging to the synonym
1469
     * involved in this synonym relationship and the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}
1470
     * used as synonym will be added to the same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group}
1471
     * to which the taxon name of <i>this</i> taxon belongs.<BR>
1472
     * The returned synonym relationship allows to add further information to it.
1473
     *
1474
     * @param synonym		the synonym involved in the "is homotypic synonym of" relationship to be created
1475
     * 						and added to <i>this</i> taxon's synonym relationships set
1476
     * @param citation		the reference source for the new synonym relationship
1477
     * @param microcitation	the string with the details describing the exact localisation within the reference
1478
     * @return 				the created synonym relationship
1479
     * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1480
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1481
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1482
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1483
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1484
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1485
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase)
1486
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1487
     * @see    	   			#getSynonymRelations()
1488
     * @see    				#removeSynonym(Synonym)
1489
     * @see    	   			Synonym#getSynonymRelations()
1490
     */
1491
    public SynonymRelationship addHomotypicSynonym(Synonym synonym, Reference citation, String microCitation){
1492
    if (this.getName() != null){
1493
            if (this.getName().getHomotypicalGroup().getTypifiedNames().isEmpty()){
1494
                this.getName().getHomotypicalGroup().getTypifiedNames().add(this.getName());
1495

    
1496
            }
1497
            this.getName().getHomotypicalGroup().addTypifiedName(synonym.getName());
1498

    
1499
        }
1500
    	SynonymRelationship synRel = null;
1501
    	if (!this.getSynonyms().contains(synonym)){
1502
    		synRel = addSynonym(synonym, SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF(), citation, microCitation);
1503
    	} else{
1504
    		logger.warn("The synonym is already related to the taxon.");
1505

    
1506
    	}
1507
        return synRel;
1508
    }
1509

    
1510
    /**
1511
     * Like {@link #removeSynonym(Synonym, boolean)} with <code>removeSynonymNameFromHomotypicalGroup</code> set to true.
1512
     * @see #removeSynonym(Synonym, boolean)
1513
     */
1514
    public void removeSynonym(Synonym synonym){
1515
        removeSynonym(synonym, true);
1516
    }
1517

    
1518
    /**
1519
     * Removes the element(s) from the set of {@link SynonymRelationship synonym relationships}
1520
     * assigned to <i>this</i> ("accepted/valid") taxon in which the given synonym is involved.
1521
     * Due to bidirectionality the same synonym relationships will also be
1522
     * removed from the set of synonym relationships assigned to the
1523
     * {@link Synonym#getSynonymRelations() synonym} involved in the relationship. Furthermore the content of
1524
     * the {@link SynonymRelationship#getAcceptedTaxon() accepted taxon} attribute and of the
1525
     * {@link SynonymRelationship#getSynonym() synonym} attribute within the synonym relationships
1526
     * themselves will be set to "null".
1527
     *
1528
     * @param  synonym  the synonym involved in the synonym relationship which should be deleted
1529
     * @param removeSynonymNameFromHomotypicalGroup if <code>true</code> the removed synonyms
1530
     * 		name will get a new homotypic group in case it is together with other names in a group.
1531
     * @see     		#getSynonymRelations()
1532
     * @see     		#addSynonym(Synonym, SynonymRelationshipType)
1533
     * @see     		#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1534
     * @see 			#removeSynonymRelation(SynonymRelationship)
1535
     * @see				#removeSynonymRelation(SynonymRelationship, boolean)
1536
     */
1537
    public void removeSynonym(Synonym synonym, boolean removeSynonymNameFromHomotypicalGroup){
1538
        Set<SynonymRelationship> synonymRelationships = new HashSet<SynonymRelationship>();
1539
        synonymRelationships.addAll(this.getSynonymRelations());
1540
        for(SynonymRelationship synonymRelationship : synonymRelationships){
1541
            if (synonymRelationship.getAcceptedTaxon().equals(this) && synonymRelationship.getSynonym().equals(synonym)){
1542
                this.removeSynonymRelation(synonymRelationship, removeSynonymNameFromHomotypicalGroup);
1543
            }
1544
        }
1545
    }
1546

    
1547

    
1548
    /**
1549
     * Retrieves the ordered list (depending on the date of publication) of
1550
     * homotypic {@link Synonym synonyms} (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1551
     * as for <i>this</i> taxon) under the condition that the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon names}
1552
     * of these synonyms and the taxon name of <i>this</i> taxon belong to the
1553
     * same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group}.
1554
     *
1555
     * @return		the ordered list of homotypic synonyms
1556
     * @see			#getHomotypicSynonymsByHomotypicRelationship()
1557
     * @see			#getSynonyms()
1558
     * @see			#getHomotypicSynonymyGroups()
1559
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1560
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup#getSynonymsInGroup(Reference)
1561
     */
1562
    @Transient
1563
    public List<Synonym> getHomotypicSynonymsByHomotypicGroup(){
1564
        if (this.getHomotypicGroup() == null){
1565
            return null;
1566
        }else{
1567
            return this.getSynonymsInGroup(this.getHomotypicGroup());
1568
        }
1569
    }
1570

    
1571
    /**
1572
     * Retrieves the ordered list (depending on the date of publication) of
1573
     * homotypic {@link Synonym synonyms} (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1574
     * as for <i>this</i> taxon) under the condition that these synonyms and
1575
     * <i>this</i> taxon are involved in {@link SynonymRelationship synonym relationships} with an
1576
     * "is homotypic synonym of" {@link SynonymRelationshipType#HOMOTYPIC_SYNONYM_OF() synonym relationship type}.
1577
     *
1578
     * @return		the ordered list of homotypic synonyms
1579
     * @see			#getHomotypicSynonymsByHomotypicGroup()
1580
     * @see			#getSynonyms()
1581
     * @see			#getHomotypicSynonymyGroups()
1582
     * @see			SynonymRelationshipType
1583
     */
1584
    @Transient
1585
    public List<Synonym> getHomotypicSynonymsByHomotypicRelationship(){
1586
        Set<SynonymRelationship> synonymRelations = this.getSynonymRelations();
1587
        List<Synonym> result = new ArrayList<Synonym>();
1588
        for(SynonymRelationship synonymRelation : synonymRelations) {
1589
            if(synonymRelation.getType().equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){
1590
                result.add(synonymRelation.getSynonym());
1591
            }
1592
        }
1593
        return result;
1594
    }
1595

    
1596
    /**
1597
     * Returns the ordered list of all {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups} {@link Synonym synonyms} of
1598
     * <i>this</i> taxon belong to. {@link eu.etaxonomy.cdm.model.name.TaxonNameBase Taxon names} of homotypic synonyms
1599
     * belong to the same homotypical group as the taxon name of <i>this</i>
1600
     * taxon. Taxon names of heterotypic synonyms belong to at least one other
1601
     * homotypical group. <BR>
1602
     * The list returned is ordered according to the date of publication of the
1603
     * first published name within each homotypical group.
1604
     *
1605
     * @see			#getHeterotypicSynonymyGroups()
1606
     * @see			#getSynonyms()
1607
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1608
     */
1609
    @Transient
1610
    public List<HomotypicalGroup> getHomotypicSynonymyGroups(){
1611
        List<HomotypicalGroup> result = new ArrayList<HomotypicalGroup>();
1612
        result.add(this.getHomotypicGroup());
1613
        for (TaxonNameBase taxonNameBase :this.getSynonymNames()){
1614
            if (taxonNameBase != null) {
1615
                if (!result.contains(taxonNameBase.getHomotypicalGroup())){
1616
                    result.add(taxonNameBase.getHomotypicalGroup());
1617
                }
1618
            } // TODO: give error message to user
1619
        }
1620
        // TODO: sort list according to date of first published name within each group
1621
        return result;
1622
    }
1623

    
1624

    
1625

    
1626
    /**
1627
     * The status of this taxon is unknown it could also be some kind of synonym.
1628
     * @return the taxonStatusUnknown
1629
     */
1630
    public boolean isTaxonStatusUnknown() {
1631
        return taxonStatusUnknown;
1632
    }
1633

    
1634
    /**
1635
     * @param taxonStatusUnknown the taxonStatusUnknown to set
1636
     */
1637
    public void setTaxonStatusUnknown(boolean taxonStatusUnknown) {
1638
        this.taxonStatusUnknown = taxonStatusUnknown;
1639
    }
1640

    
1641

    
1642

    
1643

    
1644
    public boolean isUnplaced() {
1645
        return unplaced;
1646
    }
1647

    
1648
    @Override
1649
    @Transient
1650
    public boolean isOrphaned() {
1651

    
1652
        if(taxonNodes == null || taxonNodes.isEmpty()) {
1653
            if(getRelationsFromThisTaxon().isEmpty() && getRelationsToThisTaxon().isEmpty()) {
1654
                return true;
1655
            }
1656
        }
1657
        return false;
1658
    }
1659

    
1660
    public void setUnplaced(boolean unplaced) {
1661
        this.unplaced = unplaced;
1662
    }
1663

    
1664
    public boolean isExcluded() {
1665
        return excluded;
1666
    }
1667

    
1668
    public void setExcluded(boolean excluded) {
1669
        this.excluded = excluded;
1670
    }
1671

    
1672
    /**
1673
     * Returns the ordered list of all {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups}
1674
     * that contain {@link Synonym synonyms} that are heterotypic to <i>this</i> taxon.
1675
     * {@link eu.etaxonomy.cdm.model.name.TaxonNameBase Taxon names} of heterotypic synonyms
1676
     * belong to a homotypical group which cannot be the homotypical group to which the
1677
     * taxon name of <i>this</i> taxon belongs. This method returns the same
1678
     * list as the {@link #getHomotypicSynonymyGroups() getHomotypicSynonymyGroups} method
1679
     * but without the homotypical group to which the taxon name of <i>this</i> taxon
1680
     * belongs.<BR>
1681
     * The list returned is ordered according to the date of publication of the
1682
     * first published name within each homotypical group.
1683
     *
1684
     * @see			#getHeterotypicSynonymyGroups()
1685
     * @see			#getSynonyms()
1686
     * @see			SynonymRelationshipType#HETEROTYPIC_SYNONYM_OF()
1687
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1688
     */
1689
    @Transient
1690
    public List<HomotypicalGroup> getHeterotypicSynonymyGroups(){
1691
        List<HomotypicalGroup> list = getHomotypicSynonymyGroups();
1692
        list.remove(this.getHomotypicGroup());
1693
        //sort
1694
        Map<Synonym, HomotypicalGroup> map = new HashMap<Synonym, HomotypicalGroup>();
1695
        for (HomotypicalGroup homotypicalGroup: list){
1696
            List<Synonym> synonymList = getSynonymsInGroup(homotypicalGroup);
1697
            if (synonymList.size() > 0){
1698
                map.put(synonymList.get(0), homotypicalGroup);
1699
            }
1700
        }
1701
        List<Synonym> keyList = new ArrayList<Synonym>();
1702
        keyList.addAll(map.keySet());
1703
        Collections.sort(keyList, new TaxonComparator());
1704

    
1705
        List<HomotypicalGroup> result = new ArrayList<HomotypicalGroup>();
1706
        for(Synonym synonym: keyList){
1707
            result.add(map.get(synonym));
1708
        }
1709
        //sort end
1710
        return result;
1711
    }
1712

    
1713
    /**
1714
     * Retrieves the ordered list (depending on the date of publication) of
1715
     * {@link taxon.Synonym synonyms} (according to a given reference)
1716
     * the {@link TaxonNameBase taxon names} of which belong to the homotypical group.
1717
     * If other names are part of the group that are not considered synonyms of
1718
     * <i>this</i> taxon, then they will not be included in
1719
     * the result set.
1720
     *
1721
     * @param homoGroup
1722
     * @see			TaxonNameBase#getSynonyms()
1723
     * @see			TaxonNameBase#getTaxa()
1724
     * @see			taxon.Synonym
1725
     */
1726
    @Transient
1727
    public List<Synonym> getSynonymsInGroup(HomotypicalGroup homotypicGroup){
1728
        List<Synonym> result = new ArrayList<Synonym>();
1729

    
1730
        for (TaxonNameBase<?, ?>name : homotypicGroup.getTypifiedNames()){
1731
            for (Synonym synonym : name.getSynonyms()){
1732
                for(SynonymRelationship synRel : synonym.getSynonymRelations()){
1733
                    if (synRel.getAcceptedTaxon().equals(this)){
1734
                        result.add(synRel.getSynonym());
1735
                    }
1736
                }
1737
            }
1738
        }
1739
        Collections.sort(result, new TaxonComparator());
1740
        return result;
1741
    }
1742

    
1743

    
1744
    /**
1745
     * Returns the image gallery description. If no image gallery exists, a new one is created using the
1746
     * defined title and adds the string "-Image Gallery" to the title.</BR>
1747
     * If multiple image galleries exist an arbitrary one is choosen.
1748
     * @param title
1749
     * @return
1750
     */
1751
    @Transient
1752
    public TaxonDescription getOrCreateImageGallery(String title){
1753
        return getOrCreateImageGallery(title, true, false);
1754
    }
1755

    
1756
    /**
1757
     * Returns the image gallery description. If no image gallery exists, a new one is created using the
1758
     * defined title.</BR>
1759
     * If onlyTitle == true we look only for an image gallery with this title, create a new one otherwise.
1760
     * If multiple image galleries exist that match the conditions an arbitrary one is choosen.
1761
     * @param title
1762
     * @param onlyTitle
1763
     * @param if true, the String "Image Gallery
1764
     * @return
1765
     */
1766
    @Transient
1767
    public TaxonDescription getOrCreateImageGallery(String title, boolean addImageGalleryToTitle, boolean onlyTitle){
1768
        TaxonDescription result = null;
1769
        String titleCache = (title == null) ? "Image Gallery" : title;
1770
        if (title != null && addImageGalleryToTitle){
1771
            titleCache = titleCache+ "-Image Gallery";
1772
        }
1773
        Set<TaxonDescription> descriptionSet = this.getDescriptions();
1774
        for (TaxonDescription desc: descriptionSet){
1775
            if (desc.isImageGallery()){
1776
                if (onlyTitle && ! titleCache.equals(desc.getTitleCache())){
1777
                    continue;
1778
                }
1779
                result = desc;
1780
                if (onlyTitle && titleCache.equals(desc.getTitleCache())){
1781
                    break;
1782
                }
1783
            }
1784
        }
1785
        if (result == null){
1786
            result = TaxonDescription.NewInstance();
1787
            result.setTitleCache(titleCache, true);
1788
            this.addDescription(result);
1789
            result.setImageGallery(true);
1790
        }
1791
        return result;
1792
    }
1793
    //*********************** CLONE ********************************************************/
1794

    
1795

    
1796
    /**
1797
     * Clones <i>this</i> taxon. This is a shortcut that enables to create
1798
     * a new instance that differs only slightly from <i>this</i> taxon by
1799
     * modifying only some of the attributes.<BR><BR>
1800
     * The TaxonNodes are not cloned, the list is empty.<BR>
1801
     * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1802
     * The taxon relationships and synonym relationships are cloned <BR>
1803
     *
1804
     * @see eu.etaxonomy.cdm.model.taxon.TaxonBase#clone()
1805
     * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1806
     * @see java.lang.Object#clone()
1807
     */
1808
    @Override
1809
    public Object clone() {
1810
        Taxon result;
1811
        result = (Taxon)super.clone();
1812

    
1813
        result.setRelationsFromThisTaxon(new HashSet<TaxonRelationship>());
1814

    
1815
        for (TaxonRelationship fromRelationship : this.getRelationsFromThisTaxon()){
1816
            TaxonRelationship newRelationship = (TaxonRelationship)fromRelationship.clone();
1817
            newRelationship.setRelatedFrom(result);
1818
            result.relationsFromThisTaxon.add(newRelationship);
1819
        }
1820

    
1821
        result.setRelationsToThisTaxon(new HashSet<TaxonRelationship>());
1822
        for (TaxonRelationship toRelationship : this.getRelationsToThisTaxon()){
1823
            TaxonRelationship newRelationship = (TaxonRelationship)toRelationship.clone();
1824
            newRelationship.setRelatedTo(result);
1825
            result.relationsToThisTaxon.add(newRelationship);
1826
        }
1827

    
1828

    
1829
        result.synonymRelations = new HashSet<SynonymRelationship>();
1830
        for (SynonymRelationship synRelationship : this.getSynonymRelations()){
1831
            SynonymRelationship newRelationship = (SynonymRelationship)synRelationship.clone();
1832
            newRelationship.setRelatedTo(result);
1833
            result.synonymRelations.add(newRelationship);
1834
        }
1835

    
1836

    
1837
        result.taxonNodes = new HashSet<TaxonNode>();
1838

    
1839
        /*for (TaxonNode taxonNode : this.getTaxonNodes()){
1840
            TaxonNode newTaxonNode = (TaxonNode)taxonNode.clone();
1841
            newTaxonNode.setTaxon(result);
1842
            result.addTaxonNode(newTaxonNode);
1843
        }*/
1844

    
1845
        return result;
1846

    
1847
    }
1848

    
1849
    public void clearDescriptions() {
1850
		this.descriptions = new HashSet<TaxonDescription>();
1851
	}
1852

    
1853
    @Override
1854
    public void setCacheStrategy(ITaxonCacheStrategy<Taxon> cacheStrategy){
1855
    	this.cacheStrategy = cacheStrategy;
1856
    }
1857
}
(9-9/20)