Project

General

Profile

Download (75.6 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.ArrayList;
14
import java.util.HashSet;
15
import java.util.Iterator;
16
import java.util.List;
17
import java.util.Map;
18
import java.util.Set;
19
import java.util.concurrent.CopyOnWriteArrayList;
20

    
21
import javax.persistence.Column;
22
import javax.persistence.Entity;
23
import javax.persistence.FetchType;
24
import javax.persistence.Inheritance;
25
import javax.persistence.InheritanceType;
26
import javax.persistence.JoinTable;
27
import javax.persistence.ManyToMany;
28
import javax.persistence.ManyToOne;
29
import javax.persistence.OneToMany;
30
import javax.persistence.Transient;
31
import javax.validation.Valid;
32
import javax.validation.constraints.NotNull;
33
import javax.validation.constraints.Size;
34
import javax.xml.bind.annotation.XmlAccessType;
35
import javax.xml.bind.annotation.XmlAccessorType;
36
import javax.xml.bind.annotation.XmlAttribute;
37
import javax.xml.bind.annotation.XmlElement;
38
import javax.xml.bind.annotation.XmlElementWrapper;
39
import javax.xml.bind.annotation.XmlIDREF;
40
import javax.xml.bind.annotation.XmlRootElement;
41
import javax.xml.bind.annotation.XmlSchemaType;
42
import javax.xml.bind.annotation.XmlType;
43

    
44
import org.apache.log4j.Logger;
45
import org.hibernate.annotations.Cascade;
46
import org.hibernate.annotations.CascadeType;
47
import org.hibernate.annotations.Index;
48
import org.hibernate.annotations.Table;
49
import org.hibernate.envers.Audited;
50
import org.hibernate.search.annotations.Field;
51
import org.hibernate.search.annotations.IndexedEmbedded;
52
import org.hibernate.validator.constraints.NotEmpty;
53
import org.springframework.util.ReflectionUtils;
54

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

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

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

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

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

    
142
    @XmlElement(name = "AppendedPhrase")
143
    @Field
144
    @CacheUpdate(value ="nameCache")
145
    //TODO Val #3379
146
//    @NullOrNotEmpty
147
    @Size(max = 255)
148
    private String appendedPhrase;
149

    
150
    @XmlElement(name = "NomenclaturalMicroReference")
151
    @Field
152
    @CacheUpdate(noUpdate ="titleCache")
153
    //TODO Val #3379
154
//    @NullOrNotEmpty
155
    @Size(max = 255)
156
    private String nomenclaturalMicroReference;
157

    
158
    @XmlAttribute
159
    @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
160
    private int parsingProblem = 0;
161

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

    
166
    @XmlAttribute
167
    @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
168
    private int problemEnds = -1;
169

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

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

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

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

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

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

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

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

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

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

    
313
//********* METHODS **************************************/
314

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

    
329
    public abstract String generateFullTitle();
330

    
331

    
332

    
333
    @Transient
334
    public List<TaggedText> getTaggedName(){
335
        return getCacheStrategy().getTaggedTitle(this);
336
    }
337

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

    
349

    
350
    public void setFullTitleCache(String fullTitleCache){
351
        setFullTitleCache(fullTitleCache, PROTECTED);
352
    }
353

    
354
    public void setFullTitleCache(String fullTitleCache, boolean protectCache){
355
        fullTitleCache = getTruncatedCache(fullTitleCache);
356
        this.fullTitleCache = fullTitleCache;
357
        this.setProtectedFullTitleCache(protectCache);
358
    }
359

    
360

    
361
    public boolean isProtectedFullTitleCache() {
362
        return protectedFullTitleCache;
363
    }
364

    
365
    public void setProtectedFullTitleCache(boolean protectedFullTitleCache) {
366
        this.protectedFullTitleCache = protectedFullTitleCache;
367
    }
368

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

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

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

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

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

    
499
        TaxonNameBase fromName = nameRelation.getFromName();
500
        TaxonNameBase toName = nameRelation.getToName();
501

    
502
        if (nameRelation != null) {
503
            nameRelation.setToName(null);
504
            nameRelation.setFromName(null);
505
        }
506

    
507
        if (fromName != null) {
508
            fromName.removeNameRelationship(nameRelation);
509
        }
510

    
511
        if (toName != null) {
512
            toName.removeNameRelationship(nameRelation);
513
        }
514

    
515
        this.relationsToThisName.remove(nameRelation);
516
        this.relationsFromThisName.remove(nameRelation);
517
    }
518

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

    
532

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

    
559

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

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

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

    
606
    /**
607
     * Adds a new {@link NomenclaturalStatus nomenclatural status}
608
     * to <i>this</i> taxon name's set of nomenclatural status.
609
     *
610
     * @param  nomStatus  the nomenclatural status to be added
611
     * @see 			  #getStatus()
612
     */
613
    public void addStatus(NomenclaturalStatus nomStatus) {
614
        this.status.add(nomStatus);
615
    }
616

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

    
631

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

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

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

    
711
    /**
712
     * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
713
     * The basionym {@link NameRelationship relationship} will be added to <i>this</i> taxon name
714
     * and to the basionym. The basionym cannot have itself a basionym.
715
     * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
716
     * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
717
     *
718
     * @param  basionym		the taxon name to be set as the basionym of <i>this</i> taxon name
719
     * @see  				#getBasionym()
720
     * @see  				#addBasionym(TaxonNameBase, String)
721
     */
722
    public void addBasionym(T basionym){
723
        addBasionym(basionym, null, null, null);
724
    }
725
    /**
726
     * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name
727
     * and keeps the nomenclatural rule considered for it. The basionym
728
     * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the basionym.
729
     * The basionym cannot have itself a basionym.
730
     * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
731
     * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
732
     *
733
     * @param  basionym			the taxon name to be set as the basionym of <i>this</i> taxon name
734
     * @param  ruleConsidered	the string identifying the nomenclatural rule
735
     * @return
736
     * @see  					#getBasionym()
737
     * @see  					#addBasionym(TaxonNameBase)
738
     */
739
    public NameRelationship addBasionym(T basionym, Reference citation, String microcitation, String ruleConsidered){
740
        if (basionym != null){
741
            return basionym.addRelationshipToName(this, NameRelationshipType.BASIONYM(), citation, microcitation, ruleConsidered);
742
        }else{
743
            return null;
744
        }
745
    }
746
    
747
    /**
748
     * Returns the set of taxon names which are the {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonyms} of <i>this</i> taxon name.
749
     *  
750
     */
751
    @Transient
752
    public Set<TaxonNameBase> getReplacedSynonyms(){
753
        Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
754
        Set<NameRelationship> rels = this.getRelationsToThisName();
755
        for (NameRelationship rel : rels){
756
            if (rel.getType().isReplacedSynonymRelation()){
757
                TaxonNameBase replacedSynonym = rel.getFromName();
758
                result.add(replacedSynonym);
759
            }
760
        }
761
        return result;
762
    }
763

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

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

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

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

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

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

    
863
    /**
864
     * @see  #getAppendedPhrase()
865
     */
866
    public void setAppendedPhrase(String appendedPhrase){
867
        this.appendedPhrase = appendedPhrase;
868
    }
869

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

    
889
    /* (non-Javadoc)
890
     * @see eu.etaxonomy.cdm.model.common.IParsable#getHasProblem()
891
     */
892
    @Override
893
    public int getParsingProblem(){
894
        return this.parsingProblem;
895
    }
896

    
897
    /* (non-Javadoc)
898
     * @see eu.etaxonomy.cdm.model.common.IParsable#setHasProblem(int)
899
     */
900
    @Override
901
    public void setParsingProblem(int parsingProblem){
902
        this.parsingProblem = parsingProblem;
903
    }
904

    
905
    /* (non-Javadoc)
906
     * @see eu.etaxonomy.cdm.model.common.IParsable#addProblem(eu.etaxonomy.cdm.strategy.parser.NameParserWarning)
907
     */
908
    @Override
909
    public void addParsingProblem(ParserProblem problem){
910
        parsingProblem = ParserProblem.addProblem(parsingProblem, problem);
911
    }
912

    
913
    /* (non-Javadoc)
914
     * @see eu.etaxonomy.cdm.model.common.IParsable#removeParsingProblem(eu.etaxonomy.cdm.strategy.parser.ParserProblem)
915
     */
916
    @Override
917
    public void removeParsingProblem(ParserProblem problem) {
918
        parsingProblem = ParserProblem.removeProblem(parsingProblem, problem);
919
    }
920

    
921
    /**
922
     * @param warnings
923
     */
924
    public void addParsingProblems(int problems){
925
        parsingProblem = ParserProblem.addProblems(parsingProblem, problems);
926
    }
927

    
928
    /* (non-Javadoc)
929
     * @see eu.etaxonomy.cdm.model.common.IParsable#hasProblem()
930
     */
931
    @Override
932
    public boolean hasProblem(){
933
        return parsingProblem != 0;
934
    }
935

    
936

    
937

    
938
    /* (non-Javadoc)
939
     * @see eu.etaxonomy.cdm.model.common.IParsable#hasProblem(eu.etaxonomy.cdm.strategy.parser.ParserProblem)
940
     */
941
    @Override
942
    public boolean hasProblem(ParserProblem problem) {
943
        return getParsingProblems().contains(problem);
944
    }
945

    
946

    
947
    /* (non-Javadoc)
948
     * @see eu.etaxonomy.cdm.model.common.IParsable#problemStarts()
949
     */
950
    @Override
951
    public int getProblemStarts(){
952
        return this.problemStarts;
953
    }
954

    
955
    /* (non-Javadoc)
956
     * @see eu.etaxonomy.cdm.model.common.IParsable#setProblemStarts(int)
957
     */
958
    @Override
959
    public void setProblemStarts(int start) {
960
        this.problemStarts = start;
961
    }
962

    
963
    /* (non-Javadoc)
964
     * @see eu.etaxonomy.cdm.model.common.IParsable#problemEnds()
965
     */
966
    @Override
967
    public int getProblemEnds(){
968
        return this.problemEnds;
969
    }
970

    
971
    /* (non-Javadoc)
972
     * @see eu.etaxonomy.cdm.model.common.IParsable#setProblemEnds(int)
973
     */
974
    @Override
975
    public void setProblemEnds(int end) {
976
        this.problemEnds = end;
977
    }
978

    
979
//*********************** TYPE DESIGNATION *********************************************//
980

    
981
    /**
982
     * Returns the set of {@link TypeDesignationBase type designations} assigned
983
     * to <i>this</i> taxon name.
984
     * @see     NameTypeDesignation
985
     * @see     SpecimenTypeDesignation
986
     */
987
    public Set<TypeDesignationBase> getTypeDesignations() {
988
        if(typeDesignations == null) {
989
            this.typeDesignations = new HashSet<TypeDesignationBase>();
990
        }
991
        return typeDesignations;
992
    }
993

    
994
    /**
995
     * Removes one element from the set of {@link TypeDesignationBase type designations} assigned to
996
     * <i>this</i> taxon name. The type designation itself will be nullified.
997
     *
998
     * @param  typeDesignation  the type designation which should be deleted
999
     */
1000
    @SuppressWarnings("deprecation")
1001
    public void removeTypeDesignation(TypeDesignationBase typeDesignation) {
1002
        this.typeDesignations.remove(typeDesignation);
1003
        typeDesignation.removeTypifiedName(this);
1004
    }
1005

    
1006
    /**
1007
     * Returns the set of {@link SpecimenTypeDesignation specimen type designations} assigned
1008
     * to <i>this</i> taxon name. The {@link Rank rank} of <i>this</i> taxon name is generally
1009
     * "species" or below. The specimen type designations include all the
1010
     * specimens on which the typification of this name is based (which are
1011
     * exclusively used to typify taxon names belonging to the same
1012
     * {@link HomotypicalGroup homotypical group} to which <i>this</i> taxon name
1013
     * belongs) and eventually the status of these designations.
1014
     *
1015
     * @see     SpecimenTypeDesignation
1016
     * @see     NameTypeDesignation
1017
     * @see     HomotypicalGroup
1018
     */
1019
    @Transient
1020
    public Set<SpecimenTypeDesignation> getSpecimenTypeDesignationsOfHomotypicalGroup() {
1021
        return this.getHomotypicalGroup().getSpecimenTypeDesignations();
1022
    }
1023

    
1024
//*********************** NAME TYPE DESIGNATION *********************************************//
1025

    
1026
    /**
1027
     * Returns the set of {@link NameTypeDesignation name type designations} assigned
1028
     * to <i>this</i> taxon name the rank of which must be above "species".
1029
     * The name type designations include all the taxon names used to typify
1030
     * <i>this</i> taxon name and eventually the rejected or conserved status
1031
     * of these designations.
1032
     *
1033
     * @see     NameTypeDesignation
1034
     * @see     SpecimenTypeDesignation
1035
     */
1036
    @Transient
1037
    public Set<NameTypeDesignation> getNameTypeDesignations() {
1038
        Set<NameTypeDesignation> result = new HashSet<NameTypeDesignation>();
1039
        for (TypeDesignationBase typeDesignation : this.typeDesignations){
1040
            if (typeDesignation instanceof NameTypeDesignation){
1041
                result.add((NameTypeDesignation)typeDesignation);
1042
            }
1043
        }
1044
        return result;
1045
    }
1046

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

    
1082
    /**
1083
     * Creates and adds a new {@link NameTypeDesignation name type designation}
1084
     * to <i>this</i> taxon name's set of type designations.
1085
     *
1086
     * @param  typeSpecies				the taxon name to be used as type of <i>this</i> taxon name
1087
     * @param  citation					the reference for this new designation
1088
     * @param  citationMicroReference	the string with the details (generally pages) within the reference
1089
     * @param  originalNameString		the taxon name string used in the reference to assert this designation
1090
     * @param  status                   the name type designation status
1091
     * @param  addToAllHomotypicNames	the boolean indicating whether the name type designation should be
1092
     * 									added to all taxon names of the homotypical group this taxon name belongs to
1093
     * @return
1094
     * @see 			  				#getNameTypeDesignations()
1095
     * @see 			  				NameTypeDesignation
1096
     * @see 			  				TypeDesignationBase#isNotDesignated()
1097
     */
1098
    public NameTypeDesignation addNameTypeDesignation(TaxonNameBase typeSpecies,
1099
                Reference citation,
1100
                String citationMicroReference,
1101
                String originalNameString,
1102
                NameTypeDesignationStatus status,
1103
                boolean addToAllHomotypicNames) {
1104
        NameTypeDesignation nameTypeDesignation = new NameTypeDesignation(typeSpecies, status, citation, citationMicroReference, originalNameString);
1105
        addTypeDesignation(nameTypeDesignation, addToAllHomotypicNames);
1106
        return nameTypeDesignation;
1107
    }
1108

    
1109
//*********************** SPECIMEN TYPE DESIGNATION *********************************************//
1110

    
1111
    /**
1112
     * Returns the set of {@link SpecimenTypeDesignation specimen type designations}
1113
     * that typify <i>this</i> taxon name.
1114
     */
1115
    @Transient
1116
    public Set<SpecimenTypeDesignation> getSpecimenTypeDesignations() {
1117
        Set<SpecimenTypeDesignation> result = new HashSet<SpecimenTypeDesignation>();
1118
        for (TypeDesignationBase typeDesignation : this.typeDesignations){
1119
            if (typeDesignation instanceof SpecimenTypeDesignation){
1120
                result.add((SpecimenTypeDesignation)typeDesignation);
1121
            }
1122
        }
1123
        return result;
1124
    }
1125

    
1126

    
1127
    /**
1128
     * Creates and adds a new {@link SpecimenTypeDesignation specimen type designation}
1129
     * to <i>this</i> taxon name's set of type designations.
1130
     *
1131
     * @param  typeSpecimen				the specimen to be used as a type for <i>this</i> taxon name
1132
     * @param  status					the specimen type designation status
1133
     * @param  citation					the reference for this new specimen type designation
1134
     * @param  citationMicroReference	the string with the details (generally pages) within the reference
1135
     * @param  originalNameString		the taxon name used in the reference to assert this designation
1136
     * @param  isNotDesignated			the boolean status for a specimen type designation without specimen type
1137
     * @param  addToAllHomotypicNames	the boolean indicating whether the specimen type designation should be
1138
     * 									added to all taxon names of the homotypical group the typified
1139
     * 									taxon name belongs to
1140
     * @return
1141
     * @see 			  				#getSpecimenTypeDesignations()
1142
     * @see 			  				SpecimenTypeDesignationStatus
1143
     * @see 			  				SpecimenTypeDesignation
1144
     * @see 			  				TypeDesignationBase#isNotDesignated()
1145
     */
1146
    public SpecimenTypeDesignation addSpecimenTypeDesignation(DerivedUnit typeSpecimen,
1147
                SpecimenTypeDesignationStatus status,
1148
                Reference<?> citation,
1149
                String citationMicroReference,
1150
                String originalNameString,
1151
                boolean isNotDesignated,
1152
                boolean addToAllHomotypicNames) {
1153
        SpecimenTypeDesignation specimenTypeDesignation = new SpecimenTypeDesignation(typeSpecimen, status, citation, citationMicroReference, originalNameString, isNotDesignated);
1154
        addTypeDesignation(specimenTypeDesignation, addToAllHomotypicNames);
1155
        return specimenTypeDesignation;
1156
    }
1157

    
1158
    //used by merge strategy
1159
    private boolean addTypeDesignation(TypeDesignationBase typeDesignation){
1160
        return addTypeDesignation(typeDesignation, true);
1161
    }
1162

    
1163
    /**
1164
     * Adds a {@link TypeDesignationBase type designation} to <code>this</code> taxon name's set of type designations
1165
     *
1166
     * @param typeDesignation			the typeDesignation to be added to <code>this</code> taxon name
1167
     * @param addToAllNames				the boolean indicating whether the type designation should be
1168
     * 									added to all taxon names of the homotypical group the typified
1169
     * 									taxon name belongs to
1170
     * @return							true if the operation was succesful
1171
     *
1172
     * @throws IllegalArgumentException	if the type designation already has typified names, an {@link IllegalArgumentException exception}
1173
     * 									is thrown. We do this to prevent a type designation to be used for multiple taxon names.
1174
     *
1175
     */
1176
    public boolean addTypeDesignation(TypeDesignationBase typeDesignation, boolean addToAllNames){
1177
        //currently typeDesignations are not persisted with the homotypical group
1178
        //so explicit adding to the homotypical group is not necessary.
1179
        if (typeDesignation != null){
1180
            checkHomotypicalGroup(typeDesignation);
1181
            this.typeDesignations.add(typeDesignation);
1182
            typeDesignation.addTypifiedName(this);
1183

    
1184
            if (addToAllNames){
1185
                for (TaxonNameBase taxonName : this.getHomotypicalGroup().getTypifiedNames()){
1186
                    if (taxonName != this){
1187
                        taxonName.addTypeDesignation(typeDesignation, false);
1188
                    }
1189
                }
1190
            }
1191
        }
1192
        return true;
1193
    }
1194

    
1195
    /**
1196
     * Throws an Exception this type designation already has typified names from another homotypical group.
1197
     * @param typeDesignation
1198
     */
1199
    private void checkHomotypicalGroup(TypeDesignationBase typeDesignation) {
1200
        if(typeDesignation.getTypifiedNames().size() > 0){
1201
            Set<HomotypicalGroup> groups = new HashSet<HomotypicalGroup>();
1202
            Set<TaxonNameBase> names = typeDesignation.getTypifiedNames();
1203
            for (TaxonNameBase taxonName: names){
1204
                groups.add(taxonName.getHomotypicalGroup());
1205
            }
1206
            if (groups.size() > 1){
1207
                throw new IllegalArgumentException("TypeDesignation already has typified names from another homotypical group.");
1208
            }
1209
        }
1210
    }
1211

    
1212

    
1213

    
1214
//*********************** HOMOTYPICAL GROUP *********************************************//
1215

    
1216

    
1217
    /**
1218
     * Returns the {@link HomotypicalGroup homotypical group} to which
1219
     * <i>this</i> taxon name belongs. A homotypical group represents all taxon names
1220
     * that share the same types.
1221
     *
1222
     * @see 	HomotypicalGroup
1223
     */
1224

    
1225
    public HomotypicalGroup getHomotypicalGroup() {
1226
    	return homotypicalGroup;
1227
    }
1228

    
1229
    /*
1230
     * @see #getHomotypicalGroup()
1231
     */
1232
    public void setHomotypicalGroup(HomotypicalGroup homotypicalGroup) {
1233
        if (homotypicalGroup == null){
1234
            throw new IllegalArgumentException("HomotypicalGroup of name should never be null but was set to 'null'");
1235
        }
1236
        this.homotypicalGroup = homotypicalGroup;
1237
        if (! homotypicalGroup.typifiedNames.contains(this)){
1238
            homotypicalGroup.addTypifiedName(this);
1239
        }
1240
    }
1241

    
1242

    
1243

    
1244
// *************************************************************************//
1245

    
1246
    /**
1247
     * Returns the complete string containing the
1248
     * {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation() nomenclatural reference citation}
1249
     * and the {@link #getNomenclaturalMicroReference() details} assigned to <i>this</i> taxon name.
1250
     *
1251
     * @return  the string containing the nomenclatural reference of <i>this</i> taxon name
1252
     * @see		eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation()
1253
     * @see		#getNomenclaturalReference()
1254
     * @see		#getNomenclaturalMicroReference()
1255
     */
1256
    @Transient
1257
    public String getCitationString(){
1258
        return getNomenclaturalReference().getNomenclaturalCitation(getNomenclaturalMicroReference());
1259
    }
1260

    
1261
    /**
1262
     * Returns the parsing problems
1263
     * @return
1264
     */
1265
    @Override
1266
    public List<ParserProblem> getParsingProblems(){
1267
        return ParserProblem.warningList(this.parsingProblem);
1268
    }
1269

    
1270
    /**
1271
     * Returns the string containing the publication date (generally only year)
1272
     * of the {@link #getNomenclaturalReference() nomenclatural reference} for <i>this</i> taxon name, null if there is
1273
     * no nomenclatural reference.
1274
     *
1275
     * @return  the string containing the publication date of <i>this</i> taxon name
1276
     * @see		eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getYear()
1277
     */
1278
    @Transient
1279
    public String getReferenceYear(){
1280
        if (this.getNomenclaturalReference() != null ){
1281
            return this.getNomenclaturalReference().getYear();
1282
        }else{
1283
            return null;
1284
        }
1285
    }
1286

    
1287
    /**
1288
     * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1289
     * In this context a taxon base means the use of a taxon name by a reference
1290
     * either as a {@link eu.etaxonomy.cdm.model.taxon.Taxon taxon} ("accepted/correct" name) or
1291
     * as a (junior) {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
1292
     * A taxon name can be used by several distinct {@link eu.etaxonomy.cdm.model.reference.Reference references} but only once
1293
     * within a taxonomic treatment (identified by one reference).
1294
     *
1295
     * @see	#getTaxa()
1296
     * @see	#getSynonyms()
1297
     */
1298
    public Set<TaxonBase> getTaxonBases() {
1299
        if(taxonBases == null) {
1300
            this.taxonBases = new HashSet<TaxonBase>();
1301
        }
1302
        return this.taxonBases;
1303
    }
1304

    
1305
    /**
1306
     * Adds a new {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon base}
1307
     * to the set of taxon bases using <i>this</i> taxon name.
1308
     *
1309
     * @param  taxonBase  the taxon base to be added
1310
     * @see 			  #getTaxonBases()
1311
     * @see 			  #removeTaxonBase(TaxonBase)
1312
     */
1313
    //TODO protected
1314
    public void addTaxonBase(TaxonBase taxonBase){
1315
        Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonNameBase.class});
1316
        ReflectionUtils.makeAccessible(method);
1317
        ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {this});
1318
        taxonBases.add(taxonBase);
1319
    }
1320
    /**
1321
     * Removes one element from the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1322
     *
1323
     * @param  taxonBase	the taxon base which should be removed from the corresponding set
1324
     * @see    				#getTaxonBases()
1325
     * @see    				#addTaxonBase(TaxonBase)
1326
     */
1327
    public void removeTaxonBase(TaxonBase taxonBase){
1328
        Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonNameBase.class});
1329
        ReflectionUtils.makeAccessible(method);
1330
        ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {null});
1331
     
1332
       
1333
    }
1334

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

    
1355
    /**
1356
     * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Synonym (junior) synonyms} (according to any
1357
     * reference) that are based on <i>this</i> taxon name. This set is a subset of
1358
     * the set returned by getTaxonBases().
1359
     *
1360
     * @see	eu.etaxonomy.cdm.model.taxon.Synonym
1361
     * @see	#getTaxonBases()
1362
     * @see	#getTaxa()
1363
     */
1364
    @Transient
1365
    public Set<Synonym> getSynonyms() {
1366
        Set<Synonym> result = new HashSet<Synonym>();
1367
        for (TaxonBase taxonBase : this.taxonBases){
1368
            if (taxonBase instanceof Synonym){
1369
                result.add((Synonym)taxonBase);
1370
            }
1371
        }
1372
        return result;
1373
    }
1374

    
1375

    
1376
// *********** DESCRIPTIONS *************************************
1377

    
1378
    /**
1379
     * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1380
     * to <i>this</i> taxon name. A taxon name description is a piece of information
1381
     * concerning the taxon name like for instance the content of its first
1382
     * publication (protolog) or a picture of this publication.
1383
     *
1384
     * @see	#addDescription(TaxonNameDescription)
1385
     * @see	#removeDescription(TaxonNameDescription)
1386
     * @see	eu.etaxonomy.cdm.model.description.TaxonNameDescription
1387
     */
1388
    public Set<TaxonNameDescription> getDescriptions() {
1389
        return descriptions;
1390
    }
1391

    
1392
    /**
1393
     * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name description}
1394
     * to the set of taxon name descriptions assigned to <i>this</i> taxon name. The
1395
     * content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute} of the
1396
     * taxon name description itself will be replaced with <i>this</i> taxon name.
1397
     *
1398
     * @param  description  the taxon name description to be added
1399
     * @see					#getDescriptions()
1400
     * @see 			  	#removeDescription(TaxonNameDescription)
1401
     */
1402
    public void addDescription(TaxonNameDescription description) {
1403
        java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonNameBase.class);
1404
        ReflectionUtils.makeAccessible(field);
1405
        ReflectionUtils.setField(field, description, this);
1406
        descriptions.add(description);
1407
    }
1408
    /**
1409
     * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1410
     * to <i>this</i> taxon name. The content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute}
1411
     * of the description itself will be set to "null".
1412
     *
1413
     * @param  description  the taxon name description which should be removed
1414
     * @see     		  	#getDescriptions()
1415
     * @see     		  	#addDescription(TaxonNameDescription)
1416
     * @see 			  	eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName()
1417
     */
1418
    public void removeDescription(TaxonNameDescription description) {
1419
        java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonNameBase.class);
1420
        ReflectionUtils.makeAccessible(field);
1421
        ReflectionUtils.setField(field, description, null);
1422
        descriptions.remove(description);
1423
    }
1424

    
1425
// *********** HOMOTYPIC GROUP METHODS **************************************************
1426

    
1427
    @Transient
1428
    public void mergeHomotypicGroups(TaxonNameBase name){
1429
        this.getHomotypicalGroup().merge(name.getHomotypicalGroup());
1430
        //HomotypicalGroup thatGroup = name.homotypicalGroup;
1431
        name.setHomotypicalGroup(this.homotypicalGroup);
1432
    }
1433

    
1434
    /**
1435
     * Returns the boolean value indicating whether a given taxon name belongs
1436
     * to the same {@link HomotypicalGroup homotypical group} as <i>this</i> taxon name (true)
1437
     * or not (false). Returns "true" only if the homotypical groups of both
1438
     * taxon names exist and if they are identical.
1439
     *
1440
     * @param	homoTypicName  the taxon name the homotypical group of which is to be checked
1441
     * @return  			   the boolean value of the check
1442
     * @see     			   HomotypicalGroup
1443
     */
1444
    @Transient
1445
    public boolean isHomotypic(TaxonNameBase homoTypicName) {
1446
        if (homoTypicName == null) {
1447
            return false;
1448
        }
1449
        HomotypicalGroup homotypicGroup = homoTypicName.getHomotypicalGroup();
1450
        if (homotypicGroup == null || this.getHomotypicalGroup() == null) {
1451
            return false;
1452
        }
1453
        if (homotypicGroup.equals(this.getHomotypicalGroup())) {
1454
            return true;
1455
        }
1456
        return false;
1457
    }
1458

    
1459

    
1460
    /**
1461
     * Checks whether name is a basionym for ALL names
1462
     * in its homotypical group.
1463
     * Returns <code>false</code> if there are no other names in the group
1464
     * @param name
1465
     * @return
1466
     */
1467
    @Transient
1468
    public boolean isGroupsBasionym() {
1469
    	if (homotypicalGroup == null){
1470
    		homotypicalGroup = HomotypicalGroup.NewInstance();
1471
    		homotypicalGroup.addTypifiedName(this);
1472
    	}
1473
        Set<TaxonNameBase> typifiedNames = homotypicalGroup.getTypifiedNames();
1474

    
1475
        // Check whether there are any other names in the group
1476
        if (typifiedNames.size() == 1) {
1477
                return false;
1478
        }
1479

    
1480
        boolean isBasionymToAll = true;
1481

    
1482
        for (TaxonNameBase taxonName : typifiedNames) {
1483
                if (!taxonName.equals(this)) {
1484
                        if (! isBasionymFor(taxonName)) {
1485
                                return false;
1486
                        }
1487
                }
1488
        }
1489
        return true;
1490
    }
1491

    
1492
    /**
1493
     * Checks whether a basionym relationship exists between fromName and toName.
1494
     *
1495
     * @param fromName
1496
     * @param toName
1497
     * @return
1498
     */
1499
    @Transient
1500
    public boolean isBasionymFor(TaxonNameBase newCombinationName) {
1501
            Set<NameRelationship> relations = newCombinationName.getRelationsToThisName();
1502
            for (NameRelationship relation : relations) {
1503
                    if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1504
                                    relation.getFromName().equals(this)) {
1505
                            return true;
1506
                    }
1507
            }
1508
            return false;
1509
    }
1510

    
1511
    /**
1512
     * Creates a basionym relationship to all other names in this names homotypical
1513
     * group.
1514
     *
1515
     * @see HomotypicalGroup.setGroupBasionym(TaxonNameBase basionymName)
1516

    
1517
     */
1518
    /* (non-Javadoc)
1519
     * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup#setGroupBasionym(TaxonNameBase)
1520
     */
1521
    @Transient
1522
    public void makeGroupsBasionym() {
1523
        this.homotypicalGroup.setGroupBasionym(this);
1524
    }
1525

    
1526

    
1527
//*********  Rank comparison shortcuts   ********************//
1528
    /**
1529
     * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1530
     * taxon name is higher than the genus rank (true) or not (false).
1531
     * Suprageneric non viral names are monomials.
1532
     * Returns false if rank is null.
1533
     *
1534
     * @see  #isGenus()
1535
     * @see  #isInfraGeneric()
1536
     * @see  #isSpecies()
1537
     * @see  #isInfraSpecific()
1538
     */
1539
    @Transient
1540
    public boolean isSupraGeneric() {
1541
        if (rank == null){
1542
            return false;
1543
        }
1544
        return getRank().isSupraGeneric();
1545
    }
1546
    /**
1547
     * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1548
     * taxon name is the genus rank (true) or not (false). Non viral names with
1549
     * genus rank are monomials. Returns false if rank is null.
1550
     *
1551
     * @see  #isSupraGeneric()
1552
     * @see  #isInfraGeneric()
1553
     * @see  #isSpecies()
1554
     * @see  #isInfraSpecific()
1555
     */
1556
    @Transient
1557
    public boolean isGenus() {
1558
        if (rank == null){
1559
            return false;
1560
        }
1561
        return getRank().isGenus();
1562
    }
1563
    /**
1564
     * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1565
     * taxon name is higher than the species rank and lower than the
1566
     * genus rank (true) or not (false). Infrageneric non viral names are
1567
     * binomials. Returns false if rank is null.
1568
     *
1569
     * @see  #isSupraGeneric()
1570
     * @see  #isGenus()
1571
     * @see  #isSpecies()
1572
     * @see  #isInfraSpecific()
1573
     */
1574
    @Transient
1575
    public boolean isInfraGeneric() {
1576
        if (rank == null){
1577
            return false;
1578
        }
1579
        return getRank().isInfraGeneric();
1580
    }
1581

    
1582
    /**
1583
     * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1584
     * taxon name is higher than the species rank (true) or not (false).
1585
     * Returns false if rank is null.
1586
     *
1587
     * @see  #isGenus()
1588
     * @see  #isInfraGeneric()
1589
     * @see  #isSpecies()
1590
     * @see  #isInfraSpecific()
1591
     */
1592
    @Transient
1593
    public boolean isSupraSpecific(){
1594
        if (rank == null) {
1595
            return false;
1596
        }
1597
        return getRank().isHigher(Rank.SPECIES());
1598
    }
1599

    
1600
    /**
1601
     * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1602
     * taxon name is the species rank (true) or not (false). Non viral names
1603
     * with species rank are binomials.
1604
     * Returns false if rank is null.
1605
     *
1606
     * @see  #isSupraGeneric()
1607
     * @see  #isGenus()
1608
     * @see  #isInfraGeneric()
1609
     * @see  #isInfraSpecific()
1610
     */
1611
    @Transient
1612
    public boolean isSpecies() {
1613
        if (rank == null){
1614
            return false;
1615
        }
1616
        return getRank().isSpecies();
1617
    }
1618
    /**
1619
     * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1620
     * taxon name is lower than the species rank (true) or not (false).
1621
     * Infraspecific non viral names are trinomials.
1622
     * Returns false if rank is null.
1623
     *
1624
     * @see  #isSupraGeneric()
1625
     * @see  #isGenus()
1626
     * @see  #isInfraGeneric()
1627
     * @see  #isSpecies()
1628
     */
1629
    @Transient
1630
    public boolean isInfraSpecific() {
1631
        if (rank == null){
1632
            return false;
1633
        }
1634
        return getRank().isInfraSpecific();
1635
    }
1636
    
1637
    /**
1638
     * Returns true if this name's rank indicates a rank that aggregates species like species 
1639
     * aggregates or species groups, false otherwise. This methods currently returns false 
1640
     * for all user defined ranks.
1641
     *
1642
     *@see Rank#isSpeciesAggregate()
1643
     * 
1644
     * @return
1645
     */
1646
    @Transient
1647
    public boolean isSpeciesAggregate() {
1648
        if (rank == null){
1649
            return false;
1650
        }
1651
        return getRank().isSpeciesAggregate();
1652
    }
1653

    
1654

    
1655
    /**
1656
     * Returns null as the {@link NomenclaturalCode nomenclatural code} that governs
1657
     * the construction of <i>this</i> taxon name since there is no specific
1658
     * nomenclatural code defined. The real implementention takes place in the
1659
     * subclasses {@link ViralName ViralName}, {@link BacterialName BacterialName},
1660
     * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
1661
     * {@link ZoologicalName ZoologicalName}. Each taxon name is governed by one
1662
     * and only one nomenclatural code.
1663
     *
1664
     * @return  null
1665
     * @see  	#isCodeCompliant()
1666
     * @see  	#getHasProblem()
1667
     */
1668
    abstract public NomenclaturalCode getNomenclaturalCode();
1669

    
1670
    /* (non-Javadoc)
1671
     * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1672
     */
1673
    /**
1674
     * Generates and returns the string with the scientific name of <i>this</i>
1675
     * taxon name (only non viral taxon names can be generated from their
1676
     * components). This string may be stored in the inherited
1677
     * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
1678
     * This method overrides the generic and inherited
1679
     * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() method} from
1680
     * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
1681
     *
1682
     * @return  the string with the composed name of this non viral taxon name with authorship (and maybe year)
1683
     * @see  	eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1684
     * @see  	eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
1685
     */
1686
//	@Override
1687
//	public abstract String generateTitle();
1688

    
1689
    /**
1690
     * Creates a basionym relationship between this name and
1691
     * 	each name in its homotypic group.
1692
     *
1693
     * @param basionymName
1694
     */
1695
    @Transient
1696
    public void setAsGroupsBasionym() {
1697

    
1698

    
1699
        HomotypicalGroup homotypicalGroup = this.getHomotypicalGroup();
1700

    
1701
        if (homotypicalGroup == null) {
1702
            return;
1703
        }
1704

    
1705
        Set<NameRelationship> relations = new HashSet<NameRelationship>();
1706
        Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
1707

    
1708
        for(TaxonNameBase<?, ?> typifiedName : homotypicalGroup.getTypifiedNames()){
1709

    
1710
            Set<NameRelationship> nameRelations = typifiedName.getRelationsFromThisName();
1711

    
1712
            for(NameRelationship nameRelation : nameRelations){
1713
                relations.add(nameRelation);
1714
            }
1715
        }
1716

    
1717
        for (NameRelationship relation : relations) {
1718

    
1719
            // If this is a basionym relation, and toName is in the homotypical group,
1720
            //	remove the relationship.
1721
            if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1722
                    relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
1723
                removeRelations.add(relation);
1724
            }
1725
        }
1726

    
1727
        // Removing relations from a set through which we are iterating causes a
1728
        //	ConcurrentModificationException. Therefore, we delete the targeted
1729
        //	relations in a second step.
1730
        for (NameRelationship relation : removeRelations) {
1731
            this.removeNameRelationship(relation);
1732
        }
1733

    
1734

    
1735
        for (TaxonNameBase<?, ?> name : homotypicalGroup.getTypifiedNames()) {
1736
            if (!name.equals(this)) {
1737

    
1738
                // First check whether the relationship already exists
1739
                if (!this.isBasionymFor(name)) {
1740

    
1741
                    // Then create it
1742
                    name.addRelationshipFromName(this,
1743
                            NameRelationshipType.BASIONYM(), null);
1744
                }
1745
            }
1746
        }
1747
    }
1748

    
1749
    /**
1750
     * Removes basionym relationship between this name and
1751
     * 	each name in its homotypic group.
1752
     *
1753
     * @param basionymName
1754
     */
1755
    @Transient
1756
    public void removeAsGroupsBasionym() {
1757

    
1758
        HomotypicalGroup homotypicalGroup = this.getHomotypicalGroup();
1759

    
1760
        if (homotypicalGroup == null) {
1761
            return;
1762
        }
1763

    
1764
        Set<NameRelationship> relations = new HashSet<NameRelationship>();
1765
        Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
1766

    
1767
        for(TaxonNameBase<?, ?> typifiedName : homotypicalGroup.getTypifiedNames()){
1768

    
1769
            Set<NameRelationship> nameRelations = typifiedName.getRelationsFromThisName();
1770

    
1771
            for(NameRelationship nameRelation : nameRelations){
1772
                relations.add(nameRelation);
1773
            }
1774
        }
1775

    
1776
        for (NameRelationship relation : relations) {
1777

    
1778
            // If this is a basionym relation, and toName is in the homotypical group,
1779
            //	and fromName is basionymName, remove the relationship.
1780
            if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1781
                    relation.getFromName().equals(this) &&
1782
                    relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
1783
                removeRelations.add(relation);
1784
            }
1785
        }
1786

    
1787
        // Removing relations from a set through which we are iterating causes a
1788
        //	ConcurrentModificationException. Therefore, we delete the targeted
1789
        //	relations in a second step.
1790
        for (NameRelationship relation : removeRelations) {
1791
            this.removeNameRelationship(relation);
1792
        }
1793
    }
1794

    
1795
//*********************** CLONE ********************************************************/
1796

    
1797
    /**
1798
     * Clones <i>this</i> taxon name. This is a shortcut that enables to create
1799
     * a new instance that differs only slightly from <i>this</i> taxon name by
1800
     * modifying only some of the attributes.<BR><BR>
1801
     * Usages of this name in a taxon concept are <b>not</b> cloned.<BR>
1802
     * <b>The name gets a newly created homotypical group</b><BR>
1803
     * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1804
     * {@link TaxonNameDescription Name descriptions} are cloned and not reused.<BR>
1805
     * {@link TypeDesignationBase Type designations} are cloned and not reused.<BR>
1806
     *
1807
     * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1808
     * @see java.lang.Object#clone()
1809
     */
1810
    @Override
1811
    public Object clone() {
1812
        TaxonNameBase result;
1813
        try {
1814
            result = (TaxonNameBase)super.clone();
1815

    
1816
            //taxonBases -> empty
1817
            result.taxonBases = new HashSet<TaxonBase>();
1818

    
1819
            //empty caches
1820
            if (! protectedFullTitleCache){
1821
                result.fullTitleCache = null;
1822
            }
1823

    
1824
            //descriptions
1825
            result.descriptions = new HashSet<TaxonNameDescription>();
1826
            for (TaxonNameDescription taxonNameDescription : getDescriptions()){
1827
                TaxonNameDescription newDescription = (TaxonNameDescription)taxonNameDescription.clone();
1828
                result.descriptions.add(newDescription);
1829
            }
1830

    
1831
            //status
1832
            result.status = new HashSet<NomenclaturalStatus>();
1833
            for (NomenclaturalStatus nomenclaturalStatus : getStatus()){
1834
                NomenclaturalStatus newStatus = (NomenclaturalStatus)nomenclaturalStatus.clone();
1835
                result.status.add(newStatus);
1836
            }
1837

    
1838

    
1839
            //To Relations
1840
            result.relationsToThisName = new HashSet<NameRelationship>();
1841
            for (NameRelationship toRelationship : getRelationsToThisName()){
1842
                NameRelationship newRelationship = (NameRelationship)toRelationship.clone();
1843
                newRelationship.setRelatedTo(result);
1844
                result.relationsToThisName.add(newRelationship);
1845
            }
1846

    
1847
            //From Relations
1848
            result.relationsFromThisName = new HashSet<NameRelationship>();
1849
            for (NameRelationship fromRelationship : getRelationsFromThisName()){
1850
                NameRelationship newRelationship = (NameRelationship)fromRelationship.clone();
1851
                newRelationship.setRelatedFrom(result);
1852
                result.relationsFromThisName.add(newRelationship);
1853
            }
1854

    
1855
            //type designations
1856
            result.typeDesignations = new HashSet<TypeDesignationBase>();
1857
            for (TypeDesignationBase typeDesignation : getTypeDesignations()){
1858
                TypeDesignationBase newDesignation = (TypeDesignationBase)typeDesignation.clone();
1859
                result.typeDesignations.add(newDesignation);
1860
                newDesignation.addTypifiedName(result);
1861
            }
1862

    
1863
            //homotypicalGroup
1864
            //TODO still needs to be discussed
1865
            homotypicalGroup = HomotypicalGroup.NewInstance();
1866
            homotypicalGroup.addTypifiedName(this);
1867

    
1868
            //no changes to: appendedPharse, nomenclaturalReference,
1869
            //nomenclaturalMicroReference, parsingProblem, problemEnds, problemStarts
1870
            //protectedFullTitleCache, rank
1871
            return result;
1872
        } catch (CloneNotSupportedException e) {
1873
            logger.warn("Object does not implement cloneable");
1874
            e.printStackTrace();
1875
            return null;
1876
        }
1877

    
1878
    }
1879
}
(20-20/27)