Project

General

Profile

Download (75 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.name;
11

    
12
import java.lang.reflect.Method;
13
import java.util.HashSet;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.Set;
17

    
18
import javax.persistence.Column;
19
import javax.persistence.Entity;
20
import javax.persistence.FetchType;
21
import javax.persistence.Inheritance;
22
import javax.persistence.InheritanceType;
23
import javax.persistence.JoinTable;
24
import javax.persistence.ManyToMany;
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.commons.lang.StringUtils;
41
import org.apache.log4j.Logger;
42
import org.hibernate.annotations.Cascade;
43
import org.hibernate.annotations.CascadeType;
44
import org.hibernate.annotations.Index;
45
import org.hibernate.annotations.Table;
46
import org.hibernate.envers.Audited;
47
import org.hibernate.search.annotations.Field;
48
import org.hibernate.search.annotations.IndexedEmbedded;
49
import org.hibernate.validator.constraints.NotEmpty;
50
import org.springframework.util.ReflectionUtils;
51

    
52
import eu.etaxonomy.cdm.model.common.IParsable;
53
import eu.etaxonomy.cdm.model.common.IRelated;
54
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
55
import eu.etaxonomy.cdm.model.common.RelationshipBase;
56
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
57
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
58
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
59
import eu.etaxonomy.cdm.model.reference.Reference;
60
import eu.etaxonomy.cdm.model.taxon.Synonym;
61
import eu.etaxonomy.cdm.model.taxon.Taxon;
62
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
63
import eu.etaxonomy.cdm.strategy.cache.TaggedText;
64
import eu.etaxonomy.cdm.strategy.cache.name.CacheUpdate;
65
import eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy;
66
import eu.etaxonomy.cdm.strategy.match.IMatchable;
67
import eu.etaxonomy.cdm.strategy.match.Match;
68
import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
69
import eu.etaxonomy.cdm.strategy.match.MatchMode;
70
import eu.etaxonomy.cdm.strategy.merge.Merge;
71
import eu.etaxonomy.cdm.strategy.merge.MergeMode;
72
import eu.etaxonomy.cdm.strategy.parser.ParserProblem;
73
import eu.etaxonomy.cdm.validation.Level2;
74
import eu.etaxonomy.cdm.validation.Level3;
75
import eu.etaxonomy.cdm.validation.annotation.ValidTaxonomicYear;
76

    
77
/**
78
 * The upmost (abstract) class for scientific taxon names regardless of any
79
 * particular {@link NomenclaturalCode nomenclature code}. The scientific taxon name does not depend
80
 * on the use made of it in a publication or a treatment
81
 * ({@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon concept respectively potential taxon})
82
 * as an {@link eu.etaxonomy.cdm.model.taxon.Taxon "accepted" respectively "correct" (taxon) name}
83
 * or as a {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
84
 * <P>
85
 * This class corresponds partially to: <ul>
86
 * <li> TaxonName according to the TDWG ontology
87
 * <li> ScientificName and CanonicalName according to the TCS
88
 * <li> ScientificName according to the ABCD schema
89
 * </ul>
90
 *
91
 * @author m.doering
92
 * @version 1.0
93
 * @created 08-Nov-2007 13:06:57
94
 */
95
@XmlAccessorType(XmlAccessType.FIELD)
96
@XmlType(name = "TaxonNameBase", propOrder = {
97
    "appendedPhrase",
98
    "nomenclaturalMicroReference",
99
    "nomenclaturalReference",
100
    "rank",
101
    "fullTitleCache",
102
    "protectedFullTitleCache",
103
    "homotypicalGroup",
104
    "typeDesignations",
105
    "relationsFromThisName",
106
    "relationsToThisName",
107
    "status",
108
    "descriptions",
109
    "taxonBases"
110
})
111
@XmlRootElement(name = "TaxonNameBase")
112
@Entity
113
@Audited
114
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
115
@Table(appliesTo="TaxonNameBase", indexes = { @Index(name = "taxonNameBaseTitleCacheIndex", columnNames = { "titleCache" }),  @Index(name = "taxonNameBaseNameCacheIndex", columnNames = { "nameCache" }) })
116
public abstract class TaxonNameBase<T extends TaxonNameBase<?,?>, S extends INameCacheStrategy> extends IdentifiableEntity<S> implements IParsable, IRelated, IMatchable, Cloneable {
117
    private static final long serialVersionUID = -4530368639601532116L;
118
    private static final Logger logger = Logger.getLogger(TaxonNameBase.class);
119

    
120
    @XmlElement(name = "FullTitleCache")
121
    @Column(length=800, name="fullTitleCache")  //see #1592
122
    @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
123
    @CacheUpdate(noUpdate ="titleCache")
124
    @NotEmpty(groups = Level2.class)
125
    protected String fullTitleCache;
126

    
127
    //if true titleCache will not be automatically generated/updated
128
    @XmlElement(name = "ProtectedFullTitleCache")
129
    @CacheUpdate(value ="fullTitleCache", noUpdate ="titleCache")
130
    private boolean protectedFullTitleCache;
131

    
132
    @XmlElementWrapper(name = "Descriptions")
133
    @XmlElement(name = "Description")
134
    @OneToMany(mappedBy="taxonName", fetch= FetchType.LAZY, orphanRemoval=true)
135
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
136
    @NotNull
137
    private Set<TaxonNameDescription> descriptions = new HashSet<TaxonNameDescription>();
138

    
139
    @XmlElement(name = "AppendedPhrase")
140
    @Field
141
    @CacheUpdate(value ="nameCache")
142
    //TODO Val #3379
143
//    @NullOrNotEmpty
144
    @Column(length=255)
145
    private String appendedPhrase;
146

    
147
    @XmlElement(name = "NomenclaturalMicroReference")
148
    @Field
149
    @CacheUpdate(noUpdate ="titleCache")
150
    //TODO Val #3379
151
//    @NullOrNotEmpty
152
    @Column(length=255)
153
    private String nomenclaturalMicroReference;
154

    
155
    @XmlAttribute
156
    @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
157
    private int parsingProblem = 0;
158

    
159
    @XmlAttribute
160
    @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
161
    private int problemStarts = -1;
162

    
163
    @XmlAttribute
164
    @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
165
    private int problemEnds = -1;
166

    
167
    @XmlElementWrapper(name = "TypeDesignations")
168
    @XmlElement(name = "TypeDesignation")
169
    @XmlIDREF
170
    @XmlSchemaType(name = "IDREF")
171
    @ManyToMany(fetch = FetchType.LAZY)
172
    @JoinTable(
173
        name="TaxonNameBase_TypeDesignationBase",
174
        joinColumns=@javax.persistence.JoinColumn(name="TaxonNameBase_id"),
175
        inverseJoinColumns=@javax.persistence.JoinColumn(name="typedesignations_id")
176
    )
177
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
178
    @NotNull
179
    private Set<TypeDesignationBase> typeDesignations = new HashSet<TypeDesignationBase>();
180

    
181
    @XmlElement(name = "HomotypicalGroup")
182
    @XmlIDREF
183
    @XmlSchemaType(name = "IDREF")
184
    @ManyToOne(fetch = FetchType.LAZY)
185
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
186
    @Match(MatchMode.IGNORE)
187
    @CacheUpdate(noUpdate ="titleCache")
188
    //TODO Val #3379
189
//    @NotNull
190
    private HomotypicalGroup homotypicalGroup;
191

    
192
    @XmlElementWrapper(name = "RelationsFromThisName")
193
    @XmlElement(name = "RelationFromThisName")
194
    @OneToMany(mappedBy="relatedFrom", fetch= FetchType.LAZY, orphanRemoval=true)
195
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
196
    @Merge(MergeMode.RELATION)
197
    @NotNull
198
    @Valid
199
    private Set<NameRelationship> relationsFromThisName = new HashSet<NameRelationship>();
200

    
201
    @XmlElementWrapper(name = "RelationsToThisName")
202
    @XmlElement(name = "RelationToThisName")
203
    @XmlIDREF
204
    @XmlSchemaType(name = "IDREF")
205
    @OneToMany(mappedBy="relatedTo", fetch= FetchType.LAZY, orphanRemoval=true)
206
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
207
    @Merge(MergeMode.RELATION)
208
    @NotNull
209
    @Valid
210
    private Set<NameRelationship> relationsToThisName = new HashSet<NameRelationship>();
211

    
212
    @XmlElementWrapper(name = "NomenclaturalStatuses")
213
    @XmlElement(name = "NomenclaturalStatus")
214
    @OneToMany(fetch= FetchType.LAZY, orphanRemoval=true)
215
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE,CascadeType.DELETE})
216
    @NotNull
217
    @IndexedEmbedded(depth=1)
218
    private Set<NomenclaturalStatus> status = new HashSet<NomenclaturalStatus>();
219

    
220
    @XmlElementWrapper(name = "TaxonBases")
221
    @XmlElement(name = "TaxonBase")
222
    @XmlIDREF
223
    @XmlSchemaType(name = "IDREF")
224
    @OneToMany(mappedBy="name", fetch= FetchType.LAZY)
225
    @NotNull
226
    @IndexedEmbedded(depth=1)
227
    private Set<TaxonBase> taxonBases = new HashSet<TaxonBase>();
228

    
229
    @XmlElement(name = "Rank")
230
    @XmlIDREF
231
    @XmlSchemaType(name = "IDREF")
232
    @ManyToOne(fetch = FetchType.EAGER)
233
    @CacheUpdate(value ="nameCache")
234
    //TODO Val #3379, handle maybe as groups = Level2.class ??
235
//    @NotNull
236
    @IndexedEmbedded(depth=1)
237
    private Rank rank;
238

    
239
    @XmlElement(name = "NomenclaturalReference")
240
    @XmlIDREF
241
    @XmlSchemaType(name = "IDREF")
242
    @ManyToOne(fetch = FetchType.LAZY)
243
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
244
    @CacheUpdate(noUpdate ="titleCache")
245
    @IndexedEmbedded
246
    private Reference nomenclaturalReference;
247

    
248
// ************* CONSTRUCTORS *************/
249
    /**
250
     * Class constructor: creates a new empty taxon name.
251
     *
252
     * @see #TaxonNameBase(Rank)
253
     * @see #TaxonNameBase(HomotypicalGroup)
254
     * @see #TaxonNameBase(Rank, HomotypicalGroup)
255
     */
256
    public TaxonNameBase() {
257
        super();
258
    }
259
    /**
260
     * Class constructor: creates a new taxon name
261
     * only containing its {@link Rank rank}.
262
     *
263
     * @param  rank  the rank to be assigned to <i>this</i> taxon name
264
     * @see    		 #TaxonNameBase()
265
     * @see    		 #TaxonNameBase(HomotypicalGroup)
266
     * @see    		 #TaxonNameBase(Rank, HomotypicalGroup)
267
     */
268
    public TaxonNameBase(Rank rank) {
269
        this(rank, null);
270
    }
271
    /**
272
     * Class constructor: creates a new taxon name
273
     * only containing its {@link HomotypicalGroup homotypical group}.
274
     * The new taxon name will be also added to the set of taxon names
275
     * belonging to this homotypical group.
276
     *
277
     * @param  homotypicalGroup  the homotypical group to which <i>this</i> taxon name belongs
278
     * @see    					 #TaxonNameBase()
279
     * @see    					 #TaxonNameBase(Rank)
280
     * @see    					 #TaxonNameBase(Rank, HomotypicalGroup)
281
     */
282
    public TaxonNameBase(HomotypicalGroup homotypicalGroup) {
283
        this(null, homotypicalGroup);
284
    }
285
    /**
286
     * Class constructor: creates a new taxon name
287
     * only containing its {@link Rank rank} and
288
     * its {@link HomotypicalGroup homotypical group}.
289
     * The new taxon name will be also added to the set of taxon names
290
     * belonging to this homotypical group.
291
     *
292
     * @param  rank  			 the rank to be assigned to <i>this</i> taxon name
293
     * @param  homotypicalGroup  the homotypical group to which <i>this</i> taxon name belongs
294
     * @see    					 #TaxonNameBase()
295
     * @see    					 #TaxonNameBase(Rank)
296
     * @see    					 #TaxonNameBase(HomotypicalGroup)
297
     */
298
    public TaxonNameBase(Rank rank, HomotypicalGroup homotypicalGroup) {
299
        super();
300
        this.setRank(rank);
301
        if (homotypicalGroup == null){
302
            homotypicalGroup = new HomotypicalGroup();
303
        }
304
        homotypicalGroup.addTypifiedName(this);
305
        this.homotypicalGroup = homotypicalGroup;
306
    }
307

    
308
    abstract protected Map<String, java.lang.reflect.Field> getAllFields();
309

    
310
//********* METHODS **************************************/
311

    
312
    /**
313
     * Returns the boolean value "false" since the components of <i>this</i> taxon name
314
     * cannot follow the rules of a corresponding {@link NomenclaturalCode nomenclatural code}
315
     * which is not defined for this class. The nomenclature code depends on
316
     * the concrete name subclass ({@link BacterialName BacterialName},
317
     * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName},
318
     * {@link ZoologicalName ZoologicalName} or {@link ViralName ViralName})
319
     * to which a taxon name belongs.
320
     *
321
     * @return  false
322
     */
323
    @Transient
324
    public abstract boolean isCodeCompliant();
325

    
326
    public abstract String generateFullTitle();
327

    
328

    
329

    
330
    @Transient
331
    public List<TaggedText> getTaggedName(){
332
        return getCacheStrategy().getTaggedTitle(this);
333
    }
334

    
335
    @Transient
336
    public String getFullTitleCache(){
337
        if (protectedFullTitleCache){
338
            return this.fullTitleCache;
339
        }
340
        if (fullTitleCache == null ){
341
            this.fullTitleCache = getTruncatedCache(generateFullTitle());
342
        }
343
        return fullTitleCache;
344
    }
345

    
346

    
347
    public void setFullTitleCache(String fullTitleCache){
348
        setFullTitleCache(fullTitleCache, PROTECTED);
349
    }
350

    
351
    public void setFullTitleCache(String fullTitleCache, boolean protectCache){
352
        fullTitleCache = getTruncatedCache(fullTitleCache);
353
        this.fullTitleCache = fullTitleCache;
354
        this.setProtectedFullTitleCache(protectCache);
355
    }
356

    
357

    
358
    public boolean isProtectedFullTitleCache() {
359
        return protectedFullTitleCache;
360
    }
361

    
362
    public void setProtectedFullTitleCache(boolean protectedFullTitleCache) {
363
        this.protectedFullTitleCache = protectedFullTitleCache;
364
    }
365

    
366
    /**
367
     * Returns the set of all {@link NameRelationship name relationships}
368
     * in which <i>this</i> taxon name is involved. A taxon name can be both source
369
     * in some name relationships or target in some others.
370
     *
371
     * @see    #getRelationsToThisName()
372
     * @see    #getRelationsFromThisName()
373
     * @see    #addNameRelationship(NameRelationship)
374
     * @see    #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
375
     * @see    #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
376
     */
377
    @Transient
378
    public Set<NameRelationship> getNameRelations() {
379
        Set<NameRelationship> rels = new HashSet<NameRelationship>();
380
        rels.addAll(getRelationsFromThisName());
381
        rels.addAll(getRelationsToThisName());
382
        return rels;
383
    }
384

    
385
    /**
386
     * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
387
     * and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
388
     * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
389
     *
390
     * @param toName		  the taxon name of the target for this new name relationship
391
     * @param type			  the type of this new name relationship
392
     * @param ruleConsidered  the string which specifies the rule on which this name relationship is based
393
     * @see    				  #getRelationsToThisName()
394
     * @see    				  #getNameRelations()
395
     * @see    				  #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
396
     * @see    				  #addNameRelationship(NameRelationship)
397
     */
398
    public void addRelationshipToName(TaxonNameBase toName, NameRelationshipType type, String ruleConsidered){
399
        addRelationshipToName(toName, type, null, null, ruleConsidered);
400
        //		NameRelationship rel = new NameRelationship(toName, this, type, ruleConsidered);
401
    }
402

    
403
    /**
404
     * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
405
     * and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
406
     * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
407
     *
408
     * @param toName		  the taxon name of the target for this new name relationship
409
     * @param type			  the type of this new name relationship
410
     * @param ruleConsidered  the string which specifies the rule on which this name relationship is based
411
     * @return
412
     * @see    				  #getRelationsToThisName()
413
     * @see    				  #getNameRelations()
414
     * @see    				  #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
415
     * @see    				  #addNameRelationship(NameRelationship)
416
     */
417
    public NameRelationship addRelationshipToName(TaxonNameBase toName, NameRelationshipType type, Reference citation, String microCitation, String ruleConsidered){
418
        if (toName == null){
419
            throw new NullPointerException("Null is not allowed as name for a name relationship");
420
        }
421
        NameRelationship rel = new NameRelationship(toName, this, type, citation, microCitation, ruleConsidered);
422
        return rel;
423
    }
424

    
425
    /**
426
     * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
427
     * and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
428
     * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
429
     *
430
     * @param fromName		  the taxon name of the source for this new name relationship
431
     * @param type			  the type of this new name relationship
432
     * @param ruleConsidered  the string which specifies the rule on which this name relationship is based
433
     * @param citation		  the reference in which this relation was described
434
     * @param microCitation	  the reference detail for this relation (e.g. page)
435
     * @see    				  #getRelationsFromThisName()
436
     * @see    				  #getNameRelations()
437
     * @see    				  #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
438
     * @see    				  #addNameRelationship(NameRelationship)
439
     */
440
    public NameRelationship addRelationshipFromName(TaxonNameBase fromName, NameRelationshipType type, String ruleConsidered){
441
        //fromName.addRelationshipToName(this, type, null, null, ruleConsidered);
442
        return this.addRelationshipFromName(fromName, type, null, null, ruleConsidered);
443
    }
444
    /**
445
     * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
446
     * and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
447
     * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
448
     *
449
     * @param fromName		  the taxon name of the source for this new name relationship
450
     * @param type			  the type of this new name relationship
451
     * @param ruleConsidered  the string which specifies the rule on which this name relationship is based
452
     * @param citation		  the reference in which this relation was described
453
     * @param microCitation	  the reference detail for this relation (e.g. page)
454
     * @see    				  #getRelationsFromThisName()
455
     * @see    				  #getNameRelations()
456
     * @see    				  #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
457
     * @see    				  #addNameRelationship(NameRelationship)
458
     */
459
    public NameRelationship addRelationshipFromName(TaxonNameBase fromName, NameRelationshipType type, Reference citation, String microCitation, String ruleConsidered){
460
        return fromName.addRelationshipToName(this, type, citation, microCitation, ruleConsidered);
461
    }
462

    
463
    /**
464
     * Adds an existing {@link NameRelationship name relationship} either to the set of
465
     * {@link #getRelationsToThisName() relations to <i>this</i> taxon name} or to the set of
466
     * {@link #getRelationsFromThisName() relations from <i>this</i> taxon name}. If neither the
467
     * source nor the target of the name relationship match with <i>this</i> taxon name
468
     * no addition will be carried out.
469
     *
470
     * @param rel  the name relationship to be added to one of <i>this</i> taxon name's name relationships sets
471
     * @see    	   #getNameRelations()
472
     * @see    	   #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
473
     * @see    	   #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
474
     */
475
    protected void addNameRelationship(NameRelationship rel) {
476
        if (rel!=null && rel.getToName().equals(this)){
477
            this.relationsToThisName.add(rel);
478
        }else if(rel!=null && rel.getFromName().equals(this)){
479
            this.relationsFromThisName.add(rel);
480
        }else{
481
            throw new RuntimeException("NameRelationship is either null or the relationship does not reference this name");
482
        }
483
    }
484
    /**
485
     * Removes one {@link NameRelationship name relationship} from one of both sets of
486
     * {@link #getNameRelations() name relationships} in which <i>this</i> taxon name is involved.
487
     * The name relationship will also be removed from one of both sets belonging
488
     * to the second taxon name involved. Furthermore the fromName and toName
489
     * attributes of the name relationship object will be nullified.
490
     *
491
     * @param  nameRelation  the name relationship which should be deleted from one of both sets
492
     * @see    				 #getNameRelations()
493
     */
494
    public void removeNameRelationship(NameRelationship nameRelation) {
495

    
496
        TaxonNameBase fromName = nameRelation.getFromName();
497
        TaxonNameBase toName = nameRelation.getToName();
498

    
499
        if (nameRelation != null) {
500
            nameRelation.setToName(null);
501
            nameRelation.setFromName(null);
502
        }
503

    
504
        if (fromName != null) {
505
            fromName.removeNameRelationship(nameRelation);
506
        }
507

    
508
        if (toName != null) {
509
            toName.removeNameRelationship(nameRelation);
510
        }
511

    
512
        this.relationsToThisName.remove(nameRelation);
513
        this.relationsFromThisName.remove(nameRelation);
514
    }
515

    
516
    public void removeRelationToTaxonName(TaxonNameBase toTaxonName) {
517
        Set<NameRelationship> nameRelationships = new HashSet<NameRelationship>();
518
//		nameRelationships.addAll(this.getNameRelations());
519
        nameRelationships.addAll(this.getRelationsFromThisName());
520
        nameRelationships.addAll(this.getRelationsToThisName());
521
        for(NameRelationship nameRelationship : nameRelationships) {
522
            // remove name relationship from this side
523
            if (nameRelationship.getFromName().equals(this) && nameRelationship.getToName().equals(toTaxonName)) {
524
                this.removeNameRelationship(nameRelationship);
525
            }
526
        }
527
    }
528

    
529

    
530
    /**
531
     * Does exactly the same as the addNameRelationship method provided that
532
     * the given relationship is a name relationship.
533
     *
534
     * @param relation  the relationship to be added to one of <i>this</i> taxon name's name relationships sets
535
     * @see    	   		#addNameRelationship(NameRelationship)
536
     * @see    	   		#getNameRelations()
537
     * @see    	   		NameRelationship
538
     * @see    	   		eu.etaxonomy.cdm.model.common.RelationshipBase
539
     */
540
    @Override
541
    public void addRelationship(RelationshipBase relation) {
542
        if (relation instanceof NameRelationship){
543
            addNameRelationship((NameRelationship)relation);
544
            NameRelationshipType type = (NameRelationshipType)relation.getType();
545
            if (type != null && ( type.isBasionymRelation() || type.isReplacedSynonymRelation() ) ){
546
                TaxonNameBase fromName = ((NameRelationship)relation).getFromName();
547
                TaxonNameBase toName = ((NameRelationship)relation).getToName();
548
                fromName.mergeHomotypicGroups(toName);
549
            }
550
        }else{
551
            logger.warn("Relationship not of type NameRelationship!");
552
            throw new IllegalArgumentException("Relationship not of type NameRelationship");
553
        }
554
    }
555

    
556

    
557
    /**
558
     * Returns the set of all {@link NameRelationship name relationships}
559
     * in which <i>this</i> taxon name is involved as a source ("from"-side).
560
     *
561
     * @see    #getNameRelations()
562
     * @see    #getRelationsToThisName()
563
     * @see    #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
564
     */
565
    public Set<NameRelationship> getRelationsFromThisName() {
566
        if(relationsFromThisName == null) {
567
            this.relationsFromThisName = new HashSet<NameRelationship>();
568
        }
569
        return relationsFromThisName;
570
    }
571

    
572
    /**
573
     * Returns the set of all {@link NameRelationship name relationships}
574
     * in which <i>this</i> taxon name is involved as a target ("to"-side).
575
     *
576
     * @see    #getNameRelations()
577
     * @see    #getRelationsFromThisName()
578
     * @see    #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
579
     */
580
    public Set<NameRelationship> getRelationsToThisName() {
581
        if(relationsToThisName == null) {
582
            this.relationsToThisName = new HashSet<NameRelationship>();
583
        }
584
        return relationsToThisName;
585
    }
586

    
587
    /**
588
     * Returns the set of {@link NomenclaturalStatus nomenclatural status} assigned
589
     * to <i>this</i> taxon name according to its corresponding nomenclature code.
590
     * This includes the {@link NomenclaturalStatusType type} of the nomenclatural status
591
     * and the nomenclatural code rule considered.
592
     *
593
     * @see     NomenclaturalStatus
594
     * @see     NomenclaturalStatusType
595
     */
596
    public Set<NomenclaturalStatus> getStatus() {
597
        if(status == null) {
598
            this.status = new HashSet<NomenclaturalStatus>();
599
        }
600
        return status;
601
    }
602

    
603
    /**
604
     * Adds a new {@link NomenclaturalStatus nomenclatural status}
605
     * to <i>this</i> taxon name's set of nomenclatural status.
606
     *
607
     * @param  nomStatus  the nomenclatural status to be added
608
     * @see 			  #getStatus()
609
     */
610
    public void addStatus(NomenclaturalStatus nomStatus) {
611
        this.status.add(nomStatus);
612
    }
613
    public NomenclaturalStatus addStatus(NomenclaturalStatusType statusType, Reference citation, String microCitation) {
614
        NomenclaturalStatus newStatus = NomenclaturalStatus.NewInstance(statusType, citation, microCitation);
615
        this.status.add(newStatus);
616
        return newStatus;
617
    }
618

    
619
    /**
620
     * Removes one element from the set of nomenclatural status of <i>this</i> taxon name.
621
     * Type and ruleConsidered attributes of the nomenclatural status object
622
     * will be nullified.
623
     *
624
     * @param  nomStatus  the nomenclatural status of <i>this</i> taxon name which should be deleted
625
     * @see     		  #getStatus()
626
     */
627
    public void removeStatus(NomenclaturalStatus nomStatus) {
628
        //TODO to be implemented?
629
        logger.warn("not yet fully implemented?");
630
        this.status.remove(nomStatus);
631
    }
632

    
633

    
634
    /**
635
     * Indicates whether <i>this</i> taxon name is a {@link NameRelationshipType#BASIONYM() basionym}
636
     * or a {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym}
637
     * of any other taxon name. Returns "true", if a basionym or a replaced
638
     * synonym {@link NameRelationship relationship} from <i>this</i> taxon name to another taxon name exists,
639
     * false otherwise (also in case <i>this</i> taxon name is the only one in the
640
     * homotypical group).
641
     */
642
    @Transient
643
    public boolean isOriginalCombination(){
644
        Set<NameRelationship> relationsFromThisName = this.getRelationsFromThisName();
645
        for (NameRelationship relation : relationsFromThisName) {
646
            if (relation.getType().isBasionymRelation() ||
647
                    relation.getType().isReplacedSynonymRelation()) {
648
                return true;
649
            }
650
        }
651
        return false;
652
    }
653

    
654
    /**
655
     * Indicates <i>this</i> taxon name is a {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym}
656
     * of any other taxon name. Returns "true", if a replaced
657
     * synonym {@link NameRelationship relationship} from <i>this</i> taxon name to another taxon name exists,
658
     * false otherwise (also in case <i>this</i> taxon name is the only one in the
659
     * homotypical group).
660
     */
661
    @Transient
662
    public boolean isReplacedSynonym(){
663
        Set<NameRelationship> relationsFromThisName = this.getRelationsFromThisName();
664
        for (NameRelationship relation : relationsFromThisName) {
665
            if (relation.getType().isReplacedSynonymRelation()) {
666
                return true;
667
            }
668
        }
669
        return false;
670
    }
671

    
672
    /**
673
     * Returns the taxon name which is the {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
674
     * The basionym of a taxon name is its epithet-bringing synonym.
675
     * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
676
     * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
677
     * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
678
     *
679
     * If more than one basionym exists one is choosen at radom.
680
     *
681
     * If no basionym exists null is returned.
682
     */
683
    @Transient
684
    public TaxonNameBase getBasionym(){
685
        Set<TaxonNameBase> basionyms = getBasionyms();
686
        if (basionyms.size() == 0){
687
            return null;
688
        }else{
689
            return basionyms.iterator().next();
690
        }
691
    }
692

    
693
    /**
694
     * Returns the set of taxon names which are the {@link NameRelationshipType#BASIONYM() basionyms} of <i>this</i> taxon name.
695
     * The basionym of a taxon name is its epithet-bringing synonym.
696
     * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
697
     * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
698
     * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
699
     */
700
    @Transient
701
    public Set<TaxonNameBase> getBasionyms(){
702
        Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
703
        Set<NameRelationship> rels = this.getRelationsToThisName();
704
        for (NameRelationship rel : rels){
705
            if (rel.getType()!= null && rel.getType().isBasionymRelation()){
706
                TaxonNameBase<?,?> basionym = rel.getFromName();
707
                result.add(basionym);
708
            }
709
        }
710
        return result;
711
    }
712

    
713
    /**
714
     * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
715
     * The basionym {@link NameRelationship relationship} will be added to <i>this</i> taxon name
716
     * and to the basionym. The basionym cannot have itself a basionym.
717
     * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
718
     * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
719
     *
720
     * @param  basionym		the taxon name to be set as the basionym of <i>this</i> taxon name
721
     * @see  				#getBasionym()
722
     * @see  				#addBasionym(TaxonNameBase, String)
723
     */
724
    public void addBasionym(T basionym){
725
        addBasionym(basionym, null, null, null);
726
    }
727
    /**
728
     * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name
729
     * and keeps the nomenclatural rule considered for it. The basionym
730
     * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the basionym.
731
     * The basionym cannot have itself a basionym.
732
     * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
733
     * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
734
     *
735
     * @param  basionym			the taxon name to be set as the basionym of <i>this</i> taxon name
736
     * @param  ruleConsidered	the string identifying the nomenclatural rule
737
     * @return
738
     * @see  					#getBasionym()
739
     * @see  					#addBasionym(TaxonNameBase)
740
     */
741
    public NameRelationship addBasionym(T basionym, Reference citation, String microcitation, String ruleConsidered){
742
        if (basionym != null){
743
            return basionym.addRelationshipToName(this, NameRelationshipType.BASIONYM(), citation, microcitation, ruleConsidered);
744
        }else{
745
            return null;
746
        }
747
    }
748

    
749
    /**
750
     * Returns the set of taxon names which are the {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonyms} of <i>this</i> taxon name.
751
     *
752
     */
753
    @Transient
754
    public Set<TaxonNameBase> getReplacedSynonyms(){
755
        Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
756
        Set<NameRelationship> rels = this.getRelationsToThisName();
757
        for (NameRelationship rel : rels){
758
            if (rel.getType().isReplacedSynonymRelation()){
759
                TaxonNameBase replacedSynonym = rel.getFromName();
760
                result.add(replacedSynonym);
761
            }
762
        }
763
        return result;
764
    }
765

    
766
    /**
767
     * Assigns a taxon name as {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym} of <i>this</i> taxon name
768
     * and keeps the nomenclatural rule considered for it. The replaced synonym
769
     * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the replaced synonym.
770
     * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the replaced synonym
771
     * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
772
     *
773
     * @param  basionym			the taxon name to be set as the basionym of <i>this</i> taxon name
774
     * @param  ruleConsidered	the string identifying the nomenclatural rule
775
     * @see  					#getBasionym()
776
     * @see  					#addBasionym(TaxonNameBase)
777
     */
778
    //TODO: Check if true: The replaced synonym cannot have itself a replaced synonym (?).
779
    public void addReplacedSynonym(T replacedSynonym, Reference citation, String microcitation, String ruleConsidered){
780
        if (replacedSynonym != null){
781
            replacedSynonym.addRelationshipToName(this, NameRelationshipType.REPLACED_SYNONYM(), citation, microcitation, ruleConsidered);
782
        }
783
    }
784

    
785
    /**
786
     * Removes the {@link NameRelationshipType#BASIONYM() basionym} {@link NameRelationship relationship} from the set of
787
     * {@link #getRelationsToThisName() name relationships to} <i>this</i> taxon name. The same relationhip will be
788
     * removed from the set of {@link #getRelationsFromThisName() name relationships from} the taxon name
789
     * previously used as basionym.
790
     *
791
     * @see   #getBasionym()
792
     * @see   #addBasionym(TaxonNameBase)
793
     */
794
    public void removeBasionyms(){
795
        Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
796
        for (NameRelationship nameRelation : this.getRelationsToThisName()){
797
            if (nameRelation.getType().isBasionymRelation()){
798
                removeRelations.add(nameRelation);
799
            }
800
        }
801
        // Removing relations from a set through which we are iterating causes a
802
        // ConcurrentModificationException. Therefore, we delete the targeted
803
        // relations in a second step.
804
        for (NameRelationship relation : removeRelations){
805
            this.removeNameRelationship(relation);
806
        }
807
    }
808

    
809
    /**
810
     * Returns the taxonomic {@link Rank rank} of <i>this</i> taxon name.
811
     *
812
     * @see 	Rank
813
     */
814
    public Rank getRank(){
815
        return this.rank;
816
    }
817

    
818
    /**
819
     * @see  #getRank()
820
     */
821
    public void setRank(Rank rank){
822
        this.rank = rank;
823
    }
824

    
825
    /**
826
     * Returns the {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} of <i>this</i> taxon name.
827
     * The nomenclatural reference is here meant to be the one publication
828
     * <i>this</i> taxon name was originally published in while fulfilling the formal
829
     * requirements as specified by the corresponding {@link NomenclaturalCode nomenclatural code}.
830
     *
831
     * @see 	eu.etaxonomy.cdm.model.reference.INomenclaturalReference
832
     * @see 	eu.etaxonomy.cdm.model.reference.Reference
833
     */
834
    public INomenclaturalReference getNomenclaturalReference(){
835
        return this.nomenclaturalReference;
836
    }
837
    /**
838
     * Assigns a {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} to <i>this</i> taxon name.
839
     * The corresponding {@link eu.etaxonomy.cdm.model.reference.Reference.isNomenclaturallyRelevant nomenclaturally relevant flag} will be set to true
840
     * as it is obviously used for nomenclatural purposes.
841
     *
842
     * @throws IllegalArgumentException if parameter <code>nomenclaturalReference</code> is not assignable from {@link INomenclaturalReference}
843
     * @see  #getNomenclaturalReference()
844
     */
845
    public void setNomenclaturalReference(INomenclaturalReference nomenclaturalReference){
846
        if(nomenclaturalReference != null){
847
            if(!INomenclaturalReference.class.isAssignableFrom(nomenclaturalReference.getClass())){
848
                throw new IllegalArgumentException("Parameter nomenclaturalReference is not assignable from INomenclaturalReference");
849
            }
850
            this.nomenclaturalReference = (Reference)nomenclaturalReference;
851
        } else {
852
            this.nomenclaturalReference = null;
853
        }
854
    }
855

    
856
    /**
857
     * Returns the appended phrase string assigned to <i>this</i> taxon name.
858
     * The appended phrase is a non-atomised addition to a name. It is
859
     * not ruled by a nomenclatural code.
860
     */
861
    public String getAppendedPhrase(){
862
        return this.appendedPhrase;
863
    }
864

    
865
    /**
866
     * @see  #getAppendedPhrase()
867
     */
868
    public void setAppendedPhrase(String appendedPhrase){
869
        this.appendedPhrase = StringUtils.isBlank(appendedPhrase)? null : appendedPhrase;
870
    }
871

    
872
    /**
873
     * Returns the details string of the {@link #getNomenclaturalReference() nomenclatural reference} assigned
874
     * to <i>this</i> taxon name. The details describe the exact localisation within
875
     * the publication used as nomenclature reference. These are mostly
876
     * (implicitly) pages but can also be figures or tables or any other
877
     * element of a publication. A nomenclatural micro reference (details)
878
     * requires the existence of a nomenclatural reference.
879
     */
880
    //Details of the nomenclatural reference (protologue).
881
    public String getNomenclaturalMicroReference(){
882
        return this.nomenclaturalMicroReference;
883
    }
884
    /**
885
     * @see  #getNomenclaturalMicroReference()
886
     */
887
    public void setNomenclaturalMicroReference(String nomenclaturalMicroReference){
888
        this.nomenclaturalMicroReference = StringUtils.isBlank(nomenclaturalMicroReference)? null : nomenclaturalMicroReference;
889
    }
890

    
891
    @Override
892
    public int getParsingProblem(){
893
        return this.parsingProblem;
894
    }
895

    
896
    @Override
897
    public void setParsingProblem(int parsingProblem){
898
        this.parsingProblem = parsingProblem;
899
    }
900

    
901
    @Override
902
    public void addParsingProblem(ParserProblem problem){
903
        parsingProblem = ParserProblem.addProblem(parsingProblem, problem);
904
    }
905

    
906
    @Override
907
    public void removeParsingProblem(ParserProblem problem) {
908
        parsingProblem = ParserProblem.removeProblem(parsingProblem, problem);
909
    }
910

    
911
    /**
912
     * @param warnings
913
     */
914
    public void addParsingProblems(int problems){
915
        parsingProblem = ParserProblem.addProblems(parsingProblem, problems);
916
    }
917

    
918
    @Override
919
    public boolean hasProblem(){
920
        return parsingProblem != 0;
921
    }
922

    
923
    @Override
924
    public boolean hasProblem(ParserProblem problem) {
925
        return getParsingProblems().contains(problem);
926
    }
927

    
928
    @Override
929
    public int getProblemStarts(){
930
        return this.problemStarts;
931
    }
932

    
933
    @Override
934
    public void setProblemStarts(int start) {
935
        this.problemStarts = start;
936
    }
937

    
938
    @Override
939
    public int getProblemEnds(){
940
        return this.problemEnds;
941
    }
942

    
943
    @Override
944
    public void setProblemEnds(int end) {
945
        this.problemEnds = end;
946
    }
947

    
948
//*********************** TYPE DESIGNATION *********************************************//
949

    
950
    /**
951
     * Returns the set of {@link TypeDesignationBase type designations} assigned
952
     * to <i>this</i> taxon name.
953
     * @see     NameTypeDesignation
954
     * @see     SpecimenTypeDesignation
955
     */
956
    public Set<TypeDesignationBase> getTypeDesignations() {
957
        if(typeDesignations == null) {
958
            this.typeDesignations = new HashSet<TypeDesignationBase>();
959
        }
960
        return typeDesignations;
961
    }
962

    
963
    /**
964
     * Removes one element from the set of {@link TypeDesignationBase type designations} assigned to
965
     * <i>this</i> taxon name. The type designation itself will be nullified.
966
     *
967
     * @param  typeDesignation  the type designation which should be deleted
968
     */
969
    @SuppressWarnings("deprecation")
970
    public void removeTypeDesignation(TypeDesignationBase typeDesignation) {
971
        this.typeDesignations.remove(typeDesignation);
972
        typeDesignation.removeTypifiedName(this);
973
    }
974

    
975
    /**
976
     * Returns the set of {@link SpecimenTypeDesignation specimen type designations} assigned
977
     * to <i>this</i> taxon name. The {@link Rank rank} of <i>this</i> taxon name is generally
978
     * "species" or below. The specimen type designations include all the
979
     * specimens on which the typification of this name is based (which are
980
     * exclusively used to typify taxon names belonging to the same
981
     * {@link HomotypicalGroup homotypical group} to which <i>this</i> taxon name
982
     * belongs) and eventually the status of these designations.
983
     *
984
     * @see     SpecimenTypeDesignation
985
     * @see     NameTypeDesignation
986
     * @see     HomotypicalGroup
987
     */
988
    @Transient
989
    public Set<SpecimenTypeDesignation> getSpecimenTypeDesignationsOfHomotypicalGroup() {
990
        return this.getHomotypicalGroup().getSpecimenTypeDesignations();
991
    }
992

    
993
//*********************** NAME TYPE DESIGNATION *********************************************//
994

    
995
    /**
996
     * Returns the set of {@link NameTypeDesignation name type designations} assigned
997
     * to <i>this</i> taxon name the rank of which must be above "species".
998
     * The name type designations include all the taxon names used to typify
999
     * <i>this</i> taxon name and eventually the rejected or conserved status
1000
     * of these designations.
1001
     *
1002
     * @see     NameTypeDesignation
1003
     * @see     SpecimenTypeDesignation
1004
     */
1005
    @Transient
1006
    public Set<NameTypeDesignation> getNameTypeDesignations() {
1007
        Set<NameTypeDesignation> result = new HashSet<NameTypeDesignation>();
1008
        for (TypeDesignationBase typeDesignation : this.typeDesignations){
1009
            if (typeDesignation instanceof NameTypeDesignation){
1010
                result.add((NameTypeDesignation)typeDesignation);
1011
            }
1012
        }
1013
        return result;
1014
    }
1015

    
1016
    /**
1017
     * Creates and adds a new {@link NameTypeDesignation name type designation}
1018
     * to <i>this</i> taxon name's set of type designations.
1019
     *
1020
     * @param  typeSpecies				the taxon name to be used as type of <i>this</i> taxon name
1021
     * @param  citation					the reference for this new designation
1022
     * @param  citationMicroReference	the string with the details (generally pages) within the reference
1023
     * @param  originalNameString		the taxon name string used in the reference to assert this designation
1024
     * @param  isRejectedType			the boolean status for a rejected name type designation
1025
     * @param  isConservedType			the boolean status for a conserved name type designation
1026
     * @param  isLectoType				the boolean status for a lectotype name type designation
1027
     * @param  isNotDesignated			the boolean status for a name type designation without name type
1028
     * @param  addToAllHomotypicNames	the boolean indicating whether the name type designation should be
1029
     * 									added to all taxon names of the homotypical group this taxon name belongs to
1030
     * @return
1031
     * @see 			  				#getNameTypeDesignations()
1032
     * @see 			  				NameTypeDesignation
1033
     * @see 			  				TypeDesignationBase#isNotDesignated()
1034
     */
1035
    public NameTypeDesignation addNameTypeDesignation(TaxonNameBase typeSpecies,
1036
                Reference citation,
1037
                String citationMicroReference,
1038
                String originalNameString,
1039
                NameTypeDesignationStatus status,
1040
                boolean isRejectedType,
1041
                boolean isConservedType,
1042
                /*boolean isLectoType, */
1043
                boolean isNotDesignated,
1044
                boolean addToAllHomotypicNames) {
1045
        NameTypeDesignation nameTypeDesignation = new NameTypeDesignation(typeSpecies, citation, citationMicroReference, originalNameString, status, isRejectedType, isConservedType, isNotDesignated);
1046
        //nameTypeDesignation.setLectoType(isLectoType);
1047
        addTypeDesignation(nameTypeDesignation, addToAllHomotypicNames);
1048
        return nameTypeDesignation;
1049
    }
1050

    
1051
    /**
1052
     * Creates and adds a new {@link NameTypeDesignation name type designation}
1053
     * to <i>this</i> taxon name's set of type designations.
1054
     *
1055
     * @param  typeSpecies				the taxon name to be used as type of <i>this</i> taxon name
1056
     * @param  citation					the reference for this new designation
1057
     * @param  citationMicroReference	the string with the details (generally pages) within the reference
1058
     * @param  originalNameString		the taxon name string used in the reference to assert this designation
1059
     * @param  status                   the name type designation status
1060
     * @param  addToAllHomotypicNames	the boolean indicating whether the name type designation should be
1061
     * 									added to all taxon names of the homotypical group this taxon name belongs to
1062
     * @return
1063
     * @see 			  				#getNameTypeDesignations()
1064
     * @see 			  				NameTypeDesignation
1065
     * @see 			  				TypeDesignationBase#isNotDesignated()
1066
     */
1067
    public NameTypeDesignation addNameTypeDesignation(TaxonNameBase typeSpecies,
1068
                Reference citation,
1069
                String citationMicroReference,
1070
                String originalNameString,
1071
                NameTypeDesignationStatus status,
1072
                boolean addToAllHomotypicNames) {
1073
        NameTypeDesignation nameTypeDesignation = new NameTypeDesignation(typeSpecies, status, citation, citationMicroReference, originalNameString);
1074
        addTypeDesignation(nameTypeDesignation, addToAllHomotypicNames);
1075
        return nameTypeDesignation;
1076
    }
1077

    
1078
//*********************** SPECIMEN TYPE DESIGNATION *********************************************//
1079

    
1080
    /**
1081
     * Returns the set of {@link SpecimenTypeDesignation specimen type designations}
1082
     * that typify <i>this</i> taxon name.
1083
     */
1084
    @Transient
1085
    public Set<SpecimenTypeDesignation> getSpecimenTypeDesignations() {
1086
        Set<SpecimenTypeDesignation> result = new HashSet<SpecimenTypeDesignation>();
1087
        for (TypeDesignationBase typeDesignation : this.typeDesignations){
1088
            if (typeDesignation instanceof SpecimenTypeDesignation){
1089
                result.add((SpecimenTypeDesignation)typeDesignation);
1090
            }
1091
        }
1092
        return result;
1093
    }
1094

    
1095

    
1096
    /**
1097
     * Creates and adds a new {@link SpecimenTypeDesignation specimen type designation}
1098
     * to <i>this</i> taxon name's set of type designations.
1099
     *
1100
     * @param  typeSpecimen				the specimen to be used as a type for <i>this</i> taxon name
1101
     * @param  status					the specimen type designation status
1102
     * @param  citation					the reference for this new specimen type designation
1103
     * @param  citationMicroReference	the string with the details (generally pages) within the reference
1104
     * @param  originalNameString		the taxon name used in the reference to assert this designation
1105
     * @param  isNotDesignated			the boolean status for a specimen type designation without specimen type
1106
     * @param  addToAllHomotypicNames	the boolean indicating whether the specimen type designation should be
1107
     * 									added to all taxon names of the homotypical group the typified
1108
     * 									taxon name belongs to
1109
     * @return
1110
     * @see 			  				#getSpecimenTypeDesignations()
1111
     * @see 			  				SpecimenTypeDesignationStatus
1112
     * @see 			  				SpecimenTypeDesignation
1113
     * @see 			  				TypeDesignationBase#isNotDesignated()
1114
     */
1115
    public SpecimenTypeDesignation addSpecimenTypeDesignation(DerivedUnit typeSpecimen,
1116
                SpecimenTypeDesignationStatus status,
1117
                Reference citation,
1118
                String citationMicroReference,
1119
                String originalNameString,
1120
                boolean isNotDesignated,
1121
                boolean addToAllHomotypicNames) {
1122
        SpecimenTypeDesignation specimenTypeDesignation = new SpecimenTypeDesignation(typeSpecimen, status, citation, citationMicroReference, originalNameString, isNotDesignated);
1123
        addTypeDesignation(specimenTypeDesignation, addToAllHomotypicNames);
1124
        return specimenTypeDesignation;
1125
    }
1126

    
1127
    //used by merge strategy
1128
    private boolean addTypeDesignation(TypeDesignationBase typeDesignation){
1129
        return addTypeDesignation(typeDesignation, true);
1130
    }
1131

    
1132
    /**
1133
     * Adds a {@link TypeDesignationBase type designation} to <code>this</code> taxon name's set of type designations
1134
     *
1135
     * @param typeDesignation			the typeDesignation to be added to <code>this</code> taxon name
1136
     * @param addToAllNames				the boolean indicating whether the type designation should be
1137
     * 									added to all taxon names of the homotypical group the typified
1138
     * 									taxon name belongs to
1139
     * @return							true if the operation was succesful
1140
     *
1141
     * @throws IllegalArgumentException	if the type designation already has typified names, an {@link IllegalArgumentException exception}
1142
     * 									is thrown. We do this to prevent a type designation to be used for multiple taxon names.
1143
     *
1144
     */
1145
    public boolean addTypeDesignation(TypeDesignationBase typeDesignation, boolean addToAllNames){
1146
        //currently typeDesignations are not persisted with the homotypical group
1147
        //so explicit adding to the homotypical group is not necessary.
1148
        if (typeDesignation != null){
1149
            checkHomotypicalGroup(typeDesignation);
1150
            this.typeDesignations.add(typeDesignation);
1151
            typeDesignation.addTypifiedName(this);
1152

    
1153
            if (addToAllNames){
1154
                for (TaxonNameBase taxonName : this.getHomotypicalGroup().getTypifiedNames()){
1155
                    if (taxonName != this){
1156
                        taxonName.addTypeDesignation(typeDesignation, false);
1157
                    }
1158
                }
1159
            }
1160
        }
1161
        return true;
1162
    }
1163

    
1164
    /**
1165
     * Throws an Exception this type designation already has typified names from another homotypical group.
1166
     * @param typeDesignation
1167
     */
1168
    private void checkHomotypicalGroup(TypeDesignationBase typeDesignation) {
1169
        if(typeDesignation.getTypifiedNames().size() > 0){
1170
            Set<HomotypicalGroup> groups = new HashSet<HomotypicalGroup>();
1171
            Set<TaxonNameBase> names = typeDesignation.getTypifiedNames();
1172
            for (TaxonNameBase taxonName: names){
1173
                groups.add(taxonName.getHomotypicalGroup());
1174
            }
1175
            if (groups.size() > 1){
1176
                throw new IllegalArgumentException("TypeDesignation already has typified names from another homotypical group.");
1177
            }
1178
        }
1179
    }
1180

    
1181

    
1182

    
1183
//*********************** HOMOTYPICAL GROUP *********************************************//
1184

    
1185

    
1186
    /**
1187
     * Returns the {@link HomotypicalGroup homotypical group} to which
1188
     * <i>this</i> taxon name belongs. A homotypical group represents all taxon names
1189
     * that share the same types.
1190
     *
1191
     * @see 	HomotypicalGroup
1192
     */
1193

    
1194
    public HomotypicalGroup getHomotypicalGroup() {
1195
        if (homotypicalGroup == null){
1196
            homotypicalGroup = new HomotypicalGroup();
1197
            homotypicalGroup.typifiedNames.add(this);
1198
        }
1199
    	return homotypicalGroup;
1200
    }
1201

    
1202
    /**
1203
     * @see #getHomotypicalGroup()
1204
     */
1205
    public void setHomotypicalGroup(HomotypicalGroup homotypicalGroup) {
1206
        if (homotypicalGroup == null){
1207
            throw new IllegalArgumentException("HomotypicalGroup of name should never be null but was set to 'null'");
1208
        }
1209
        /*if (this.homotypicalGroup != null){
1210
        	this.homotypicalGroup.removeTypifiedName(this, false);
1211
        }*/
1212
        this.homotypicalGroup = homotypicalGroup;
1213
        if (!this.homotypicalGroup.typifiedNames.contains(this)){
1214
        	 this.homotypicalGroup.addTypifiedName(this);
1215
        }
1216
    }
1217

    
1218

    
1219

    
1220
// *************************************************************************//
1221

    
1222
    /**
1223
     * Returns the complete string containing the
1224
     * {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation() nomenclatural reference citation}
1225
     * and the {@link #getNomenclaturalMicroReference() details} assigned to <i>this</i> taxon name.
1226
     *
1227
     * @return  the string containing the nomenclatural reference of <i>this</i> taxon name
1228
     * @see		eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation()
1229
     * @see		#getNomenclaturalReference()
1230
     * @see		#getNomenclaturalMicroReference()
1231
     */
1232
    @Transient
1233
    public String getCitationString(){
1234
        return getNomenclaturalReference().getNomenclaturalCitation(getNomenclaturalMicroReference());
1235
    }
1236

    
1237
    /**
1238
     * Returns the parsing problems
1239
     * @return
1240
     */
1241
    @Override
1242
    public List<ParserProblem> getParsingProblems(){
1243
        return ParserProblem.warningList(this.parsingProblem);
1244
    }
1245

    
1246
    /**
1247
     * Returns the string containing the publication date (generally only year)
1248
     * of the {@link #getNomenclaturalReference() nomenclatural reference} for <i>this</i> taxon name, null if there is
1249
     * no nomenclatural reference.
1250
     *
1251
     * @return  the string containing the publication date of <i>this</i> taxon name
1252
     * @see		eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getYear()
1253
     */
1254
    @Transient
1255
    @ValidTaxonomicYear(groups=Level3.class)
1256
    public String getReferenceYear(){
1257
        if (this.getNomenclaturalReference() != null ){
1258
            return this.getNomenclaturalReference().getYear();
1259
        }else{
1260
            return null;
1261
        }
1262
    }
1263

    
1264
    /**
1265
     * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1266
     * In this context a taxon base means the use of a taxon name by a reference
1267
     * either as a {@link eu.etaxonomy.cdm.model.taxon.Taxon taxon} ("accepted/correct" name) or
1268
     * as a (junior) {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
1269
     * A taxon name can be used by several distinct {@link eu.etaxonomy.cdm.model.reference.Reference references} but only once
1270
     * within a taxonomic treatment (identified by one reference).
1271
     *
1272
     * @see	#getTaxa()
1273
     * @see	#getSynonyms()
1274
     */
1275
    public Set<TaxonBase> getTaxonBases() {
1276
        if(taxonBases == null) {
1277
            this.taxonBases = new HashSet<TaxonBase>();
1278
        }
1279
        return this.taxonBases;
1280
    }
1281

    
1282
    /**
1283
     * Adds a new {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon base}
1284
     * to the set of taxon bases using <i>this</i> taxon name.
1285
     *
1286
     * @param  taxonBase  the taxon base to be added
1287
     * @see 			  #getTaxonBases()
1288
     * @see 			  #removeTaxonBase(TaxonBase)
1289
     */
1290
    //TODO protected
1291
    public void addTaxonBase(TaxonBase taxonBase){
1292
        Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonNameBase.class});
1293
        ReflectionUtils.makeAccessible(method);
1294
        ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {this});
1295
        taxonBases.add(taxonBase);
1296
    }
1297
    /**
1298
     * Removes one element from the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1299
     *
1300
     * @param  taxonBase	the taxon base which should be removed from the corresponding set
1301
     * @see    				#getTaxonBases()
1302
     * @see    				#addTaxonBase(TaxonBase)
1303
     */
1304
    public void removeTaxonBase(TaxonBase taxonBase){
1305
        Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonNameBase.class});
1306
        ReflectionUtils.makeAccessible(method);
1307
        ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {null});
1308

    
1309

    
1310
    }
1311

    
1312
    /**
1313
     * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Taxon taxa} ("accepted/correct" names according to any
1314
     * reference) that are based on <i>this</i> taxon name. This set is a subset of
1315
     * the set returned by getTaxonBases().
1316
     *
1317
     * @see	eu.etaxonomy.cdm.model.taxon.Taxon
1318
     * @see	#getTaxonBases()
1319
     * @see	#getSynonyms()
1320
     */
1321
    @Transient
1322
    public Set<Taxon> getTaxa(){
1323
        Set<Taxon> result = new HashSet<Taxon>();
1324
        for (TaxonBase taxonBase : this.taxonBases){
1325
            if (taxonBase instanceof Taxon){
1326
                result.add((Taxon)taxonBase);
1327
            }
1328
        }
1329
        return result;
1330
    }
1331

    
1332
    /**
1333
     * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Synonym (junior) synonyms} (according to any
1334
     * reference) that are based on <i>this</i> taxon name. This set is a subset of
1335
     * the set returned by getTaxonBases().
1336
     *
1337
     * @see	eu.etaxonomy.cdm.model.taxon.Synonym
1338
     * @see	#getTaxonBases()
1339
     * @see	#getTaxa()
1340
     */
1341
    @Transient
1342
    public Set<Synonym> getSynonyms() {
1343
        Set<Synonym> result = new HashSet<Synonym>();
1344
        for (TaxonBase taxonBase : this.taxonBases){
1345
            if (taxonBase instanceof Synonym){
1346
                result.add((Synonym)taxonBase);
1347
            }
1348
        }
1349
        return result;
1350
    }
1351

    
1352

    
1353
// *********** DESCRIPTIONS *************************************
1354

    
1355
    /**
1356
     * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1357
     * to <i>this</i> taxon name. A taxon name description is a piece of information
1358
     * concerning the taxon name like for instance the content of its first
1359
     * publication (protolog) or a picture of this publication.
1360
     *
1361
     * @see	#addDescription(TaxonNameDescription)
1362
     * @see	#removeDescription(TaxonNameDescription)
1363
     * @see	eu.etaxonomy.cdm.model.description.TaxonNameDescription
1364
     */
1365
    public Set<TaxonNameDescription> getDescriptions() {
1366
        return descriptions;
1367
    }
1368

    
1369
    /**
1370
     * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name description}
1371
     * to the set of taxon name descriptions assigned to <i>this</i> taxon name. The
1372
     * content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute} of the
1373
     * taxon name description itself will be replaced with <i>this</i> taxon name.
1374
     *
1375
     * @param  description  the taxon name description to be added
1376
     * @see					#getDescriptions()
1377
     * @see 			  	#removeDescription(TaxonNameDescription)
1378
     */
1379
    public void addDescription(TaxonNameDescription description) {
1380
        java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonNameBase.class);
1381
        ReflectionUtils.makeAccessible(field);
1382
        ReflectionUtils.setField(field, description, this);
1383
        descriptions.add(description);
1384
    }
1385
    /**
1386
     * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1387
     * to <i>this</i> taxon name. The content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute}
1388
     * of the description itself will be set to "null".
1389
     *
1390
     * @param  description  the taxon name description which should be removed
1391
     * @see     		  	#getDescriptions()
1392
     * @see     		  	#addDescription(TaxonNameDescription)
1393
     * @see 			  	eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName()
1394
     */
1395
    public void removeDescription(TaxonNameDescription description) {
1396
        java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonNameBase.class);
1397
        ReflectionUtils.makeAccessible(field);
1398
        ReflectionUtils.setField(field, description, null);
1399
        descriptions.remove(description);
1400
    }
1401

    
1402
// *********** HOMOTYPIC GROUP METHODS **************************************************
1403

    
1404
    @Transient
1405
    public void mergeHomotypicGroups(TaxonNameBase name){
1406
        this.getHomotypicalGroup().merge(name.getHomotypicalGroup());
1407
        //HomotypicalGroup thatGroup = name.homotypicalGroup;
1408
        name.setHomotypicalGroup(this.homotypicalGroup);
1409
    }
1410

    
1411
    /**
1412
     * Returns the boolean value indicating whether a given taxon name belongs
1413
     * to the same {@link HomotypicalGroup homotypical group} as <i>this</i> taxon name (true)
1414
     * or not (false). Returns "true" only if the homotypical groups of both
1415
     * taxon names exist and if they are identical.
1416
     *
1417
     * @param	homoTypicName  the taxon name the homotypical group of which is to be checked
1418
     * @return  			   the boolean value of the check
1419
     * @see     			   HomotypicalGroup
1420
     */
1421
    @Transient
1422
    public boolean isHomotypic(TaxonNameBase homoTypicName) {
1423
        if (homoTypicName == null) {
1424
            return false;
1425
        }
1426
        HomotypicalGroup homotypicGroup = homoTypicName.getHomotypicalGroup();
1427
        if (homotypicGroup == null || this.getHomotypicalGroup() == null) {
1428
            return false;
1429
        }
1430
        if (homotypicGroup.equals(this.getHomotypicalGroup())) {
1431
            return true;
1432
        }
1433
        return false;
1434
    }
1435

    
1436

    
1437
    /**
1438
     * Checks whether name is a basionym for ALL names
1439
     * in its homotypical group.
1440
     * Returns <code>false</code> if there are no other names in the group
1441
     * @param name
1442
     * @return
1443
     */
1444
    @Transient
1445
    public boolean isGroupsBasionym() {
1446
    	if (homotypicalGroup == null){
1447
    		homotypicalGroup = HomotypicalGroup.NewInstance();
1448
    		homotypicalGroup.addTypifiedName(this);
1449
    	}
1450
        Set<TaxonNameBase> typifiedNames = homotypicalGroup.getTypifiedNames();
1451

    
1452
        // Check whether there are any other names in the group
1453
        if (typifiedNames.size() == 1) {
1454
                return false;
1455
        }
1456

    
1457
        boolean isBasionymToAll = true;
1458

    
1459
        for (TaxonNameBase taxonName : typifiedNames) {
1460
                if (!taxonName.equals(this)) {
1461
                        if (! isBasionymFor(taxonName)) {
1462
                                return false;
1463
                        }
1464
                }
1465
        }
1466
        return true;
1467
    }
1468

    
1469
    /**
1470
     * Checks whether a basionym relationship exists between fromName and toName.
1471
     *
1472
     * @param fromName
1473
     * @param toName
1474
     * @return
1475
     */
1476
    @Transient
1477
    public boolean isBasionymFor(TaxonNameBase newCombinationName) {
1478
            Set<NameRelationship> relations = newCombinationName.getRelationsToThisName();
1479
            for (NameRelationship relation : relations) {
1480
                    if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1481
                                    relation.getFromName().equals(this)) {
1482
                            return true;
1483
                    }
1484
            }
1485
            return false;
1486
    }
1487

    
1488
    /**
1489
     * Creates a basionym relationship to all other names in this names homotypical
1490
     * group.
1491
     *
1492
     * @see HomotypicalGroup.setGroupBasionym(TaxonNameBase basionymName)
1493
     */
1494
    @Transient
1495
    public void makeGroupsBasionym() {
1496
        this.homotypicalGroup.setGroupBasionym(this);
1497
    }
1498

    
1499

    
1500
//*********  Rank comparison shortcuts   ********************//
1501
    /**
1502
     * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1503
     * taxon name is higher than the genus rank (true) or not (false).
1504
     * Suprageneric non viral names are monomials.
1505
     * Returns false if rank is null.
1506
     *
1507
     * @see  #isGenus()
1508
     * @see  #isInfraGeneric()
1509
     * @see  #isSpecies()
1510
     * @see  #isInfraSpecific()
1511
     */
1512
    @Transient
1513
    public boolean isSupraGeneric() {
1514
        if (rank == null){
1515
            return false;
1516
        }
1517
        return getRank().isSupraGeneric();
1518
    }
1519
    /**
1520
     * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1521
     * taxon name is the genus rank (true) or not (false). Non viral names with
1522
     * genus rank are monomials. Returns false if rank is null.
1523
     *
1524
     * @see  #isSupraGeneric()
1525
     * @see  #isInfraGeneric()
1526
     * @see  #isSpecies()
1527
     * @see  #isInfraSpecific()
1528
     */
1529
    @Transient
1530
    public boolean isGenus() {
1531
        if (rank == null){
1532
            return false;
1533
        }
1534
        return getRank().isGenus();
1535
    }
1536
    /**
1537
     * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1538
     * taxon name is higher than the species rank and lower than the
1539
     * genus rank (true) or not (false). Infrageneric non viral names are
1540
     * binomials. Returns false if rank is null.
1541
     *
1542
     * @see  #isSupraGeneric()
1543
     * @see  #isGenus()
1544
     * @see  #isSpecies()
1545
     * @see  #isInfraSpecific()
1546
     */
1547
    @Transient
1548
    public boolean isInfraGeneric() {
1549
        if (rank == null){
1550
            return false;
1551
        }
1552
        return getRank().isInfraGeneric();
1553
    }
1554

    
1555
    /**
1556
     * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1557
     * taxon name is higher than the species rank (true) or not (false).
1558
     * Returns false if rank is null.
1559
     *
1560
     * @see  #isGenus()
1561
     * @see  #isInfraGeneric()
1562
     * @see  #isSpecies()
1563
     * @see  #isInfraSpecific()
1564
     */
1565
    @Transient
1566
    public boolean isSupraSpecific(){
1567
        if (rank == null) {
1568
            return false;
1569
        }
1570
        return getRank().isHigher(Rank.SPECIES());
1571
    }
1572

    
1573
    /**
1574
     * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1575
     * taxon name is the species rank (true) or not (false). Non viral names
1576
     * with species rank are binomials.
1577
     * Returns false if rank is null.
1578
     *
1579
     * @see  #isSupraGeneric()
1580
     * @see  #isGenus()
1581
     * @see  #isInfraGeneric()
1582
     * @see  #isInfraSpecific()
1583
     */
1584
    @Transient
1585
    public boolean isSpecies() {
1586
        if (rank == null){
1587
            return false;
1588
        }
1589
        return getRank().isSpecies();
1590
    }
1591
    /**
1592
     * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1593
     * taxon name is lower than the species rank (true) or not (false).
1594
     * Infraspecific non viral names are trinomials.
1595
     * Returns false if rank is null.
1596
     *
1597
     * @see  #isSupraGeneric()
1598
     * @see  #isGenus()
1599
     * @see  #isInfraGeneric()
1600
     * @see  #isSpecies()
1601
     */
1602
    @Transient
1603
    public boolean isInfraSpecific() {
1604
        if (rank == null){
1605
            return false;
1606
        }
1607
        return getRank().isInfraSpecific();
1608
    }
1609

    
1610
    /**
1611
     * Returns true if this name's rank indicates a rank that aggregates species like species
1612
     * aggregates or species groups, false otherwise. This methods currently returns false
1613
     * for all user defined ranks.
1614
     *
1615
     *@see Rank#isSpeciesAggregate()
1616
     *
1617
     * @return
1618
     */
1619
    @Transient
1620
    public boolean isSpeciesAggregate() {
1621
        if (rank == null){
1622
            return false;
1623
        }
1624
        return getRank().isSpeciesAggregate();
1625
    }
1626

    
1627

    
1628
    /**
1629
     * Returns null as the {@link NomenclaturalCode nomenclatural code} that governs
1630
     * the construction of <i>this</i> taxon name since there is no specific
1631
     * nomenclatural code defined. The real implementention takes place in the
1632
     * subclasses {@link ViralName ViralName}, {@link BacterialName BacterialName},
1633
     * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
1634
     * {@link ZoologicalName ZoologicalName}. Each taxon name is governed by one
1635
     * and only one nomenclatural code.
1636
     *
1637
     * @return  null
1638
     * @see  	#isCodeCompliant()
1639
     * @see  	#getHasProblem()
1640
     */
1641
    abstract public NomenclaturalCode getNomenclaturalCode();
1642

    
1643

    
1644
    /**
1645
     * Generates and returns the string with the scientific name of <i>this</i>
1646
     * taxon name (only non viral taxon names can be generated from their
1647
     * components). This string may be stored in the inherited
1648
     * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
1649
     * This method overrides the generic and inherited
1650
     * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() method} from
1651
     * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
1652
     *
1653
     * @return  the string with the composed name of this non viral taxon name with authorship (and maybe year)
1654
     * @see  	eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1655
     * @see  	eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
1656
     */
1657
//	@Override
1658
//	public abstract String generateTitle();
1659

    
1660
    /**
1661
     * Creates a basionym relationship between this name and
1662
     * 	each name in its homotypic group.
1663
     *
1664
     * @param basionymName
1665
     */
1666
    @Transient
1667
    public void setAsGroupsBasionym() {
1668

    
1669

    
1670
        HomotypicalGroup homotypicalGroup = this.getHomotypicalGroup();
1671

    
1672

    
1673
        if (homotypicalGroup == null) {
1674
            return;
1675
        }
1676

    
1677
        Set<NameRelationship> relations = new HashSet<NameRelationship>();
1678
        Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
1679

    
1680
        for(TaxonNameBase<?, ?> typifiedName : homotypicalGroup.getTypifiedNames()){
1681

    
1682
            Set<NameRelationship> nameRelations = typifiedName.getRelationsFromThisName();
1683

    
1684
            for(NameRelationship nameRelation : nameRelations){
1685
                relations.add(nameRelation);
1686
            }
1687
        }
1688

    
1689
        for (NameRelationship relation : relations) {
1690

    
1691
            // If this is a basionym relation, and toName is in the homotypical group,
1692
            //	remove the relationship.
1693
            if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1694
                    relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
1695
                removeRelations.add(relation);
1696
            }
1697
        }
1698

    
1699
        // Removing relations from a set through which we are iterating causes a
1700
        //	ConcurrentModificationException. Therefore, we delete the targeted
1701
        //	relations in a second step.
1702
        for (NameRelationship relation : removeRelations) {
1703
            this.removeNameRelationship(relation);
1704
        }
1705

    
1706

    
1707
        for (TaxonNameBase<?, ?> name : homotypicalGroup.getTypifiedNames()) {
1708
            if (!name.equals(this)) {
1709

    
1710
                // First check whether the relationship already exists
1711
                if (!this.isBasionymFor(name)) {
1712

    
1713
                    // Then create it
1714
                    name.addRelationshipFromName(this,
1715
                            NameRelationshipType.BASIONYM(), null);
1716
                }
1717
            }
1718
        }
1719
    }
1720

    
1721
    /**
1722
     * Removes basionym relationship between this name and
1723
     * 	each name in its homotypic group.
1724
     *
1725
     * @param basionymName
1726
     */
1727
    @Transient
1728
    public void removeAsGroupsBasionym() {
1729

    
1730
        HomotypicalGroup homotypicalGroup = this.getHomotypicalGroup();
1731

    
1732
        if (homotypicalGroup == null) {
1733
            return;
1734
        }
1735

    
1736
        Set<NameRelationship> relations = new HashSet<NameRelationship>();
1737
        Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
1738

    
1739
        for(TaxonNameBase<?, ?> typifiedName : homotypicalGroup.getTypifiedNames()){
1740

    
1741
            Set<NameRelationship> nameRelations = typifiedName.getRelationsFromThisName();
1742

    
1743
            for(NameRelationship nameRelation : nameRelations){
1744
                relations.add(nameRelation);
1745
            }
1746
        }
1747

    
1748
        for (NameRelationship relation : relations) {
1749

    
1750
            // If this is a basionym relation, and toName is in the homotypical group,
1751
            //	and fromName is basionymName, remove the relationship.
1752
            if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1753
                    relation.getFromName().equals(this) &&
1754
                    relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
1755
                removeRelations.add(relation);
1756
            }
1757
        }
1758

    
1759
        // Removing relations from a set through which we are iterating causes a
1760
        //	ConcurrentModificationException. Therefore, we delete the targeted
1761
        //	relations in a second step.
1762
        for (NameRelationship relation : removeRelations) {
1763
            this.removeNameRelationship(relation);
1764
        }
1765
    }
1766

    
1767
//*********************** CLONE ********************************************************/
1768

    
1769
    /**
1770
     * Clones <i>this</i> taxon name. This is a shortcut that enables to create
1771
     * a new instance that differs only slightly from <i>this</i> taxon name by
1772
     * modifying only some of the attributes.<BR><BR>
1773
     * Usages of this name in a taxon concept are <b>not</b> cloned.<BR>
1774
     * <b>The name gets a newly created homotypical group</b><BR>
1775
     * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1776
     * {@link TaxonNameDescription Name descriptions} are cloned and not reused.<BR>
1777
     * {@link TypeDesignationBase Type designations} are cloned and not reused.<BR>
1778
     *
1779
     * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1780
     * @see java.lang.Object#clone()
1781
     */
1782
    @Override
1783
    public Object clone() {
1784
        TaxonNameBase result;
1785
        try {
1786
            result = (TaxonNameBase)super.clone();
1787

    
1788
            //taxonBases -> empty
1789
            result.taxonBases = new HashSet<TaxonBase>();
1790

    
1791
            //empty caches
1792
            if (! protectedFullTitleCache){
1793
                result.fullTitleCache = null;
1794
            }
1795

    
1796
            //descriptions
1797
            result.descriptions = new HashSet<TaxonNameDescription>();
1798
            for (TaxonNameDescription taxonNameDescription : getDescriptions()){
1799
                TaxonNameDescription newDescription = (TaxonNameDescription)taxonNameDescription.clone();
1800
                result.descriptions.add(newDescription);
1801
            }
1802

    
1803
            //status
1804
            result.status = new HashSet<NomenclaturalStatus>();
1805
            for (NomenclaturalStatus nomenclaturalStatus : getStatus()){
1806
                NomenclaturalStatus newStatus = (NomenclaturalStatus)nomenclaturalStatus.clone();
1807
                result.status.add(newStatus);
1808
            }
1809

    
1810

    
1811
            //To Relations
1812
            result.relationsToThisName = new HashSet<NameRelationship>();
1813
            for (NameRelationship toRelationship : getRelationsToThisName()){
1814
                NameRelationship newRelationship = (NameRelationship)toRelationship.clone();
1815
                newRelationship.setRelatedTo(result);
1816
                result.relationsToThisName.add(newRelationship);
1817
            }
1818

    
1819
            //From Relations
1820
            result.relationsFromThisName = new HashSet<NameRelationship>();
1821
            for (NameRelationship fromRelationship : getRelationsFromThisName()){
1822
                NameRelationship newRelationship = (NameRelationship)fromRelationship.clone();
1823
                newRelationship.setRelatedFrom(result);
1824
                result.relationsFromThisName.add(newRelationship);
1825
            }
1826

    
1827
            //type designations
1828
            result.typeDesignations = new HashSet<TypeDesignationBase>();
1829
            for (TypeDesignationBase typeDesignation : getTypeDesignations()){
1830
                TypeDesignationBase newDesignation = (TypeDesignationBase)typeDesignation.clone();
1831
                result.typeDesignations.add(newDesignation);
1832
                newDesignation.addTypifiedName(result);
1833
            }
1834

    
1835
            //homotypicalGroup
1836
            //TODO still needs to be discussed
1837
            result.homotypicalGroup = HomotypicalGroup.NewInstance();
1838
            result.homotypicalGroup.addTypifiedName(this);
1839

    
1840
            //no changes to: appendedPharse, nomenclaturalReference,
1841
            //nomenclaturalMicroReference, parsingProblem, problemEnds, problemStarts
1842
            //protectedFullTitleCache, rank
1843
            return result;
1844
        } catch (CloneNotSupportedException e) {
1845
            logger.warn("Object does not implement cloneable");
1846
            e.printStackTrace();
1847
            return null;
1848
        }
1849

    
1850
    }
1851
}
(21-21/28)