Project

General

Profile

Download (72.7 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.OneToMany;
26
import javax.persistence.Transient;
27
import javax.validation.Valid;
28
import javax.validation.constraints.NotNull;
29
import javax.xml.bind.annotation.XmlAccessType;
30
import javax.xml.bind.annotation.XmlAccessorType;
31
import javax.xml.bind.annotation.XmlAttribute;
32
import javax.xml.bind.annotation.XmlElement;
33
import javax.xml.bind.annotation.XmlElementWrapper;
34
import javax.xml.bind.annotation.XmlIDREF;
35
import javax.xml.bind.annotation.XmlRootElement;
36
import javax.xml.bind.annotation.XmlSchemaType;
37
import javax.xml.bind.annotation.XmlType;
38

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

    
51
import eu.etaxonomy.cdm.hibernate.search.GroupByTaxonClassBridge;
52
import eu.etaxonomy.cdm.hibernate.search.TaxonRelationshipClassBridge;
53
import eu.etaxonomy.cdm.model.common.IRelated;
54
import eu.etaxonomy.cdm.model.common.RelationshipBase;
55
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
56
import eu.etaxonomy.cdm.model.description.IDescribable;
57
import eu.etaxonomy.cdm.model.description.TaxonDescription;
58
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
59
import eu.etaxonomy.cdm.model.name.ITaxonNameBase;
60
import eu.etaxonomy.cdm.model.name.TaxonName;
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
 * @since 08-Nov-2007 13:06:56
75
 */
76
@XmlAccessorType(XmlAccessType.FIELD)
77
@XmlType(name = "Taxon", propOrder = {
78
    "taxonNodes",
79
    "synonyms",
80
    "relationsFromThisTaxon",
81
    "relationsToThisTaxon",
82
    "descriptions"
83
})
84
@XmlRootElement(name = "Taxon")
85
@Entity
86
@Indexed(index = "eu.etaxonomy.cdm.model.taxon.TaxonBase")
87
@Audited
88
@Configurable
89
@ClassBridges({
90
    @ClassBridge(impl = GroupByTaxonClassBridge.class),
91
    @ClassBridge(impl = TaxonRelationshipClassBridge.class)
92
})
93
public class Taxon
94
            extends TaxonBase<ITaxonCacheStrategy<Taxon>>
95
            implements IRelated<RelationshipBase>, IDescribable<TaxonDescription>, Cloneable{
96

    
97
    private static final long serialVersionUID = -584946869762749006L;
98
    private static final Logger logger = Logger.getLogger(Taxon.class);
99

    
100
    private static final TaxonComparator defaultTaxonComparator = new TaxonComparator();
101

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

    
110
    // all related synonyms
111
    @XmlElementWrapper(name = "Synonyms")
112
    @XmlElement(name = "Synonym")
113
    @XmlIDREF
114
    @XmlSchemaType(name = "IDREF")
115
    @OneToMany(mappedBy="acceptedTaxon", fetch=FetchType.LAZY, orphanRemoval=false) //we allow synonyms to stay on their own for dirty data and for intermediate states during e.g. imports
116
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
117
    @NotNull
118
    @Valid
119
    private Set<Synonym> synonyms = new HashSet<>();
120

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

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

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

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

    
159
// ************************* FACTORY METHODS ********************************/
160

    
161
    /**
162
     * @see #NewInstance(TaxonName, Reference)
163
     * @param taxonName
164
     * @param sec
165
     * @return
166
     */
167
    public static Taxon NewInstance(ITaxonNameBase taxonName, Reference sec){
168
        return NewInstance(TaxonName.castAndDeproxy(taxonName), sec);
169
    }
170

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

    
185
    /**
186
     * Creates a new Taxon for the given name, secundum reference and secundum detail
187
     * @param taxonName
188
     * @param sec
189
     * @param secMicroReference
190
     * @see #
191
     */
192
    public static Taxon NewInstance(TaxonName taxonName, Reference sec, String secMicroReference){
193
        Taxon result = new Taxon(taxonName, sec, secMicroReference);
194
        return result;
195
    }
196

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

    
213
    //TODO should be private, but still produces Spring init errors
214
    @Deprecated
215
    public Taxon(){
216
        this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<>();
217
    }
218

    
219
    private Taxon(TaxonName taxonName, Reference sec){
220
        super(taxonName, sec, null);
221
        this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<>();
222
    }
223

    
224
    private Taxon(TaxonName taxonName, Reference sec, String secMicroReference){
225
        super(taxonName, sec, secMicroReference);
226
        this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Taxon>();
227
    }
228

    
229
//********* METHODS **************************************/
230

    
231

    
232

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

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

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

    
293

    
294
    public void removeDescription(TaxonDescription description, boolean removeElements){
295
    	if (removeElements){
296
    		Set<DescriptionElementBase> elements = new HashSet<DescriptionElementBase>(description.getElements());
297
            for (DescriptionElementBase el:elements){
298
            	description.getElements().remove(el);
299
            }
300
            removeDescription(description);
301
    	} else{
302
    		removeDescription(description);
303
    	}
304
    }
305

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

    
330

    
331

    
332
    public Set<TaxonNode> getTaxonNodes() {
333
        return taxonNodes;
334
    }
335
    //	protected void setTaxonNodes(Set<TaxonNode> taxonNodes) {
336
//		this.taxonNodes = taxonNodes;
337
//	}
338
    protected void addTaxonNode(TaxonNode taxonNode){
339
        taxonNodes.add(taxonNode);
340
    }
341

    
342
    public boolean removeTaxonNode(TaxonNode taxonNode){
343
        if (!taxonNodes.contains(taxonNode)){
344
            return false;
345
        }
346
        TaxonNode parent = taxonNode.getParent();
347
        if (parent != null){
348
            parent.removeChildNode(taxonNode);
349
        }
350
        taxonNode.setTaxon(null);
351
        return taxonNodes.remove(taxonNode);
352

    
353
    }
354

    
355
    public boolean removeTaxonNode(TaxonNode taxonNode, boolean deleteChildren){
356
        TaxonNode parent = taxonNode.getParent();
357
        boolean success = true;
358

    
359
        if ((!taxonNode.getChildNodes().isEmpty() && deleteChildren) || (taxonNode.getChildNodes().isEmpty()) ){
360

    
361
            taxonNode.delete();
362

    
363
        } else if (!taxonNode.isTopmostNode()){
364

    
365
            List<TaxonNode> nodes = new ArrayList<TaxonNode> (taxonNode.getChildNodes());
366
            for (TaxonNode childNode: nodes){
367
                taxonNode.getChildNodes().remove(childNode);
368
                parent.addChildNode(childNode, null, null);
369
            }
370

    
371
            taxonNode.delete();
372

    
373
        } else if (taxonNode.isTopmostNode()){
374
            success = false;
375
        }
376
        return success;
377
    }
378

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

    
400
                for (int i = 0; i<node.getChildNodes().size(); i++){
401
                    node.removeChild(i);
402
                }
403

    
404

    
405
            }
406

    
407
            removeNodes.add(node);
408
         }
409
        for (int i = 0; i<removeNodes.size(); i++){
410
            TaxonNode removeNode = removeNodes.get(i);
411
            success = removeNode.delete(deleteChildren);
412
            removeNode.setTaxon(null);
413
            removeTaxonNode(removeNode);
414
        }
415
        return success;
416

    
417
    }
418

    
419
    /**
420
     * @param classification
421
     */
422
    public TaxonNode getTaxonNode(Classification classification) {
423
        if (classification == null){
424
            return null;
425
        }
426
        for (TaxonNode node : this.getTaxonNodes()){
427
            if (classification.equals(node.getClassification())){
428
                return node;
429
            }
430
        }
431
        return null;
432
    }
433

    
434

    
435
    /**
436
     * Returns the set of all {@link Synonym synonyms}
437
     * for which <i>this</i> ("accepted/valid") taxon is the accepted taxon.
438
     *
439
     * @see    #addSynonym(Synonym, SynonymType)
440
     * @see    #removeSynonym(Synonym)
441
     */
442
    public Set<Synonym> getSynonyms() {
443
        if(synonyms == null) {
444
            this.synonyms = new HashSet<>();
445
        }
446
        return synonyms;
447
    }
448

    
449

    
450

    
451
    /**
452
     * Returns the set of all {@link TaxonRelationship taxon relationships}
453
     * between two taxa in which <i>this</i> taxon is involved as a source.
454
     *
455
     * @see    #getRelationsToThisTaxon()
456
     * @see    #getTaxonRelations()
457
     */
458
    public Set<TaxonRelationship> getRelationsFromThisTaxon() {
459
        if(relationsFromThisTaxon == null) {
460
            this.relationsFromThisTaxon = new HashSet<>();
461
        }
462
        return relationsFromThisTaxon;
463
    }
464

    
465

    
466
    /**
467
     * Returns the set of all {@link TaxonRelationship taxon relationships}
468
     * between two taxa in which <i>this</i> taxon is involved as a target.
469
     *
470
     * @see    #getRelationsFromThisTaxon()
471
     * @see    #getTaxonRelations()
472
     */
473
    public Set<TaxonRelationship> getRelationsToThisTaxon() {
474
        if(relationsToThisTaxon == null) {
475
            this.relationsToThisTaxon = new HashSet<>();
476
        }
477
        return relationsToThisTaxon;
478
    }
479
    /**
480
     * Returns the set of all {@link TaxonRelationship taxon relationships}
481
     * between two taxa in which <i>this</i> taxon is involved either as a source or
482
     * as a target.
483
     *
484
     * @see    #getRelationsFromThisTaxon()
485
     * @see    #getRelationsToThisTaxon()
486
     */
487
    @Transient
488
    public Set<TaxonRelationship> getTaxonRelations() {
489
        Set<TaxonRelationship> rels = new HashSet<>();
490
        rels.addAll(getRelationsToThisTaxon());
491
        rels.addAll(getRelationsFromThisTaxon());
492
        return rels;
493
    }
494

    
495
    /**
496
     * @see    #getRelationsToThisTaxon()
497
     */
498
    protected void setRelationsToThisTaxon(Set<TaxonRelationship> relationsToThisTaxon) {
499
        this.relationsToThisTaxon = relationsToThisTaxon;
500
    }
501

    
502
    /**
503
     * @see    #getRelationsFromThisTaxon()
504
     */
505
    protected void setRelationsFromThisTaxon(Set<TaxonRelationship> relationsFromThisTaxon) {
506
        this.relationsFromThisTaxon = relationsFromThisTaxon;
507
    }
508

    
509
    /**
510
     * If a relationships between <i>this</i> and the given taxon exists they will be returned.
511
     * <i>This</i> taxon is involved either as a source or as a target in the relationships.
512
     * The method will return <code>null</code> if no relations exist between the two taxa.
513
     *
514
     * @param possiblyRelatedTaxon
515
     * 			a taxon to check for a relationship
516
     * @return
517
     * 			a set of <code>TaxonRelationship</code>s or <code>null</null> if none exists.
518
     */
519
    public Set<TaxonRelationship> getTaxonRelations(Taxon possiblyRelatedTaxon){
520
        Set<TaxonRelationship> relations = new HashSet<>();
521

    
522
        for(TaxonRelationship relationship : getTaxonRelations()){
523
            if(relationship.getFromTaxon().equals(possiblyRelatedTaxon)) {
524
                relations.add(relationship);
525
            }
526
            if(relationship.getToTaxon().equals(possiblyRelatedTaxon)) {
527
                relations.add(relationship);
528
            }
529
        }
530

    
531
        return relations.size() > 0 ? relations : null;
532
    }
533

    
534
    /**
535
     * Removes one {@link TaxonRelationship taxon relationship} from one of both sets of
536
     * {@link #getTaxonRelations() taxon relationships} in which <i>this</i> taxon is involved
537
     * either as a {@link #getRelationsFromThisTaxon() source} or as a {@link #getRelationsToThisTaxon() target}.
538
     * The taxon relationship will also be removed from one of both sets
539
     * belonging to the second taxon involved. Furthermore the inherited RelatedFrom and
540
     * RelatedTo attributes of the given taxon relationship will be nullified.<P>
541
     *
542
     * @param  rel  the taxon relationship which should be removed from one
543
     * 				of both sets
544
     * @see    		#getTaxonRelations()
545
     * @see    		eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom()
546
     * @see    		eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo()
547
     *
548
     */
549
    public void removeTaxonRelation(TaxonRelationship rel) {
550
        this.relationsToThisTaxon.remove(rel);
551
        this.relationsFromThisTaxon.remove(rel);
552
        Taxon fromTaxon = rel.getFromTaxon();
553
        Taxon toTaxon = rel.getToTaxon();
554

    
555
        //delete Relationship from other related Taxon
556
        if (fromTaxon != this){
557
            rel.setToTaxon(null);  //remove this Taxon from relationship
558
            if (fromTaxon != null){
559
                if (fromTaxon.getTaxonRelations().contains(rel)){
560
                    fromTaxon.removeTaxonRelation(rel);
561
                }
562
            }
563
        }
564
        if (toTaxon != this ){
565
            rel.setFromTaxon(null); //remove this Taxon from relationship
566
           if (toTaxon != null){
567
               if (toTaxon.getTaxonRelations().contains(rel)) {
568
                   toTaxon.removeTaxonRelation(rel);
569
               }
570
           }
571
        }
572
    }
573

    
574
    /**
575
     * Adds an existing {@link TaxonRelationship taxon relationship} either to the set of
576
     * {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon} or to the set of
577
     * {@link #getRelationsFromThisTaxon() taxon relationships from <i>this</i> taxon}. If neither the
578
     * source nor the target of the taxon relationship match with <i>this</i> taxon
579
     * no addition will be carried out. The taxon relationship will also be
580
     * added to the second taxon involved in the given relationship.<P>
581
     *
582
     * @param rel  the taxon relationship to be added to one of <i>this</i> taxon's taxon relationships sets
583
     * @see    	   #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
584
     * @see    	   #getTaxonRelations()
585
     * @see    	   #getRelationsFromThisTaxon()
586
     * @see    	   #getRelationsToThisTaxon()
587
     */
588
    public void addTaxonRelation(TaxonRelationship rel) {
589
        if (rel!=null && rel.getType()!=null && !getTaxonRelations().contains(rel) ){
590
            Taxon toTaxon=rel.getToTaxon();
591
            Taxon fromTaxon=rel.getFromTaxon();
592
            if ( this.equals(toTaxon) || this.equals(fromTaxon) ){
593
                if (this.equals(fromTaxon)){
594
                    relationsFromThisTaxon.add(rel);
595
                    // also add relation to other taxon object
596
                    if (toTaxon!=null){
597
                        toTaxon.addTaxonRelation(rel);
598
                    }
599
                }else if (this.equals(toTaxon)){
600
                    relationsToThisTaxon.add(rel);
601
                    // also add relation to other taxon object
602
                    if (fromTaxon!=null){
603
                        fromTaxon.addTaxonRelation(rel);
604
                    }
605
                }
606
            }else if (toTaxon == null || fromTaxon == null){
607
                if (toTaxon == null){
608
                    toTaxon = this;
609
                    relationsToThisTaxon.add(rel);
610
                    if (fromTaxon!= null){
611
                        fromTaxon.addTaxonRelation(rel);
612
                    }
613
                }else if (fromTaxon == null && toTaxon != null){
614
                    fromTaxon = this;
615
                    relationsFromThisTaxon.add(rel);
616
                    toTaxon.addTaxonRelation(rel);
617
                }
618
            }
619
        }
620
    }
621

    
622

    
623
    @Override
624
    @Deprecated //for inner use by RelationshipBase only
625
    public void addRelationship(RelationshipBase rel){
626
        if (rel instanceof TaxonRelationship){
627
            addTaxonRelation((TaxonRelationship)rel);
628
        }else{
629
            throw new ClassCastException("Wrong Relationsship type for Taxon.addRelationship");
630
        }
631
    }
632

    
633
    /**
634
     * Creates a new {@link TaxonRelationship taxon relationship} instance where <i>this</i> taxon
635
     * plays the source role and adds it to the set of
636
     * {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to <i>this</i> taxon.
637
     * The taxon relationship will also be added to the set of taxon
638
     * relationships to the second taxon involved in the created relationship.<P>
639
     *
640
     * @param toTaxon		the taxon which plays the target role in the new taxon relationship
641
     * @param type			the taxon relationship type for the new taxon relationship
642
     * @param citation		the reference source for the new taxon relationship
643
     * @param microcitation	the string with the details describing the exact localisation within the reference
644
     * @return
645
     * @see    	   			#addTaxonRelation(TaxonRelationship)
646
     * @see    	   			#getTaxonRelations()
647
     * @see    	   			#getRelationsFromThisTaxon()
648
     * @see    	   			#getRelationsToThisTaxon()
649
     */
650
    public TaxonRelationship addTaxonRelation(Taxon toTaxon, TaxonRelationshipType type, Reference citation, String microcitation) {
651
        return new TaxonRelationship(this, toTaxon, type, citation, microcitation);
652
    }
653
    /**
654
     * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
655
     * "misapplied name for") instance where <i>this</i> taxon plays the target role
656
     * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
657
     * The taxon relationship will also be added to the set of taxon
658
     * relationships to the other (misapplied name) taxon involved in the created relationship.
659
     *
660
     * @param misappliedNameTaxon	the taxon which plays the source role in the new taxon relationship
661
     * @param citation				the reference source for the new taxon relationship
662
     * @param microcitation			the string with the details describing the exact localisation within the reference
663
     * @return
664
     * @see    	   					#getMisappliedNames()
665
     * @see                         #addProParteMisappliedName(Taxon, Reference, String)
666
     * @see    	   					#addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
667
     * @see    	   					#addTaxonRelation(TaxonRelationship)
668
     * @see    	   					#getTaxonRelations()
669
     * @see    	   					#getRelationsFromThisTaxon()
670
     * @see    	   					#getRelationsToThisTaxon()
671
     */
672
    public TaxonRelationship addMisappliedName(Taxon misappliedNameTaxon, Reference citation, String microcitation) {
673
        return misappliedNameTaxon.addTaxonRelation(this, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), citation, microcitation);
674
    }
675

    
676
//	public void removeMisappliedName(Taxon misappliedNameTaxon){
677
//		Set<TaxonRelationship> taxRels = this.getTaxonRelations();
678
//		for (TaxonRelationship taxRel : taxRels ){
679
//			if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())
680
//				&& taxRel.getFromTaxon().equals(misappliedNameTaxon)){
681
//				this.removeTaxonRelation(taxRel);
682
//			}
683
//		}
684
//	}
685

    
686
    /**
687
     * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
688
     * "pro parte misapplied name for") instance where <i>this</i> taxon plays the target role
689
     * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
690
     * The taxon relationship will also be added to the set of taxon
691
     * relationships to the other (pro parte misapplied name) taxon involved in the created relationship.
692
     *
693
     * @param proParteMisappliedNameTaxon   the taxon which plays the source role in the new taxon relationship
694
     * @param citation              the reference source for the new taxon relationship
695
     * @param microcitation         the string with the details describing the exact localisation within the reference
696
     * @return
697
     * @see                         #addMisappliedName(Taxon, Reference, String)
698
     * @see                         #getMisappliedNames()
699
     * @see                         #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
700
     * @see                         #addTaxonRelation(TaxonRelationship)
701
     * @see                         #getTaxonRelations()
702
     * @see                         #getRelationsFromThisTaxon()
703
     * @see                         #getRelationsToThisTaxon()
704
     */
705
    public TaxonRelationship addProParteMisappliedName(Taxon proParteMisappliedNameTaxon, Reference citation, String microcitation) {
706
        return proParteMisappliedNameTaxon.addTaxonRelation(this, TaxonRelationshipType.PRO_PARTE_MISAPPLIED_NAME_FOR(), citation, microcitation);
707
    }
708

    
709
    /**
710
     * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
711
     * "partial misapplied name for") instance where <i>this</i> taxon plays the target role
712
     * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
713
     * The taxon relationship will also be added to the set of taxon
714
     * relationships to the other (pro parte misapplied name) taxon involved in the created relationship.
715
     *
716
     * @param partialMisappliedNameTaxon   the taxon which plays the source role in the new taxon relationship
717
     * @param citation              the reference source for the new taxon relationship
718
     * @param microcitation         the string with the details describing the exact localization within the reference
719
     * @return
720
     * @see                         #addMisappliedName(Taxon, Reference, String)
721
     * @see                         #addProParteMisappliedName(Taxon, Reference, String)
722
     * @see                         #getMisappliedNames()
723
     * @see                         #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
724
     * @see                         #addTaxonRelation(TaxonRelationship)
725
     * @see                         #getTaxonRelations()
726
     * @see                         #getRelationsFromThisTaxon()
727
     * @see                         #getRelationsToThisTaxon()
728
     */
729
    public TaxonRelationship addPartialMisappliedName(Taxon partialMisappliedNameTaxon, Reference citation, String microcitation) {
730
        return partialMisappliedNameTaxon.addTaxonRelation(this, TaxonRelationshipType.PARTIAL_MISAPPLIED_NAME_FOR(), citation, microcitation);
731
    }
732

    
733
    /**
734
     * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
735
     * "pro parte synonym for") instance where <i>this</i> taxon plays the target role
736
     * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
737
     * The taxon relationship will also be added to the set of taxon
738
     * relationships to the other (pro parte synonym) taxon involved in the created relationship.
739
     *
740
     * @param proParteTaxon         the taxon which plays the source role in 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                         #getMisappliedNames()
745
     * @see                         #addProParteMisappliedName(Taxon, Reference, String)
746
     * @see                         #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
747
     * @see                         #addTaxonRelation(TaxonRelationship)
748
     * @see                         #getTaxonRelations()
749
     * @see                         #getRelationsFromThisTaxon()
750
     * @see                         #getRelationsToThisTaxon()
751
     */
752
    public TaxonRelationship addProparteSynonym(Taxon proParteTaxon, Reference citation, String microcitation) {
753
        return proParteTaxon.addTaxonRelation(this, TaxonRelationshipType.PRO_PARTE_SYNONYM_FOR(), citation, microcitation);
754
    }
755

    
756
    /**
757
     * Creates a new {@link TaxonRelationship taxon relationship} instance with
758
     * {@link TaxonRelationshipType taxon relationship type} {@link TaxonRelationshipType#PARTIAL_SYNONYM_FOR()
759
     * partial synonym for} where <i>this</i> taxon plays the target role
760
     * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
761
     * The taxon relationship will also be added to the set of taxon
762
     * relationships to the other (partial synonym) taxon involved in the created relationship.
763
     *
764
     * @param partialTaxon         the taxon which plays the source role in the new taxon relationship
765
     * @param citation             the reference source for the new taxon relationship
766
     * @param microcitation        the string with the details describing the exact localisation within the reference
767
     * @return
768
     * @see                         #addProparteSynonym(Taxon, Reference, String)
769
     * @see                         #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
770
     * @see                         #addTaxonRelation(TaxonRelationship)
771
     * @see                         #getTaxonRelations()
772
     * @see                         #getRelationsFromThisTaxon()
773
     * @see                         #getRelationsToThisTaxon()
774
     */
775
    public TaxonRelationship addPartialSynonym(Taxon partialTaxon, Reference citation, String microcitation) {
776
        return partialTaxon.addTaxonRelation(this, TaxonRelationshipType.PARTIAL_SYNONYM_FOR(), citation, microcitation);
777
    }
778

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

    
802
    /**
803
     * Returns the boolean value indicating whether <i>this</i> taxon is a misapplication
804
     * (misapplied name) for at least one other taxon.
805
     */
806
    // TODO cache as for #hasTaxonomicChildren
807
    @Transient
808
    public boolean isMisapplication(){
809
        return computeMisapliedNameRelations() > 0;
810
    }
811

    
812
    /**
813
     * Counts the number of misapplied name relationships (including pro parte misapplied
814
     * names) where this taxon represents the
815
     * misapplied name for another taxon.
816
     * @return
817
     */
818
    private int computeMisapliedNameRelations(){
819
        int count = 0;
820
        for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
821
            if (rel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())
822
                    || rel.getType().equals(TaxonRelationshipType.PRO_PARTE_MISAPPLIED_NAME_FOR())){
823
                count++;
824
            }
825
        }
826
        return count;
827
    }
828

    
829
    /**
830
     * Returns the boolean value indicating whether <i>this</i> taxon is a misapplication
831
     * (misapplied name) for at least one other taxon.
832
     */
833
    // TODO cache as for #hasTaxonomicChildren
834
    @Transient
835
    public boolean isProparteSynonym(){
836
        return computeProparteSynonymRelations() > 0;
837
    }
838

    
839
    /**
840
     * Counts the number of misapplied name relationships (including pro parte misapplied
841
     * names) where this taxon represents the
842
     * misapplied name for another taxon.
843
     * @return
844
     */
845
    private int computeProparteSynonymRelations(){
846
        int count = 0;
847
        for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
848
            if (rel.getType().equals(TaxonRelationshipType.PRO_PARTE_SYNONYM_FOR())
849
                    || rel.getType().equals(TaxonRelationshipType.PARTIAL_SYNONYM_FOR())){
850
                count++;
851
            }
852
        }
853
        return count;
854
    }
855

    
856
    /**
857
     * Returns the boolean value indicating whether <i>this</i> taxon is a related
858
     * concept for at least one other taxon.
859
     */
860
    @Transient
861
    public boolean isRelatedConcept(){
862
        return computeConceptRelations() > 0;
863
    }
864

    
865
    /**
866
     * Counts the number of concept relationships where this taxon represents the
867
     * related concept for another taxon.
868
     * @return
869
     */
870
    private int computeConceptRelations(){
871
        int count = 0;
872
        for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
873
            TaxonRelationshipType type = rel.getType();
874
            if (type.isConceptRelationship()){
875
                count++;
876
            }
877
        }
878
        return count;
879
    }
880

    
881
    /**
882
     * Returns the boolean value indicating whether <i>this</i> taxon has at least one
883
     * {@link Synonym synonym} (true) or not (false). If true the {@link #getSynonyms() set of synonyms}
884
     * belonging to <i>this</i> ("accepted/valid") taxon is not empty .
885
     *
886
     * @see  #getSynonyms()
887
     * @see  #getSynonymNames()
888
     * @see  #removeSynonym(Synonym)
889
     */
890
    @Transient
891
    public boolean hasSynonyms(){
892
        return this.getSynonyms().size() > 0;
893
    }
894

    
895

    
896
    /**
897
     * Returns the boolean value indicating whether <i>this</i> taxon is at least
898
     * involved in one {@link #getTaxonRelations() taxon relationship} between
899
     * two taxa (true), either as a source or as a target, or not (false).
900
     *
901
     * @see  #getTaxonRelations()
902
     * @see  #getRelationsToThisTaxon()
903
     * @see  #getRelationsFromThisTaxon()
904
     * @see  #removeTaxonRelation(TaxonRelationship)
905
     * @see  TaxonRelationship
906
     */
907
    public boolean hasTaxonRelationships(){
908
        return this.getTaxonRelations().size() > 0;
909
    }
910

    
911
    /*
912
     * MISAPPLIED NAMES
913
     */
914
    /**
915
     * Returns the set of taxa playing the source role in {@link TaxonRelationship taxon relationships}
916
     * (with {@link TaxonRelationshipType taxon relationship type} "misapplied name for") where
917
     * <i>this</i> taxon plays the target role. A misapplied name is a taxon the
918
     * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} of which has been erroneously used
919
     * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
920
     * as the one meant by <i>this</i> ("accepted/correct") taxon.
921
     *
922
     * @see  #getTaxonRelations()
923
     * @see  #getRelationsToThisTaxon()
924
     * @see  #addMisappliedName(Taxon, Reference, String)
925
     * @param includeNonCongruent if <code>true</code> also those taxa are returned that are related
926
     * via a non congruent relationship like {@link TaxonRelationshipType#PRO_PARTE_MISAPPLIED_NAME_FOR()
927
     * pro parte misapplied name}
928
     */
929
    @Transient
930
    public Set<Taxon> getMisappliedNames(boolean includeNonCongruent){
931
        Set<Taxon> taxa = new HashSet<>();
932
        Set<TaxonRelationship> rels = this.getRelationsToThisTaxon();
933
        for (TaxonRelationship rel: rels){
934
            TaxonRelationshipType relType = rel.getType();
935
            if ( (includeNonCongruent && relType.isAnyMisappliedName())
936
                    || relType.equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
937
                taxa.add(rel.getFromTaxon());
938
            }
939
        }
940
        return taxa;
941
    }
942

    
943
    /**
944
     * Returns the set of misapplied name relationships in which this taxon
945
     * plays the role of the correctly accepted taxon (target). A misapplied name is a taxon the
946
     * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} of which has been erroneously used
947
     * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
948
     * as the one meant by <i>this</i> ("accepted/correct") taxon.
949
     */
950
    @Transient
951
    public Set<TaxonRelationship> getMisappliedNameRelations(){
952
        Set<TaxonRelationship> result = new HashSet<>();
953
        Set<TaxonRelationship> rels = this.getRelationsToThisTaxon();
954
        for (TaxonRelationship rel: rels){
955
            TaxonRelationshipType relType = rel.getType();
956
            if (relType.isAnyMisappliedName()){
957
                result.add(rel);
958
            }
959
        }
960
        return result;
961
    }
962

    
963
    /**
964
     * Returns the set of taxa playing the target role in {@link TaxonRelationship taxon relationships}
965
     * (with {@link TaxonRelationshipType taxon relationship type} "misapplied name for"
966
     * or "pro parte misapplied name for") where
967
     * <i>this</i> taxon plays the source role. A misapplied name is a taxon the
968
     * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} of which has been erroneously used
969
     * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
970
     * as the one meant by <i>this</i> ("accepted/correct") taxon.
971

    
972
     * @param includeNonCongruent if <code>true</code> also those taxa are returned that are related
973
     * via a non congruent relationship like {@link TaxonRelationshipType#PRO_PARTE_MISAPPLIED_NAME_FOR()
974
     * pro parte misapplied name}
975
     *
976
     * @see  #getTaxonRelations()
977
     * @see  #getRelationsToThisTaxon()
978
     * @see  #addMisappliedName(Taxon, Reference, String)
979
     * @see  #addProParteMisappliedName(Taxon, Reference, String)
980
     */
981
    @Transient
982
    public Set<Taxon> getTaxaForMisappliedName(boolean includeNonCongruent){
983
        Set<Taxon> taxa = new HashSet<>();
984
        Set<TaxonRelationship> rels = this.getRelationsFromThisTaxon();
985
        for (TaxonRelationship rel: rels){
986
            TaxonRelationshipType relType = rel.getType();
987
            if ( (includeNonCongruent && relType.isAnyMisappliedName())
988
                    || relType.equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
989
                taxa.add(rel.getToTaxon());
990
            }
991
        }
992
        return taxa;
993
    }
994

    
995
//***************************** Synonyms ******************************************************/
996

    
997
//    /**
998
//     * Returns the set of taxa playing the source role in {@link TaxonRelationship taxon relationships}
999
//     * (with {@link TaxonRelationshipType taxon relationship type} "Pro Parte Synonym for") where
1000
//     * <i>this</i> taxon plays the target role.
1001
//     *
1002
//     * @see  #getTaxonRelations()
1003
//     * @see  #getRelationsToThisTaxon()
1004
//
1005
//     */
1006
//    @Transient
1007
//    public Set<Taxon> getProParteSynonyms(){
1008
//        Set<Taxon> taxa = new HashSet<>();
1009
//        Set<TaxonRelationship> rels = this.getRelationsToThisTaxon();
1010
//        for (TaxonRelationship rel: rels){
1011
//            TaxonRelationshipType relType = rel.getType();
1012
//            if ( relType.equals(TaxonRelationshipType.PRO_PARTE_SYNONYM_FOR())){
1013
//                taxa.add(rel.getFromTaxon());
1014
//            }
1015
//        }
1016
//        return taxa;
1017
//    }
1018

    
1019

    
1020

    
1021
    /**
1022
     * Returns the set of pro parte or partial synonym relationships in which this taxon
1023
     * plays the role of the "correctly" accepted taxon (target).
1024
     *
1025
     * @see #getProParteAndPartialSynonyms()
1026
     * @see #getMisappliedNameRelations()
1027
     */
1028
    @Transient
1029
    public Set<TaxonRelationship> getProParteAndPartialSynonymRelations(){
1030
        Set<TaxonRelationship> result = new HashSet<>();
1031
        Set<TaxonRelationship> rels = this.getRelationsToThisTaxon();
1032
        for (TaxonRelationship rel: rels){
1033
            TaxonRelationshipType relType = rel.getType();
1034
            if (relType.isAnySynonym()){
1035
                result.add(rel);
1036
            }
1037
        }
1038
        return result;
1039
    }
1040

    
1041
    /**
1042
     * Returns the set of pro parte or partial synonyms in which this taxon
1043
     * plays the role of the "correctly" accepted taxon (target).
1044
     *
1045
     * @see #getProParteAndPartialSynonymRelations()
1046
     * @see #getMisappliedNames(boolean)
1047
     */
1048
    @Transient
1049
    public Set<Taxon> getProParteAndPartialSynonyms(){
1050
        Set<Taxon> synonyms = new HashSet<>();
1051
        Set<TaxonRelationship> rels = this.getProParteAndPartialSynonymRelations();
1052
        for (TaxonRelationship rel: rels){
1053
            synonyms.add(rel.getFromTaxon());
1054
        }
1055
        return synonyms;
1056
    }
1057

    
1058
    /**
1059
     * Returns the set of all {@link TaxonName taxon names} used as {@link Synonym synonyms}
1060
     * of <i>this</i> ("accepted/valid") taxon.
1061
     *
1062
     * @see    #getSynonyms()
1063
     * @see    #getSynonymsSortedByType()
1064
     * @see    #addSynonymName(TaxonName, SynonymType)
1065
     * @see    #addSynonym(Synonym, SynonymType, Reference, String)
1066
     * @see    #removeSynonym(Synonym)
1067
     */
1068
    @Transient
1069
    public Set<TaxonName> getSynonymNames(){
1070
        Set<TaxonName> names = new HashSet<>();
1071
        for (Synonym syn: this.getSynonyms()){
1072
            names.add(syn.getName());
1073
        }
1074
        return names;
1075
    }
1076

    
1077
    /**
1078
     * Might be public in future. For the moment protected to ensure that
1079
     * synonym type is always set after refactoring.
1080
     *
1081
     * @param synonym
1082
     */
1083
    protected void addSynonym(Synonym synonym){
1084
        if (! this.equals(synonym.getAcceptedTaxon())){
1085
            synonym.setAcceptedTaxon(this);
1086
        }
1087
        if (!synonyms.contains(synonym)){
1088
            synonyms.add(synonym);
1089
        }
1090
    }
1091

    
1092
    /**
1093
     * Adds the given {@link Synonym synonym} to <code>this</code> taxon
1094
     * and changes the {@link SynonymType
1095
     * synonym type} before.
1096
     *
1097
     * @param synonym       the synonym to be added
1098
     * @param synonymType   the synonym type of the synonym to be added. If not <code>null</code>
1099
     *                      and if the synonym already has a type the existing type will be overwritten.<BR>
1100
     *                      If synonymType is {@link SynonymType#HOMOTYPIC_SYNONYM_OF()}
1101
     *                      the homotypic group of the synonym is changed to that of <code>this</code> taxon.<BR>
1102
     *                      To explicitly set the type to <code>null</code> use {@link Synonym#setType(SynonymType)}
1103
     * @see                 #addSynonym(Synonym)
1104
     * @see                 #addSynonym(Synonym, SynonymType, Reference, String)
1105
     * @see                 #addSynonymName(TaxonName, SynonymType)
1106
     * @see                 #addSynonymName(TaxonName, SynonymType, Reference, String)
1107
     * @see                 #addHomotypicSynonymName(TaxonName, Reference, String)
1108
     * @see                 #addHeterotypicSynonymName(TaxonName)
1109
     * @see                 #addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1110
     * @see                 #getSynonyms()
1111
     * @see                 #removeSynonym(Synonym)
1112
     * @see                 Synonym#getAcceptedTaxon()
1113
     */
1114
    public void addSynonym(Synonym synonym, SynonymType synonymType){
1115
        synonym.setType(synonymType); //must be set before as otherwise merging of homotypical groups may not work correctly in Synonym.checkHomotypic()
1116
        addSynonym(synonym);
1117
    }
1118

    
1119
    /**
1120
     * Adds the given {@link Synonym synonym} with the given {@link SynonymType
1121
     * synonym relationship type}
1122
     *
1123
     * @param synonym		the synonym to be added
1124
     * @param synonymType	the synonym  type of the synonym to be added. If not null
1125
     *                      and if the synonym already has a type the existing type will be overwritten.
1126
//     * @param citation		the reference source for the new synonym relationship
1127
//     * @param microcitation	the string with the details describing the exact localization within the reference
1128
     * @see    	   			#addSynonym(Synonym)
1129
     * @see    	   			#addSynonym(Synonym, SynonymType, Reference, String)
1130
     * @see    	   			#addSynonymName(TaxonName, SynonymType)
1131
     * @see    	   			#addSynonymName(TaxonName, SynonymType, Reference, String)
1132
     * @see    	   			#addHomotypicSynonymName(TaxonName, Reference, String)
1133
     * @see    	   			#addHeterotypicSynonymName(TaxonName)
1134
     * @see    	   			#addHeterotypicSynonymName(TaxonName, HomotypicalGroup, Reference, String)
1135
     * @see    	   			#getSynonyms()
1136
     * @see    				#removeSynonym(Synonym)
1137
     * @see    	   			Synonym#getAcceptedTaxon()
1138
     */
1139
    private void addSynonym(Synonym synonym, SynonymType synonymType, Reference newSecReference, String newSecMicroReference){
1140
        if (newSecReference != null){
1141
            synonym.setSec(newSecReference);
1142
        }
1143
        if (newSecMicroReference != null){
1144
            synonym.setSecMicroReference(newSecMicroReference);
1145
        }
1146
        addSynonym(synonym, synonymType);
1147
        return;
1148
    }
1149

    
1150
    /**
1151
     * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the
1152
     * given {@link TaxonName synonym name} and with the given
1153
     * {@link SynonymType synonym type}. If the later is
1154
     * {@link SynonymType#HOMOTYPIC_SYNONYM_OF() homotypic synonym}
1155
     * the name will be added to the same {@link HomotypicalGroup homotypical group}
1156
     * as the <code>this</code> accepted taxon.<BR>
1157
     * The secundum reference of the new synonym is taken from <code>this</code> taxon.
1158
     * A secundum detail is not set.
1159
     *
1160
     * @param synonymName	the taxon name to be used as a synonym to be added
1161
     * 						to <i>this</i> taxon's set of synonyms
1162
     * @param synonymType	the synonym  type of the synonym
1163
     * 						relationship to be added
1164
     * @return 				the created synonym
1165
     * @see    	   			#addSynonymName(TaxonName, SynonymType, Reference, String)
1166
     * @see    	   			#addSynonym(Synonym, SynonymType)
1167
     * @see    	   			#addSynonym(Synonym, SynonymType, Reference, String)
1168
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1169
     * @see    	   			#addHomotypicSynonymName(TaxonName, Reference, String)
1170
     * @see    	   			#addHeterotypicSynonymName(TaxonName)
1171
     * @see    	   			#addHeterotypicSynonymName(TaxonName, HomotypicalGroup, Reference, String)
1172
     * @see    	   			#getSynonyms()
1173
     * @see    				#removeSynonym(Synonym)
1174
     */
1175
    public Synonym addSynonymName(TaxonName synonymName, SynonymType synonymType){
1176
        return addSynonymName(synonymName, null, null, synonymType);
1177
    }
1178
    /**
1179
     * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the
1180
     * given {@link TaxonName synonym name} and with the given
1181
     * {@link SynonymType synonym type}. If the later is
1182
     * {@link SynonymType#HOMOTYPIC_SYNONYM_OF() homotypic synonym}
1183
     * the name will be added to the same {@link HomotypicalGroup homotypical group}
1184
     * as the <code>this</code> accepted taxon.<BR>
1185
     *
1186
     * If secReference is not <code>null</code>, the new synonym will have this as
1187
     * secundum reference. Otherwise <code>this</code> taxons sec reference is taken
1188
     * as secundum reference for the synonym. SecDetail will be the secMicroReference of the
1189
     * new synonym.<BR>
1190
     *
1191
     * @param synonymName	the taxon name to be used as a synonym to be added
1192
     * 						to <i>this</i> taxon's set of synonyms
1193
     * @param secReference	the secundum reference for the new synonym (if <code>null</code>
1194
     *                      <code>this</code> taxon's secundum reference is taken.
1195
     * @param secMicroReference the secundum micro reference of the new synonym
1196
     * @param synonymType	the synonym type of the synonym to be added
1197
     *
1198
     * @see    	   			#addSynonymName(TaxonName, SynonymType, Reference, String)
1199
     * @see    	   			#addSynonym(Synonym, SynonymType)
1200
     * @see    	   			#addSynonym(Synonym, SynonymType, Reference, String)
1201
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1202
     * @see    	   			#addHomotypicSynonymName(TaxonName, Reference, String)
1203
     * @see    	   			#addHeterotypicSynonymName(TaxonName)
1204
     * @see    	   			#addHeterotypicSynonymName(TaxonName, HomotypicalGroup, Reference, String)
1205
     * @see    	   			#getSynonyms()
1206
     * @see    				#removeSynonym(Synonym)
1207
     */
1208
    public Synonym addSynonymName(TaxonName synonymName, Reference secReference, String secMicroReference, SynonymType synonymType){
1209
        Synonym synonym = Synonym.NewInstance(synonymName, this.getSec()); //default sec
1210
        addSynonym(synonym, synonymType, secReference, secMicroReference);
1211
        return synonym;
1212
    }
1213

    
1214
    /**
1215
     * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the given
1216
     * {@link TaxonName synonym name}. The synonym will have the synonym type
1217
     * {@link SynonymType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of"}.<BR>
1218
     * The secundum reference is taken from <code>this</code> taxon.
1219
     * No secMicroReference will be set for the new synonym.<BR>
1220
     * The synonym will keep it's old homotypical group.<BR>
1221
     *
1222
     * @param synonymName	the taxon name to be used as an heterotypic synonym
1223
     * 						to be added to <i>this</i> taxon's set of synonyms
1224
     * @return 				the created synonym
1225
     * @see    	   			#addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1226
     * @see    	   			#addSynonymName(TaxonName, SynonymType)
1227
     * @see    	   			#addSynonymName(TaxonName, SynonymType, Reference, String)
1228
     * @see    	   			#addSynonym(Synonym, SynonymType)
1229
     * @see    	   			#addSynonym(Synonym, SynonymType, Reference, String)
1230
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1231
     * @see    	   			#addHomotypicSynonymName(TaxonName, Reference, String)
1232
     * @see    	   			#getSynonyms()
1233
     * @see    				#removeSynonym(Synonym)
1234
     */
1235
    public Synonym addHeterotypicSynonymName(TaxonName synonymName){
1236
        return addHeterotypicSynonymName(synonymName, null, null, null);
1237
    }
1238

    
1239
    /**
1240
     * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the given
1241
     * {@link TaxonName synonym name}. The synonym will have the synonym type
1242
     * {@link SynonymType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of"}.<BR>
1243
     *
1244
     * If secReference is not <code>null</code>, the new synonym will have this as
1245
     * secundum reference. Otherwise <code>this</code> taxons sec reference is taken
1246
     * as secundum reference for the synonym. SecDetail will be the secMicroReference of the
1247
     * new synonym.<BR>
1248
     * Furthermore the taxon name used as synonym will be added
1249
     * to the given {@link name.HomotypicalGroup homotypical group} (if not <code>null</code>).<BR>
1250
     *
1251
     * @param synonymName		the taxon name to be used as an heterotypic synonym
1252
     * 							to be added to <i>this</i> taxon's set of synonyms
1253
     * @param secReference		the secundum reference for the new synonym
1254
     * @param secDetail		    the secundum detil for the new synonym
1255
     * @param homotypicalGroup	the homotypical group to which the taxon name
1256
     * 							of the synonym will be added. If <code>null</code>
1257
     *                          the homotypical group of synonymName is not changed
1258
     * @return 					the created synonym
1259
     * @see    	   				#addHeterotypicSynonymName(TaxonName)
1260
     * @see    	   				#addSynonymName(TaxonName, SynonymType, Reference, String)
1261
     * @see    	   				#addSynonymName(TaxonName, SynonymType)
1262
     * @see    	   				#addSynonym(Synonym, SynonymType)
1263
     * @see    	   				#addSynonym(Synonym, SynonymType, Reference, String)
1264
     * @see    	   				#addHomotypicSynonym(Synonym, Reference, String)
1265
     * @see    	   				#addHomotypicSynonymName(TaxonName, Reference, String)
1266
     * @see    	   				#getSynonyms()
1267
     * @see    					#removeSynonym(Synonym)
1268
     */
1269
    public Synonym addHeterotypicSynonymName(TaxonName synonymName, Reference secReference, String secDetail, HomotypicalGroup homotypicalGroup){
1270
        Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
1271
        if (homotypicalGroup != null){
1272
            homotypicalGroup.addTypifiedName(synonymName);
1273
        }
1274
        addSynonym(synonym, SynonymType.HETEROTYPIC_SYNONYM_OF(), secReference, secDetail);
1275
        return synonym;
1276
    }
1277

    
1278
    /**
1279
    * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the given
1280
     * {@link TaxonName synonym name}. The synonym will have the synonym type
1281
     * {@link SynonymType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of"}.<BR>
1282
     * The secundum reference is taken from <code>this</code> taxon.
1283
     * No secMicroReference will be set for the new synonym.<BR>
1284
     * The synonym's homotypic group will be changed to <code>this</code> taxon's group.<BR>
1285
     *
1286
     * @param synonymName	the taxon name to be used as an homotypic synonym
1287
     * 						to be added to <i>this</i> taxon's set of synonyms
1288
     * @return 				the created synonym
1289
     * @see    	   			#addHomotypicSynonym(Synonym, Reference, String)
1290
     * @see    	   			#addSynonymName(TaxonName, SynonymType, Reference, String)
1291
     * @see    	   			#addSynonymName(TaxonName, SynonymType)
1292
     * @see    	   			#addSynonym(Synonym, SynonymType)
1293
     * @see    	   			#addSynonym(Synonym, SynonymType, Reference, String)
1294
     * @see    	   			#addHeterotypicSynonymName(TaxonName)
1295
     * @see    	   			#addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1296
     * @see    	   			#getSynonyms()
1297
     * @see    				#removeSynonym(Synonym)
1298
     */
1299
    public Synonym addHomotypicSynonymName(TaxonName synonymName){
1300
        Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
1301
        addHomotypicSynonym(synonym);
1302
        return synonym;
1303
    }
1304

    
1305
    /**
1306
     * Adds the given {@link Synonym synonym} to <code>this</code> taxon,
1307
     * with the {@link SynonymType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of"
1308
     * relationship type} and returns it.
1309
     * Furthermore the {@link TaxonName taxon name}
1310
     * used as synonym will be added to the same {@link HomotypicalGroup homotypic group}
1311
     * to which the taxon name of <i>this</i> taxon belongs.<BR>
1312
     *
1313
     * @param synonym		the synonym added to <i>this</i> taxon's synonym set
1314
     * @see    	   			#addHomotypicSynonymName(TaxonName, Reference, String)
1315
     * @see    	   			#addSynonym(Synonym, SynonymType)
1316
     * @see    	   			#addSynonym(Synonym, SynonymType, Reference, String)
1317
     * @see    	   			#addSynonymName(TaxonName, SynonymType, Reference, String)
1318
     * @see    	   			#addSynonymName(TaxonName, SynonymType)
1319
     * @see    	   			#addHeterotypicSynonymName(TaxonName)
1320
     * @see    	   			#addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1321
     * @see    	   			#getSynonyms()
1322
     * @see    				#removeSynonym(Synonym)
1323
     */
1324
    public void addHomotypicSynonym(Synonym synonym){
1325
    	if (!this.getSynonyms().contains(synonym)){
1326
    		addSynonym(synonym, SynonymType.HOMOTYPIC_SYNONYM_OF());
1327
    	} else{
1328
    		logger.warn("Tried to add a synonym to an accepted taxon that already is a synonym of this taxon.");
1329
    	}
1330
        return;
1331
    }
1332

    
1333
    /**
1334
     * Like {@link #removeSynonym(Synonym, boolean)} with <code>removeSynonymNameFromHomotypicalGroup</code> set to true.
1335
     * @see #removeSynonym(Synonym, boolean)
1336
     */
1337
    public void removeSynonym(Synonym synonym){
1338
        removeSynonym(synonym, true);
1339
    }
1340

    
1341

    
1342
    /**
1343
     * Removes one element from the set of {@link Synonym synonyms} assigned
1344
     * to <i>this</i> (accepted/valid) taxon.
1345
     *
1346
     * @param synonym  the synonym to be removed
1347
     * @param removeSynonymNameFromHomotypicalGroup
1348
     *              if <code>true</code> the synonym name will also be deleted from its homotypical group if the
1349
     *              group contains other names
1350
     * @see     #getSynonyms()
1351
     * @see     #removeSynonym(Synonym)
1352
     */
1353
    public void removeSynonym(Synonym synonym, boolean removeSynonymNameFromHomotypicalGroup) {
1354
        if (synonym != null && this.equals(synonym.getAcceptedTaxon())){
1355
            if(removeSynonymNameFromHomotypicalGroup){
1356
                HomotypicalGroup synHG = synonym.getName().getHomotypicalGroup();
1357
                if (synHG.getTypifiedNames().size() > 1){
1358
                    synHG.removeTypifiedName(synonym.getName(), false);
1359
                }
1360
            }
1361
            this.synonyms.remove(synonym);
1362
            synonym.setAcceptedTaxon(null);
1363
        }
1364
    }
1365

    
1366
    /**
1367
     * @see #getHomotypicSynonymsByHomotypicGroup(TaxonComparator)
1368
     */
1369
    @Transient
1370
    public List<Synonym> getHomotypicSynonymsByHomotypicGroup(){
1371
        return getHomotypicSynonymsByHomotypicGroup(null);
1372
    }
1373

    
1374
    /**
1375
     * Retrieves the ordered list (depending on the date of publication) of
1376
     * homotypic {@link Synonym synonyms} (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1377
     * as for <i>this</i> taxon) under the condition that the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon names}
1378
     * of these synonyms and the taxon name of <i>this</i> taxon belong to the
1379
     * same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group}.
1380
     *
1381
     * @param       comparator the taxon comparator to use, if <code>null</code> the default comparator is taken.
1382
     * @return      the ordered list of homotypic synonyms
1383
     * @see         #getHomotypicSynonymsByHomotypicSynonymType()
1384
     * @see         #getSynonyms()
1385
     * @see         #getHomotypicSynonymyGroups()
1386
     * @see         eu.etaxonomy.cdm.model.name.HomotypicalGroup
1387
     * @see         eu.etaxonomy.cdm.model.name.HomotypicalGroup#getSynonymsInGroup(Reference)
1388
     */
1389
    @Transient
1390
    public List<Synonym> getHomotypicSynonymsByHomotypicGroup(TaxonComparator comparator){
1391
        if (this.getHomotypicGroup() == null){
1392
            return null;
1393
        }else if (comparator == null){
1394
            return this.getSynonymsInGroup(this.getHomotypicGroup());
1395
        }else{
1396
            return this.getSynonymsInGroup(this.getHomotypicGroup(), comparator);
1397
        }
1398
    }
1399

    
1400
    /**
1401
     * Retrieves the list of homotypic {@link Synonym synonyms}
1402
     * (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1403
     * as for <i>this</i> taxon) under the condition that these synonyms and
1404
     * <i>this</i> taxon are involved in {@link SynonymRelationship synonym relationships} with an
1405
     * "is homotypic synonym of" {@link SynonymType#HOMOTYPIC_SYNONYM_OF() synonym relationship type}.
1406
     *
1407
     * @return		the ordered list of homotypic synonyms
1408
     * @see			#getHomotypicSynonymsByHomotypicGroup()
1409
     * @see			#getSynonyms()
1410
     * @see			#getHomotypicSynonymyGroups()
1411
     * @see			SynonymType
1412
     * @deprecated as the method currently returns data not matching the original description of the method
1413
     * as an ordered list (according to date of publication) of synonyms with same secundum as <i>this</i> taxon.
1414
     * In future this method will either be removed or semantics may change.
1415
     */
1416
    @Deprecated
1417
    @Transient
1418
    public List<Synonym> getHomotypicSynonymsByHomotypicSynonymType(){
1419
        Set<Synonym> synonyms = this.getSynonyms();
1420
        List<Synonym> result = new ArrayList<>();
1421
        for(Synonym synonym : synonyms) {
1422
            if(synonym.getType().equals(SynonymType.HOMOTYPIC_SYNONYM_OF())){
1423
                result.add(synonym);
1424
            }
1425
        }
1426
        return result;
1427
    }
1428

    
1429
    /**
1430
     * Returns the ordered list of all {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups} {@link Synonym synonyms} of
1431
     * <i>this</i> taxon belong to. {@link eu.etaxonomy.cdm.model.name.TaxonName Taxon names} of homotypic synonyms
1432
     * belong to the same homotypical group as the taxon name of <i>this</i>
1433
     * taxon. Taxon names of heterotypic synonyms belong to at least one other
1434
     * homotypical group. <BR>
1435
     * The list returned is ordered according to the date of publication of the
1436
     * first published name within each homotypical group.
1437
     *
1438
     * @see			#getHeterotypicSynonymyGroups()
1439
     * @see			#getSynonyms()
1440
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1441
     */
1442
    @Transient
1443
    public List<HomotypicalGroup> getHomotypicSynonymyGroups(){
1444
        List<HomotypicalGroup> result = new ArrayList<>();
1445
        HomotypicalGroup myGroup = this.getHomotypicGroup();
1446
        if (myGroup != null){  //if taxon has no name HG might be null
1447
            result.add(myGroup);
1448
        }
1449
        for (TaxonName taxonName :this.getSynonymNames()){
1450
            if (taxonName != null) {
1451
                if (!result.contains(taxonName.getHomotypicalGroup())){
1452
                    result.add(taxonName.getHomotypicalGroup());
1453
                }
1454
            }
1455
        }
1456
        return result;
1457
    }
1458

    
1459
    /**
1460
     * {@inheritDoc}.
1461
     * <BR>Also returns <code>false</code> if it is a misapplied name or has a similar concept relationship that
1462
     * is similar to synonym relationship (shows up in the synonymy of applications)
1463
     */
1464
    @Override
1465
    @Transient
1466
    public boolean isOrphaned() {
1467

    
1468
        if(taxonNodes == null || taxonNodes.isEmpty()) {
1469
            if(getRelationsFromThisTaxon().isEmpty()) {
1470
                return true;
1471
            }else{
1472
                for (TaxonRelationship rel : getRelationsFromThisTaxon()){
1473
                    if (rel.getType() != null && ! rel.getType().isConceptRelationship()){
1474
                        return false;  //a synonym relationship type similar relationship exists => not orphaned
1475
                    }
1476
                }
1477
                return true;  //all relations are real concept relations and therefore not relevant
1478
            }
1479
        }else{
1480
            return false;
1481
        }
1482
    }
1483

    
1484
    /**
1485
     * Returns the ordered list of all
1486
     * {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups}
1487
     * that contain {@link Synonym synonyms} that are heterotypic to <i>this</i> taxon.<BR>
1488
     *
1489
     * {@link eu.etaxonomy.cdm.model.name.TaxonName Taxon names} of heterotypic synonyms
1490
     * belong to a homotypical group which cannot be the homotypical group to which the
1491
     * taxon name of <i>this</i> taxon belongs.
1492
     * This method returns the same
1493
     * list as the {@link #getHomotypicSynonymyGroups() getHomotypicSynonymyGroups} method
1494
     * but without the homotypical group to which the taxon name of <i>this</i> taxon
1495
     * belongs.<BR>
1496
     * The list returned is <B>ordered</B> according to the rules defined for
1497
     * the {@link HomotypicGroupTaxonComparator} which includes 1) grouping of
1498
     * basionym groups, 2) replaced synonym relationships, 3) publication date,
1499
     * 4) ranks and 5) alphabetical order.
1500
     *
1501
     * @see			#getHeterotypicSynonymyGroups()
1502
     * @see			#getSynonyms()
1503
     * @see			SynonymType#HETEROTYPIC_SYNONYM_OF()
1504
     * @see			eu.etaxonomy.cdm.model.name.HomotypicalGroup
1505
     */
1506
    @Transient
1507
    public List<HomotypicalGroup> getHeterotypicSynonymyGroups(){
1508
        List<HomotypicalGroup> list = getHomotypicSynonymyGroups();
1509
        //remove homotypic group
1510
        list.remove(this.getHomotypicGroup());
1511
        //sort
1512
        Map<Synonym, HomotypicalGroup> map = new HashMap<>();
1513
        for (HomotypicalGroup homotypicalGroup: list){
1514
            List<Synonym> synonymList = getSynonymsInGroup(homotypicalGroup);
1515
            if (synonymList.size() > 0){
1516
                //select the first synonym in the group
1517
                map.put(synonymList.get(0), homotypicalGroup);
1518
            }
1519
        }
1520
        List<Synonym> keyList = new ArrayList<>();
1521
        keyList.addAll(map.keySet());
1522
        //order by first synonym
1523
        Collections.sort(keyList, defaultTaxonComparator);
1524

    
1525
        List<HomotypicalGroup> result = new ArrayList<>();
1526
        for(Synonym synonym: keyList){
1527
            //"replace" synonyms by homotypic groups
1528
            result.add(map.get(synonym));
1529
        }
1530
        //sort end
1531
        return result;
1532
    }
1533

    
1534
    /**
1535
     * Retrieves the ordered list (depending on the rules defined for
1536
     * the {@link HomotypicGroupTaxonComparator}) of
1537
     * {@link taxon.Synonym synonyms} (according to a given reference)
1538
     * the {@link TaxonName taxon names} of which belong to the homotypical group.
1539
     * If other names are part of the group that are not considered synonyms of
1540
     * <i>this</i> taxon, then they will not be included in
1541
     * the result set.
1542
     *
1543
     * @param homotypicGroup
1544
     * @see          #getHeterotypicSynonymyGroups()
1545
     * @see			TaxonName#getSynonyms()
1546
     * @see			TaxonName#getTaxa()
1547
     * @see			taxon.Synonym
1548
     */
1549
    @Transient
1550
    public List<Synonym> getSynonymsInGroup(HomotypicalGroup homotypicGroup){
1551
        return getSynonymsInGroup(homotypicGroup, new HomotypicGroupTaxonComparator(this));
1552
    }
1553

    
1554
    /**
1555
     * @param homotypicGroup
1556
     * @param comparator
1557
     * @return
1558
     * @see     #getSynonymsInGroup(HomotypicalGroup)
1559
     * @see     #getHeterotypicSynonymyGroups()
1560
     */
1561
    @Transient
1562
    public List<Synonym> getSynonymsInGroup(HomotypicalGroup homotypicGroup, TaxonComparator comparator){
1563
        List<Synonym> result = new ArrayList<>();
1564
        if (homotypicGroup == null){
1565
            return result;  //always empty
1566
        }
1567

    
1568
        for (Synonym synonym : this.getSynonyms()){
1569
            if (homotypicGroup.equals(synonym.getHomotypicGroup())){
1570
                result.add(synonym);
1571
            }
1572
        }
1573

    
1574
        Collections.sort(result, comparator);
1575
        return result;
1576
    }
1577

    
1578

    
1579
    /**
1580
     * Returns the image gallery description. If no image gallery exists, a new one is created using the
1581
     * defined title and adds the string "-Image Gallery" to the title.</BR>
1582
     * If multiple image galleries exist an arbitrary one is choosen.
1583
     * @param title
1584
     * @return
1585
     */
1586
    public TaxonDescription getOrCreateImageGallery(String title){
1587
        return getOrCreateImageGallery(title, true, false);
1588
    }
1589

    
1590
    /**
1591
     * Returns the image gallery description. If no image gallery exists, a new one is created using the
1592
     * defined title.</BR>
1593
     * If onlyTitle == true we look only for an image gallery with this title, create a new one otherwise.
1594
     * If multiple image galleries exist that match the conditions an arbitrary one is choosen.
1595
     * @param title
1596
     * @param onlyTitle
1597
     * @param if true, the String "Image Gallery
1598
     * @return
1599
     */
1600
    @Transient
1601
    public TaxonDescription getOrCreateImageGallery(String title, boolean addImageGalleryToTitle, boolean onlyTitle){
1602
        TaxonDescription result = null;
1603
        String titleCache = (title == null) ? "Image Gallery" : title;
1604
        if (title != null && addImageGalleryToTitle){
1605
            titleCache = titleCache+ "-Image Gallery";
1606
        }
1607
        Set<TaxonDescription> descriptionSet = this.getDescriptions();
1608
        for (TaxonDescription desc: descriptionSet){
1609
            if (desc.isImageGallery()){
1610
                if (onlyTitle && ! titleCache.equals(desc.getTitleCache())){
1611
                    continue;
1612
                }
1613
                result = desc;
1614
                if (onlyTitle && titleCache.equals(desc.getTitleCache())){
1615
                    break;
1616
                }
1617
            }
1618
        }
1619
        if (result == null){
1620
            result = TaxonDescription.NewInstance();
1621
            result.setTitleCache(titleCache, true);
1622
            this.addDescription(result);
1623
            result.setImageGallery(true);
1624
        }
1625
        return result;
1626
    }
1627
    //*********************** CLONE ********************************************************/
1628

    
1629

    
1630
    /**
1631
     * Clones <i>this</i> taxon. This is a shortcut that enables to create
1632
     * a new instance that differs only slightly from <i>this</i> taxon by
1633
     * modifying only some of the attributes.<BR><BR>
1634
     * The TaxonNodes are not cloned, the list is empty.<BR>
1635
     * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1636
     * The taxon relationships and synonym relationships are cloned <BR>
1637
     *
1638
     * @see eu.etaxonomy.cdm.model.taxon.TaxonBase#clone()
1639
     * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1640
     * @see java.lang.Object#clone()
1641
     */
1642
    @Override
1643
    public Object clone() {
1644
        Taxon result;
1645
        result = (Taxon)super.clone();
1646

    
1647
        result.setRelationsFromThisTaxon(new HashSet<>());
1648

    
1649
        for (TaxonRelationship fromRelationship : this.getRelationsFromThisTaxon()){
1650
            TaxonRelationship newRelationship = (TaxonRelationship)fromRelationship.clone();
1651
            newRelationship.setRelatedFrom(result);
1652
            result.relationsFromThisTaxon.add(newRelationship);
1653
        }
1654

    
1655
        result.setRelationsToThisTaxon(new HashSet<>());
1656
        for (TaxonRelationship toRelationship : this.getRelationsToThisTaxon()){
1657
            TaxonRelationship newRelationship = (TaxonRelationship)toRelationship.clone();
1658
            newRelationship.setRelatedTo(result);
1659
            result.relationsToThisTaxon.add(newRelationship);
1660
        }
1661

    
1662
        //clone synonyms (is this wanted or should we remove synonyms
1663
        result.synonyms = new HashSet<>();
1664
        for (Synonym synonym : this.getSynonyms()){
1665
            Synonym newSyn = (Synonym)synonym.clone();
1666
            newSyn.setAcceptedTaxon(result);
1667
        }
1668

    
1669
        result.descriptions = new HashSet<>();
1670
        for (TaxonDescription description : this.getDescriptions()){
1671
            TaxonDescription newDescription = (TaxonDescription)description.clone();
1672
            result.addDescription(newDescription);
1673
        }
1674

    
1675

    
1676
        result.taxonNodes = new HashSet<>();
1677

    
1678
        /*for (TaxonNode taxonNode : this.getTaxonNodes()){
1679
            TaxonNode newTaxonNode = (TaxonNode)taxonNode.clone();
1680
            newTaxonNode.setTaxon(result);
1681
            result.addTaxonNode(newTaxonNode);
1682
        }*/
1683

    
1684
        return result;
1685

    
1686
    }
1687

    
1688
    public void clearDescriptions() {
1689
		this.descriptions = new HashSet<>();
1690
	}
1691

    
1692

    
1693
}
(9-9/21)