Project

General

Profile

Download (86.8 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, CascadeType.DELETE})
136
//    @Valid
137
    private Set<TaxonRelationship> relationsToThisTaxon = new HashSet<TaxonRelationship>();
138

    
139
    @XmlAttribute(name= "taxonStatusUnknown")
140
    private boolean taxonStatusUnknown = false;
141
    /**
142
     * The status of this taxon is unknown it could also be some kind of synonym.
143
     * @return the taxonStatusUnknown
144
     */
145
    public boolean isTaxonStatusUnknown() {return taxonStatusUnknown;}
146
     /** @see #isTaxonStatusUnknown()*/
147
    public void setTaxonStatusUnknown(boolean taxonStatusUnknown) {this.taxonStatusUnknown = taxonStatusUnknown;}
148

    
149

    
150
    @XmlAttribute(name= "unplaced")
151
    private boolean unplaced = false;
152
    public boolean isUnplaced() {return unplaced;}
153
    public void setUnplaced(boolean unplaced) {this.unplaced = unplaced;}
154

    
155

    
156
    @XmlAttribute(name= "excluded")
157
    private boolean excluded = false;
158
    public boolean isExcluded() {return excluded;}
159
    public void setExcluded(boolean excluded) {this.excluded = excluded;}
160

    
161

    
162
    // shortcut to the taxonomicIncluded (parent) taxon. Managed by the taxonRelations setter
163
    @XmlElement(name = "TaxonomicParentCache")
164
    @XmlIDREF
165
    @XmlSchemaType(name = "IDREF")
166
    @ManyToOne(fetch = FetchType.LAZY)
167
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
168
    private Taxon taxonomicParentCache;
169

    
170

    
171
    @XmlElementWrapper(name = "taxonNodes")
172
    @XmlElement(name = "taxonNode")
173
    @XmlIDREF
174
    @XmlSchemaType(name = "IDREF")
175
    @OneToMany(mappedBy="taxon", fetch=FetchType.LAZY)
176
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
177
    @IndexedEmbedded
178
    private Set<TaxonNode> taxonNodes = new HashSet<TaxonNode>();
179

    
180
    //cached number of taxonomic children
181
    @XmlElement(name = "TaxonomicChildrenCount")
182
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
183
    private int taxonomicChildrenCount;
184

    
185
// ************************* FACTORY METHODS ********************************/
186

    
187
    /**
188
     * Creates a new (accepted/correct) taxon instance with
189
     * the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
190
     * using it.
191
     *
192
     * @param  taxonNameBase	the taxon name used
193
     * @param  sec				the reference using the taxon name
194
     * @see    					#Taxon(TaxonNameBase, Reference)
195
     */
196
    public static Taxon NewInstance(TaxonNameBase taxonNameBase, Reference sec){
197
        Taxon result = new Taxon(taxonNameBase, sec);
198
        return result;
199
    }
200

    
201
    /**
202
     * Creates a new taxon instance with an unknown status (accepted/synonym) and with
203
     * the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
204
     * using it.
205
     *
206
     * @param  taxonNameBase	the taxon name used
207
     * @param  sec				the reference using the taxon name
208
     * @see    					#Taxon(TaxonNameBase, Reference)
209
     */
210
    public static Taxon NewUnknownStatusInstance(TaxonNameBase taxonNameBase, Reference sec){
211
        Taxon result = new Taxon(taxonNameBase, sec);
212
        result.setTaxonStatusUnknown(true);
213
        return result;
214
    }
215
// ************* CONSTRUCTORS *************/
216

    
217
    //TODO should be private, but still produces Spring init errors
218
    @Deprecated
219
    public Taxon(){
220
        this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Taxon>();
221
    }
222

    
223
    /**
224
     * Class constructor: creates a new (accepted/correct) taxon instance with
225
     * the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
226
     * using it.
227
     *
228
     * @param  taxonNameBase	the taxon name used
229
     * @param  sec				the reference using the taxon name
230
     * @see    					TaxonBase#TaxonBase(TaxonNameBase, Reference)
231
     */
232
    public Taxon(TaxonNameBase taxonNameBase, Reference sec){
233
        super(taxonNameBase, sec);
234
        this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Taxon>();
235
    }
236

    
237
//********* METHODS **************************************/
238

    
239

    
240

    
241
    /**
242
     * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon descriptions}
243
     * concerning <i>this</i> taxon.
244
     *
245
     * @see #removeDescription(TaxonDescription)
246
     * @see #addDescription(TaxonDescription)
247
     * @see eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
248
     */
249
    @Override
250
    public Set<TaxonDescription> getDescriptions() {
251
        if(descriptions == null) {
252
            descriptions = new HashSet<TaxonDescription>();
253
        }
254
        return descriptions;
255
    }
256

    
257
    /**
258
     * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon description} to the set
259
     * of taxon descriptions assigned to <i>this</i> (accepted/correct) taxon.
260
     * Due to bidirectionality the content of the {@link eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon() taxon attribute} of the
261
     * taxon description itself will be replaced with <i>this</i> taxon. The taxon
262
     * description will also be removed from the set of taxon descriptions
263
     * assigned to its previous taxon.
264
     *
265
     * @param  description	the taxon description to be added for <i>this</i> taxon
266
     * @see     		  	#getDescriptions()
267
     * @see     		  	#removeDescription(TaxonDescription)
268
     * @see 			  	eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
269
     */
270
    @Override
271
    public void addDescription(TaxonDescription description) {
272
        if (description.getTaxon() != null){
273
            description.getTaxon().removeDescription(description);
274
        }
275
        Field field = ReflectionUtils.findField(TaxonDescription.class, "taxon", Taxon.class);
276
        ReflectionUtils.makeAccessible(field);
277
        ReflectionUtils.setField(field, description, this);
278
        descriptions.add(description);
279

    
280
    }
281
    /**
282
     * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon descriptions} assigned
283
     * to <i>this</i> (accepted/correct) taxon. Due to bidirectionality the content of
284
     * the {@link eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon() taxon attribute} of the taxon description
285
     * itself will be set to "null".
286
     *
287
     * @param  description  the taxon description which should be removed
288
     * @see     		  	#getDescriptions()
289
     * @see     		  	#addDescription(TaxonDescription)
290
     * @see 			  	eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
291
     */
292
    @Override
293
    public void removeDescription(TaxonDescription description) {
294
        //description.setTaxon(null) for not visible method
295
        Field field = ReflectionUtils.findField(TaxonDescription.class, "taxon", Taxon.class);
296
        ReflectionUtils.makeAccessible(field);
297
        ReflectionUtils.setField(field, description, null);
298
        descriptions.remove(description);
299
    }
300

    
301

    
302
    public void removeDescription(TaxonDescription description, boolean removeElements){
303
    	if (removeElements){
304
    		Set<DescriptionElementBase> elements = new HashSet<DescriptionElementBase>(description.getElements());
305
            for (DescriptionElementBase el:elements){
306
            	description.getElements().remove(el);
307
            }
308
            removeDescription(description);
309
    	} else{
310
    		removeDescription(description);
311
    	}
312
    }
313

    
314
    /**
315
     * Returns the image gallery for a taxon. If there are multiple taxon descriptions
316
     * marked as image galleries an arbitrary one is chosen.
317
     * If no image gallery exists, a new one is created if <code>createNewIfNotExists</code>
318
     * is <code>true</code>.
319
     * @param createNewIfNotExists
320
     * @return
321
     */
322
    public TaxonDescription getImageGallery(boolean createNewIfNotExists) {
323
        TaxonDescription result = null;
324
        Set<TaxonDescription> descriptions= getDescriptions();
325
        for (TaxonDescription description : descriptions){
326
            if (description.isImageGallery()){
327
                result = description;
328
                break;
329
            }
330
        }
331
        if (result == null && createNewIfNotExists){
332
            result = TaxonDescription.NewInstance(this);
333
            result.setImageGallery(true);
334
        }
335
        return result;
336
    }
337

    
338

    
339

    
340
    public Set<TaxonNode> getTaxonNodes() {
341
        return taxonNodes;
342
    }
343
    //	protected void setTaxonNodes(Set<TaxonNode> taxonNodes) {
344
//		this.taxonNodes = taxonNodes;
345
//	}
346
    protected void addTaxonNode(TaxonNode taxonNode){
347
        taxonNodes.add(taxonNode);
348
    }
349

    
350
    public boolean removeTaxonNode(TaxonNode taxonNode){
351
        if (!taxonNodes.contains(taxonNode)){
352
            return false;
353
        }
354
        TaxonNode parent = taxonNode.getParent();
355
        if (parent != null){
356
            parent.removeChildNode(taxonNode);
357
        }
358
        taxonNode.setTaxon(null);
359
        return taxonNodes.remove(taxonNode);
360

    
361
    }
362

    
363
    public boolean removeTaxonNode(TaxonNode taxonNode, boolean deleteChildren){
364
        TaxonNode parent = taxonNode.getParent();
365
        boolean success = true;
366

    
367
        if ((!taxonNode.getChildNodes().isEmpty() && deleteChildren) || (taxonNode.getChildNodes().isEmpty()) ){
368

    
369
            taxonNode.delete();
370

    
371
        } else if (!taxonNode.isTopmostNode()){
372

    
373
            List<TaxonNode> nodes = new ArrayList<TaxonNode> (taxonNode.getChildNodes());
374
            for (TaxonNode childNode: nodes){
375
                taxonNode.getChildNodes().remove(childNode);
376
                parent.addChildNode(childNode, null, null);
377
            }
378

    
379
            taxonNode.delete();
380

    
381
        } else if (taxonNode.isTopmostNode()){
382
            success = false;
383
        }
384
        return success;
385
    }
386

    
387
    public boolean removeTaxonNodes(boolean deleteChildren){
388
        Iterator<TaxonNode> nodesIterator = taxonNodes.iterator();
389
        TaxonNode node;
390
        TaxonNode parent;
391
        boolean success = false;
392
        List<TaxonNode> removeNodes = new ArrayList<TaxonNode>();
393
        while (nodesIterator.hasNext()){
394
            node = nodesIterator.next();
395
            if (!deleteChildren){
396
                List<TaxonNode> children = node.getChildNodes();
397
                Iterator<TaxonNode> childrenIterator = children.iterator();
398
                parent = node.getParent();
399
                while (childrenIterator.hasNext()){
400
                    TaxonNode childNode = childrenIterator.next();
401
                    if (parent != null){
402
                        parent.addChildNode(childNode, null, null);
403
                    }else{
404
                        childNode.setParent(null);
405
                    }
406
                }
407

    
408
                for (int i = 0; i<node.getChildNodes().size(); i++){
409
                    node.removeChild(i);
410
                }
411

    
412

    
413
            }
414

    
415
            removeNodes.add(node);
416
         }
417
        for (int i = 0; i<removeNodes.size(); i++){
418
            TaxonNode removeNode = removeNodes.get(i);
419
            success = removeNode.delete(deleteChildren);
420
            removeNode.setTaxon(null);
421
            removeTaxonNode(removeNode);
422
        }
423
        return success;
424

    
425
    }
426

    
427

    
428

    
429

    
430
    /**
431
     * Returns the set of all {@link SynonymRelationship synonym relationships}
432
     * in which <i>this</i> ("accepted/correct") taxon is involved. <i>This</i> taxon can only
433
     * be the target of these synonym relationships.
434
     *
435
     * @see    #addSynonymRelation(SynonymRelationship)
436
     * @see    #removeSynonymRelation(SynonymRelationship)
437
     * @see    #getSynonyms()
438
     */
439
    public Set<SynonymRelationship> getSynonymRelations() {
440
        if(synonymRelations == null) {
441
            this.synonymRelations = new HashSet<SynonymRelationship>();
442
        }
443
        return synonymRelations;
444
    }
445

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

    
497
    /**
498
     * Like {@link Taxon#removeSynonymRelation(SynonymRelationship, boolean)} but synonym name
499
     * will be deleted from homotypical group by default
500
     *
501
     * @param synonymRelation   the synonym relationship which should be deleted
502
     *
503
     * @see					#removeSynonymRelation(SynonymRelationship, boolean)
504
     */
505
    public void removeSynonymRelation(SynonymRelationship synonymRelation){
506
        removeSynonymRelation(synonymRelation, true);
507
    }
508

    
509

    
510
    /**
511
     * Returns the set of all {@link TaxonRelationship taxon relationships}
512
     * between two taxa in which <i>this</i> taxon is involved as a source.
513
     *
514
     * @see    #getRelationsToThisTaxon()
515
     * @see    #getTaxonRelations()
516
     */
517
    public Set<TaxonRelationship> getRelationsFromThisTaxon() {
518
        if(relationsFromThisTaxon == null) {
519
            this.relationsFromThisTaxon = new HashSet<TaxonRelationship>();
520
        }
521
        return relationsFromThisTaxon;
522
    }
523

    
524

    
525
    /**
526
     * Returns the set of all {@link TaxonRelationship taxon relationships}
527
     * between two taxa in which <i>this</i> taxon is involved as a target.
528
     *
529
     * @see    #getRelationsFromThisTaxon()
530
     * @see    #getTaxonRelations()
531
     */
532
    public Set<TaxonRelationship> getRelationsToThisTaxon() {
533
        if(relationsToThisTaxon == null) {
534
            this.relationsToThisTaxon = new HashSet<TaxonRelationship>();
535
        }
536
        return relationsToThisTaxon;
537
    }
538
    /**
539
     * Returns the set of all {@link TaxonRelationship taxon relationships}
540
     * between two taxa in which <i>this</i> taxon is involved either as a source or
541
     * as a target.
542
     *
543
     * @see    #getRelationsFromThisTaxon()
544
     * @see    #getRelationsToThisTaxon()
545
     */
546
    @Transient
547
    public Set<TaxonRelationship> getTaxonRelations() {
548
        Set<TaxonRelationship> rels = new HashSet<TaxonRelationship>();
549
        rels.addAll(getRelationsToThisTaxon());
550
        rels.addAll(getRelationsFromThisTaxon());
551
        return rels;
552
    }
553

    
554
    /**
555
     * @see    #getRelationsToThisTaxon()
556
     */
557
    protected void setRelationsToThisTaxon(Set<TaxonRelationship> relationsToThisTaxon) {
558
        this.relationsToThisTaxon = relationsToThisTaxon;
559
    }
560

    
561
    /**
562
     * @see    #getRelationsFromThisTaxon()
563
     */
564
    protected void setRelationsFromThisTaxon(Set<TaxonRelationship> relationsFromThisTaxon) {
565
        this.relationsFromThisTaxon = relationsFromThisTaxon;
566
    }
567

    
568
    /**
569
     * If a relationships between <i>this</i> and the given taxon exists they will be returned.
570
     * <i>This</i> taxon is involved either as a source or as a target in the relationships.
571
     * The method will return <code>null</code> if no relations exist between the two taxa.
572
     *
573
     * @param possiblyRelatedTaxon
574
     * 			a taxon to check for a relationship
575
     * @return
576
     * 			a set of <code>TaxonRelationship</code>s or <code>null</null> if none exists.
577
     */
578
    public Set<TaxonRelationship> getTaxonRelations(Taxon possiblyRelatedTaxon){
579
        Set<TaxonRelationship> relations = new HashSet<TaxonRelationship>();
580

    
581
        for(TaxonRelationship relationship : getTaxonRelations()){
582
            if(relationship.getFromTaxon().equals(possiblyRelatedTaxon)) {
583
                relations.add(relationship);
584
            }
585
            if(relationship.getToTaxon().equals(possiblyRelatedTaxon)) {
586
                relations.add(relationship);
587
            }
588
        }
589

    
590
        return relations.size() > 0 ? relations : null;
591
    }
592

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

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

    
689
                }
690
            }else if (toTaxon == null || fromTaxon == null){
691
                if (toTaxon == null){
692
                    toTaxon = this;
693
                    relationsToThisTaxon.add(rel);
694
                    if (fromTaxon!= null){
695
                        fromTaxon.addTaxonRelation(rel);
696
                    }
697
                    if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && fromTaxon!=null ){
698
                        this.taxonomicChildrenCount++;
699
                    }
700
                }else if (fromTaxon == null && toTaxon != null){
701
                    fromTaxon = this;
702
                    relationsFromThisTaxon.add(rel);
703
                    if (toTaxon!=null){
704
                        toTaxon.addTaxonRelation(rel);
705
                    }
706
                    if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && toTaxon!=null ){
707
                        this.taxonomicParentCache = toTaxon;
708
                    }
709
                }
710
            }
711
        }
712
    }
713

    
714
    /* (non-Javadoc)
715
     * @see eu.etaxonomy.cdm.model.common.IRelated#addRelationship(eu.etaxonomy.cdm.model.common.RelationshipBase)
716
     */
717
    @Override
718
    @Deprecated //for inner use by RelationshipBase only
719
    public void addRelationship(RelationshipBase rel){
720
        if (rel instanceof TaxonRelationship){
721
            addTaxonRelation((TaxonRelationship)rel);
722
        }else if (rel instanceof SynonymRelationship){
723
            addSynonymRelation((SynonymRelationship)rel);
724
        }else{
725
            throw new ClassCastException("Wrong Relationsship type for Taxon.addRelationship");
726
        }
727
    }
728

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

    
776
//	public void removeMisappliedName(Taxon misappliedNameTaxon){
777
//		Set<TaxonRelationship> taxRels = this.getTaxonRelations();
778
//		for (TaxonRelationship taxRel : taxRels ){
779
//			if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())
780
//				&& taxRel.getFromTaxon().equals(misappliedNameTaxon)){
781
//				this.removeTaxonRelation(taxRel);
782
//			}
783
//		}
784
//	}
785

    
786
    /**
787
     * TODO update documentation
788
     * Removes one {@link TaxonRelationship taxon relationship} with {@link TaxonRelationshipType taxon relationship type}
789
     * taxonRelType and with the given child taxon playing the
790
     * source role from the set of {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging
791
     * to <i>this</i> taxon. The taxon relationship will also be removed from the set
792
     * of {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to the other side taxon.
793
     * Furthermore, the inherited RelatedFrom and RelatedTo attributes of the
794
     * taxon relationship will be nullified.<P>
795
     *
796
     * @param taxon			the taxon which plays the source role in the taxon relationship
797
     * @param taxonRelType	the taxon relationship type
798
     */
799
    public void removeTaxon(Taxon taxon, TaxonRelationshipType taxonRelType){
800
        Set<TaxonRelationship> taxRels = this.getTaxonRelations();
801
        for (TaxonRelationship taxRel : taxRels ){
802
            if (taxRel.getType().equals(taxonRelType)
803
                && taxRel.getFromTaxon().equals(taxon)){
804
                this.removeTaxonRelation(taxRel);
805
            }
806
        }
807
    }
808

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

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

    
877
    /**
878
     * Returns the taxon which is the next higher taxon (parent) of <i>this</i> taxon
879
     * within the classification and which is stored in the
880
     * TaxonomicParentCache attribute. Each taxon can have only one parent taxon.
881
     * The child taxon and the parent taxon play the source respectively the
882
     * target role in one {@link TaxonRelationship taxon relationship} with
883
     * {@link TaxonRelationshipType taxon relationship type} "taxonomically included in".
884
     * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
885
     * than the rank of the taxon name used as a child taxon.
886
     *
887
     * @see  #setTaxonomicParent(Taxon, Reference, String)
888
     * @see  #getTaxonomicChildren()
889
     * @see  #getTaxonomicChildrenCount()
890
     * @see  #getRelationsFromThisTaxon()
891
     */
892
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
893
    public Taxon getTaxonomicParent() {
894
        return this.taxonomicParentCache;
895
    }
896

    
897
    /**
898
     * Sets the taxononomic parent of <i>this</i> taxon to null.
899
     * Note that this method does not handle taxonomic relationships.
900
     */
901
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
902
    public void nullifyTaxonomicParent() {
903
        this.taxonomicParentCache = null;
904
    }
905

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

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

    
977
    /**
978
     * Returns the number of taxa which have <i>this</i> taxon as next higher taxon
979
     * (parent) within the classification and the number of which is stored in
980
     * the TaxonomicChildrenCount attribute. Each taxon can have several child
981
     * taxa. The child taxon and the parent taxon play the source respectively
982
     * the target role in one {@link TaxonRelationship taxon relationship} with
983
     * {@link TaxonRelationshipType taxon relationship type} "taxonomically included in".
984
     * The {@link name.Rank rank} of the taxon name used as a parent taxon must be higher
985
     * than the rank of the taxon name used as a child taxon.
986
     *
987
     * @see  #getTaxonomicChildren()
988
     * @see  #getRelationsToThisTaxon()
989
     */
990
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
991
    public int getTaxonomicChildrenCount(){
992
        return taxonomicChildrenCount;
993
    }
994

    
995
    /**
996
     * @see  #getTaxonomicChildrenCount()
997
     */
998
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
999
    public void setTaxonomicChildrenCount(int taxonomicChildrenCount) {
1000
        this.taxonomicChildrenCount = taxonomicChildrenCount;
1001
    }
1002

    
1003
    /**
1004
     * Returns the boolean value indicating whether <i>this</i> taxon has at least one
1005
     * taxonomic child taxon within the classification (true) or not (false).
1006
     *
1007
     * @see  #getTaxonomicChildrenCount()
1008
     * @see  #getTaxonomicChildren()
1009
     */
1010
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
1011
    @Transient
1012
    public boolean hasTaxonomicChildren(){
1013
        return this.taxonomicChildrenCount > 0;
1014
    }
1015

    
1016
    @Deprecated //will be removed in future versions. Use Classification/TaxonNode instead
1017
    private int computeTaxonomicChildrenCount(){
1018
        int count = 0;
1019
        for (TaxonRelationship rel: this.getRelationsToThisTaxon()){
1020
            if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())){
1021
                count++;
1022
            }
1023
        }
1024
        return count;
1025
    }
1026

    
1027

    
1028
    /**
1029
     * Returns the boolean value indicating whether <i>this</i> taxon is a misaplication
1030
     * (misapplied name) for at least one other taxon.
1031
     */
1032
    // TODO cache as for #hasTaxonomicChildren
1033
    @Transient
1034
    public boolean isMisapplication(){
1035
        return computeMisapliedNameRelations() > 0;
1036
    }
1037

    
1038
    /**
1039
     * Counts the number of misaplied names relationships where this taxon represents the
1040
     * misaplied name for another taxon.
1041
     * @return
1042
     */
1043
    private int computeMisapliedNameRelations(){
1044
        int count = 0;
1045
        for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
1046
            if (rel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
1047
                count++;
1048
            }
1049
        }
1050
        return count;
1051
    }
1052

    
1053
    /**
1054
     * Returns the boolean value indicating whether <i>this</i> taxon is a related
1055
     * concept for at least one other taxon.
1056
     */
1057
    @Transient
1058
    public boolean isRelatedConcept(){
1059
        return computeConceptRelations() > 0;
1060
    }
1061

    
1062
    /**
1063
     * Counts the number of concept relationships where this taxon represents the
1064
     * related concept for another taxon.
1065
     * @return
1066
     */
1067
    private int computeConceptRelations(){
1068
        int count = 0;
1069
        for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
1070
            TaxonRelationshipType type = rel.getType();
1071
            if (type.isConceptRelationship()){
1072
                count++;
1073
            }
1074
        }
1075
        return count;
1076
    }
1077

    
1078
    /**
1079
     * Returns the boolean value indicating whether <i>this</i> taxon has at least one
1080
     * {@link Synonym synonym} (true) or not (false). If true the {@link #getSynonymRelations() set of synonym relationships}
1081
     * belonging to <i>this</i> ("accepted/correct") taxon is not empty .
1082
     *
1083
     * @see  #getSynonymRelations()
1084
     * @see  #getSynonyms()
1085
     * @see  #getSynonymNames()
1086
     * @see  #removeSynonym(Synonym)
1087
     * @see  SynonymRelationship
1088
     */
1089
    @Transient
1090
    public boolean hasSynonyms(){
1091
        return this.getSynonymRelations().size() > 0;
1092
    }
1093

    
1094

    
1095
    /**
1096
     * Returns the boolean value indicating whether <i>this</i> taxon is at least
1097
     * involved in one {@link #getTaxonRelations() taxon relationship} between
1098
     * two taxa (true), either as a source or as a target, or not (false).
1099
     *
1100
     * @see  #getTaxonRelations()
1101
     * @see  #getRelationsToThisTaxon()
1102
     * @see  #getRelationsFromThisTaxon()
1103
     * @see  #removeTaxonRelation(TaxonRelationship)
1104
     * @see  TaxonRelationship
1105
     */
1106
    public boolean hasTaxonRelationships(){
1107
        return this.getTaxonRelations().size() > 0;
1108
    }
1109

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

    
1164

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

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

    
1365
    /**
1366
     * Creates a new {@link Synonym synonym} (with the given {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}),
1367
     * a new {@link SynonymRelationship synonym relationship} (with the new synonym and with the
1368
     * {@link SynonymRelationshipType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of" relationship type}),
1369
     * returns the relationship and adds it to the set of
1370
     * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> taxon.
1371
     * The new synonym will have the same {@link TaxonBase#getSec() concept reference}
1372
     * as <i>this</i> taxon. The new synonym relationship will also be added to
1373
     * the set of {@link Synonym#getSynonymRelations() synonym relationships} belonging
1374
     * to the created synonym.<BR>
1375
     * The returned synonym relationship allows to add further information to it.
1376
     *
1377
     * @param synonymName	the taxon name to be used as an heterotypic synonym
1378
     * 						to be added to <i>this</i> taxon's set of synonyms
1379
     * @return 				the created synonym relationship
1380
     * @see    	   			#addHeterotypicSynonymName(TaxonNameBase, HomotypicalGroup, Reference, String)
1381
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType)
1382
     * @see    	   			#addSynonymName(TaxonNameBase, SynonymRelationshipType, Reference, String)
1383
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType)
1384
     * @see    	   			#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1385
     * @see    	   			#addSynonymRelation(SynonymRelationship)
1386
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1387
     * @see    	   			#addHomotypicSynonymName(TaxonNameBase, Reference, String)
1388
     * @see    	   			#getSynonymRelations()
1389
     * @see    				#removeSynonym(Synonym)
1390
     * @see    	   			Synonym#getSynonymRelations()
1391
     */
1392
    public SynonymRelationship addHeterotypicSynonymName(TaxonNameBase synonymName){
1393
        return addHeterotypicSynonymName(synonymName, null, null, null);
1394
    }
1395

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

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

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

    
1511
            }
1512
            this.getName().getHomotypicalGroup().addTypifiedName(synonym.getName());
1513

    
1514
        }
1515
    	SynonymRelationship synRel = null;
1516
    	if (!this.getSynonyms().contains(synonym)){
1517
    		synRel = addSynonym(synonym, SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF(), citation, microCitation);
1518
    	} else{
1519
    		logger.warn("The synonym is already related to the taxon.");
1520

    
1521
    	}
1522
        return synRel;
1523
    }
1524

    
1525
    /**
1526
     * Like {@link #removeSynonym(Synonym, boolean)} with <code>removeSynonymNameFromHomotypicalGroup</code> set to true.
1527
     * @see #removeSynonym(Synonym, boolean)
1528
     */
1529
    public void removeSynonym(Synonym synonym){
1530
        removeSynonym(synonym, true);
1531
    }
1532

    
1533
    /**
1534
     * Removes the element(s) from the set of {@link SynonymRelationship synonym relationships}
1535
     * assigned to <i>this</i> ("accepted/valid") taxon in which the given synonym is involved.
1536
     * Due to bidirectionality the same synonym relationships will also be
1537
     * removed from the set of synonym relationships assigned to the
1538
     * {@link Synonym#getSynonymRelations() synonym} involved in the relationship. Furthermore the content of
1539
     * the {@link SynonymRelationship#getAcceptedTaxon() accepted taxon} attribute and of the
1540
     * {@link SynonymRelationship#getSynonym() synonym} attribute within the synonym relationships
1541
     * themselves will be set to "null".
1542
     *
1543
     * @param  synonym  the synonym involved in the synonym relationship which should be deleted
1544
     * @param removeSynonymNameFromHomotypicalGroup if <code>true</code> the removed synonyms
1545
     * 		name will get a new homotypic group in case it is together with other names in a group.
1546
     * @see     		#getSynonymRelations()
1547
     * @see     		#addSynonym(Synonym, SynonymRelationshipType)
1548
     * @see     		#addSynonym(Synonym, SynonymRelationshipType, Reference, String)
1549
     * @see 			#removeSynonymRelation(SynonymRelationship)
1550
     * @see				#removeSynonymRelation(SynonymRelationship, boolean)
1551
     */
1552
    public void removeSynonym(Synonym synonym, boolean removeSynonymNameFromHomotypicalGroup){
1553
        Set<SynonymRelationship> synonymRelationships = new HashSet<SynonymRelationship>();
1554
        synonymRelationships.addAll(this.getSynonymRelations());
1555
        for(SynonymRelationship synonymRelationship : synonymRelationships){
1556
            if (synonymRelationship.getAcceptedTaxon().equals(this) && synonymRelationship.getSynonym().equals(synonym)){
1557
                this.removeSynonymRelation(synonymRelationship, removeSynonymNameFromHomotypicalGroup);
1558
            }
1559
        }
1560
    }
1561

    
1562

    
1563
    /**
1564
     * Retrieves the ordered list (depending on the date of publication) of
1565
     * homotypic {@link Synonym synonyms} (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1566
     * as for <i>this</i> taxon) under the condition that the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon names}
1567
     * of these synonyms and the taxon name of <i>this</i> taxon belong to the
1568
     * same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group}.
1569
     *
1570
     * @return		the ordered list of homotypic synonyms
1571
     * @see			#getHomotypicSynonymsByHomotypicRelationship()
1572
     * @see			#getSynonyms()
1573
     * @see			#getHomotypicSynonymyGroups()
1574
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1575
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup#getSynonymsInGroup(Reference)
1576
     */
1577
    @Transient
1578
    public List<Synonym> getHomotypicSynonymsByHomotypicGroup(){
1579
        if (this.getHomotypicGroup() == null){
1580
            return null;
1581
        }else{
1582
            return this.getSynonymsInGroup(this.getHomotypicGroup());
1583
        }
1584
    }
1585

    
1586
    /**
1587
     * Retrieves the list of homotypic {@link Synonym synonyms}
1588
     * (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1589
     * as for <i>this</i> taxon) under the condition that these synonyms and
1590
     * <i>this</i> taxon are involved in {@link SynonymRelationship synonym relationships} with an
1591
     * "is homotypic synonym of" {@link SynonymRelationshipType#HOMOTYPIC_SYNONYM_OF() synonym relationship type}.
1592
     *
1593
     * @return		the ordered list of homotypic synonyms
1594
     * @see			#getHomotypicSynonymsByHomotypicGroup()
1595
     * @see			#getSynonyms()
1596
     * @see			#getHomotypicSynonymyGroups()
1597
     * @see			SynonymRelationshipType
1598
     * @deprecated as the method currently returns data not matching the original description of the method
1599
     * as an ordered list (according to date of publication) of synonyms with same secundum as <i>this</i> taxon.
1600
     * In future this method will either be removed or semantics may change.
1601
     */
1602
    @Deprecated
1603
    @Transient
1604
    public List<Synonym> getHomotypicSynonymsByHomotypicRelationship(){
1605
        Set<SynonymRelationship> synonymRelations = this.getSynonymRelations();
1606
        List<Synonym> result = new ArrayList<Synonym>();
1607
        for(SynonymRelationship synonymRelation : synonymRelations) {
1608
            if(synonymRelation.getType().equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){
1609
                result.add(synonymRelation.getSynonym());
1610
            }
1611
        }
1612
        return result;
1613
    }
1614

    
1615
    /**
1616
     * Returns the ordered list of all {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups} {@link Synonym synonyms} of
1617
     * <i>this</i> taxon belong to. {@link eu.etaxonomy.cdm.model.name.TaxonNameBase Taxon names} of homotypic synonyms
1618
     * belong to the same homotypical group as the taxon name of <i>this</i>
1619
     * taxon. Taxon names of heterotypic synonyms belong to at least one other
1620
     * homotypical group. <BR>
1621
     * The list returned is ordered according to the date of publication of the
1622
     * first published name within each homotypical group.
1623
     *
1624
     * @see			#getHeterotypicSynonymyGroups()
1625
     * @see			#getSynonyms()
1626
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1627
     */
1628
    @Transient
1629
    public List<HomotypicalGroup> getHomotypicSynonymyGroups(){
1630
        List<HomotypicalGroup> result = new ArrayList<HomotypicalGroup>();
1631
        result.add(this.getHomotypicGroup());
1632
        for (TaxonNameBase taxonNameBase :this.getSynonymNames()){
1633
            if (taxonNameBase != null) {
1634
                if (!result.contains(taxonNameBase.getHomotypicalGroup())){
1635
                    result.add(taxonNameBase.getHomotypicalGroup());
1636
                }
1637
            } // TODO: give error message to user
1638
        }
1639
        // TODO: sort list according to date of first published name within each group
1640
        return result;
1641
    }
1642

    
1643
    @Override
1644
    @Transient
1645
    public boolean isOrphaned() {
1646

    
1647
        if(taxonNodes == null || taxonNodes.isEmpty()) {
1648
            if(getRelationsFromThisTaxon().isEmpty() && getRelationsToThisTaxon().isEmpty()) {
1649
                return true;
1650
            }
1651
        }
1652
        return false;
1653
    }
1654

    
1655
    /**
1656
     * Returns the ordered list of all
1657
     * {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups}
1658
     * that contain {@link Synonym synonyms} that are heterotypic to <i>this</i> taxon.<BR>
1659
     *
1660
     * {@link eu.etaxonomy.cdm.model.name.TaxonNameBase Taxon names} of heterotypic synonyms
1661
     * belong to a homotypical group which cannot be the homotypical group to which the
1662
     * taxon name of <i>this</i> taxon belongs.
1663
     * This method returns the same
1664
     * list as the {@link #getHomotypicSynonymyGroups() getHomotypicSynonymyGroups} method
1665
     * but without the homotypical group to which the taxon name of <i>this</i> taxon
1666
     * belongs.<BR>
1667
     * The list returned is <B>ordered</B> according to the rules defined for
1668
     * the {@link HomotypicGroupTaxonComparator} which includes 1) grouping of
1669
     * basionym groups, 2) replaced synonym relationships, 3) publication date,
1670
     * 4) ranks and 5) alphabetical order.
1671
     *
1672
     * @see			#getHeterotypicSynonymyGroups()
1673
     * @see			#getSynonyms()
1674
     * @see			SynonymRelationshipType#HETEROTYPIC_SYNONYM_OF()
1675
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1676
     */
1677
    @Transient
1678
    public List<HomotypicalGroup> getHeterotypicSynonymyGroups(){
1679
        List<HomotypicalGroup> list = getHomotypicSynonymyGroups();
1680
        list.remove(this.getHomotypicGroup());
1681
        //sort
1682
        Map<Synonym, HomotypicalGroup> map = new HashMap<Synonym, HomotypicalGroup>();
1683
        for (HomotypicalGroup homotypicalGroup: list){
1684
            List<Synonym> synonymList = getSynonymsInGroup(homotypicalGroup);
1685
            if (synonymList.size() > 0){
1686
                //select the first synonym in the group
1687
                map.put(synonymList.get(0), homotypicalGroup);
1688
            }
1689
        }
1690
        List<Synonym> keyList = new ArrayList<Synonym>();
1691
        keyList.addAll(map.keySet());
1692
        //order by first synonym
1693
        Collections.sort(keyList, new TaxonComparator());
1694

    
1695
        List<HomotypicalGroup> result = new ArrayList<HomotypicalGroup>();
1696
        for(Synonym synonym: keyList){
1697
            result.add(map.get(synonym));
1698
        }
1699
        //sort end
1700
        return result;
1701
    }
1702

    
1703
    /**
1704
     * Retrieves the ordered list (depending on the rules defined for
1705
     * the {@link HomotypicGroupTaxonComparator}) of
1706
     * {@link taxon.Synonym synonyms} (according to a given reference)
1707
     * the {@link TaxonNameBase taxon names} of which belong to the homotypical group.
1708
     * If other names are part of the group that are not considered synonyms of
1709
     * <i>this</i> taxon, then they will not be included in
1710
     * the result set.
1711
     *
1712
     * @param homotypicGroup
1713
     * @see          #getHeterotypicSynonymyGroups()
1714
     * @see			TaxonNameBase#getSynonyms()
1715
     * @see			TaxonNameBase#getTaxa()
1716
     * @see			taxon.Synonym
1717
     */
1718
    @Transient
1719
    public List<Synonym> getSynonymsInGroup(HomotypicalGroup homotypicGroup){
1720
        return getSynonymsInGroup(homotypicGroup, new HomotypicGroupTaxonComparator(this));
1721
    }
1722

    
1723
    /**
1724
     * @param homotypicGroup
1725
     * @param comparator
1726
     * @return
1727
     * @see     #getSynonymsInGroup(HomotypicalGroup)
1728
     * @see     #getHeterotypicSynonymyGroups()
1729
     */
1730
    @Transient
1731
    public List<Synonym> getSynonymsInGroup(HomotypicalGroup homotypicGroup, TaxonComparator comparator){
1732
        List<Synonym> result = new ArrayList<Synonym>();
1733

    
1734
        for (TaxonNameBase<?, ?>name : homotypicGroup.getTypifiedNames()){
1735
            for (Synonym synonym : name.getSynonyms()){
1736
                for(SynonymRelationship synRel : synonym.getSynonymRelations()){
1737
                    if (synRel.getAcceptedTaxon().equals(this)){
1738
                        result.add(synRel.getSynonym());
1739
                    }
1740
                }
1741
            }
1742
        }
1743
        Collections.sort(result, comparator);
1744
        return result;
1745
    }
1746

    
1747

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

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

    
1798

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

    
1816
        result.setRelationsFromThisTaxon(new HashSet<TaxonRelationship>());
1817

    
1818
        for (TaxonRelationship fromRelationship : this.getRelationsFromThisTaxon()){
1819
            TaxonRelationship newRelationship = (TaxonRelationship)fromRelationship.clone();
1820
            newRelationship.setRelatedFrom(result);
1821
            result.relationsFromThisTaxon.add(newRelationship);
1822
        }
1823

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

    
1831

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

    
1839

    
1840
        result.taxonNodes = new HashSet<TaxonNode>();
1841

    
1842
        /*for (TaxonNode taxonNode : this.getTaxonNodes()){
1843
            TaxonNode newTaxonNode = (TaxonNode)taxonNode.clone();
1844
            newTaxonNode.setTaxon(result);
1845
            result.addTaxonNode(newTaxonNode);
1846
        }*/
1847

    
1848
        return result;
1849

    
1850
    }
1851

    
1852
    public void clearDescriptions() {
1853
		this.descriptions = new HashSet<TaxonDescription>();
1854
	}
1855

    
1856
}
(10-10/21)