Project

General

Profile

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

    
40
import org.apache.log4j.Logger;
41
import org.hibernate.annotations.Cascade;
42
import org.hibernate.annotations.CascadeType;
43
import org.hibernate.annotations.Index;
44
import org.hibernate.annotations.Table;
45
import org.hibernate.envers.Audited;
46
import org.hibernate.search.annotations.Field;
47
import org.hibernate.search.annotations.IndexedEmbedded;
48
import org.hibernate.validator.constraints.NotEmpty;
49
import org.springframework.util.ReflectionUtils;
50

    
51
import eu.etaxonomy.cdm.model.common.IParsable;
52
import eu.etaxonomy.cdm.model.common.IReferencedEntity;
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.Specimen;
58
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
59
import eu.etaxonomy.cdm.model.reference.ReferenceBase;
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.name.CacheUpdate;
64
import eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy;
65
import eu.etaxonomy.cdm.strategy.match.IMatchable;
66
import eu.etaxonomy.cdm.strategy.match.Match;
67
import eu.etaxonomy.cdm.strategy.match.MatchMode;
68
import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
69
import eu.etaxonomy.cdm.strategy.merge.Merge;
70
import eu.etaxonomy.cdm.strategy.merge.MergeMode;
71
import eu.etaxonomy.cdm.strategy.parser.ParserProblem;
72
import eu.etaxonomy.cdm.validation.Level2;
73
import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
74

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

    
118
	@XmlElement(name = "FullTitleCache")
119
	@Column(length=330, name="fullTitleCache")
120
	@Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
121
	@CacheUpdate(noUpdate ="titleCache")
122
	@NotEmpty(groups = Level2.class)
123
	@Size(max = 330)
124
	protected String fullTitleCache;
125
	
126
	//if true titleCache will not be automatically generated/updated
127
	@XmlElement(name = "ProtectedFullTitleCache")
128
	@CacheUpdate(value ="fullTitleCache", noUpdate ="titleCache")
129
	private boolean protectedFullTitleCache;
130
	
131
    @XmlElementWrapper(name = "Descriptions")
132
    @XmlElement(name = "Description")
133
    @OneToMany(mappedBy="taxonName", fetch= FetchType.LAZY) 
134
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
135
	@NotNull
136
	private Set<TaxonNameDescription> descriptions = new HashSet<TaxonNameDescription>();
137
	
138
    @XmlElement(name = "AppendedPhrase")
139
    @Field(index= org.hibernate.search.annotations.Index.TOKENIZED)
140
    @CacheUpdate(value ="nameCache")
141
    @NullOrNotEmpty
142
    @Size(max = 255)
143
	private String appendedPhrase;
144
	
145
    @XmlElement(name = "NomenclaturalMicroReference")
146
    @Field(index= org.hibernate.search.annotations.Index.TOKENIZED)
147
    @CacheUpdate(noUpdate ="titleCache")
148
    @NullOrNotEmpty
149
    @Size(max = 255)
150
	private String nomenclaturalMicroReference;
151
	
152
    @XmlAttribute
153
    @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
154
	private int parsingProblem = 0;
155
	
156
    @XmlAttribute
157
    @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
158
    private int problemStarts = -1;
159
    
160
    @XmlAttribute
161
    @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
162
    private int problemEnds = -1;
163
    
164
    @XmlElementWrapper(name = "TypeDesignations")
165
    @XmlElement(name = "TypeDesignation")
166
    @XmlIDREF
167
    @XmlSchemaType(name = "IDREF")
168
    @ManyToMany(fetch = FetchType.LAZY)
169
	//TODO @Cascade({CascadeType.DELETE_ORPHAN})
170
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
171
	@NotNull
172
	private Set<TypeDesignationBase> typeDesignations = new HashSet<TypeDesignationBase>();
173

    
174
    @XmlElement(name = "HomotypicalGroup")
175
    @XmlIDREF
176
    @XmlSchemaType(name = "IDREF")
177
    @ManyToOne(fetch = FetchType.LAZY)
178
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
179
	@Match(MatchMode.IGNORE)
180
	@CacheUpdate(noUpdate ="titleCache")
181
	@NotNull
182
	private HomotypicalGroup homotypicalGroup;
183

    
184
    @XmlElementWrapper(name = "RelationsFromThisName")
185
    @XmlElement(name = "RelationFromThisName")
186
    @OneToMany(mappedBy="relatedFrom", fetch= FetchType.LAZY)
187
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE_ORPHAN})
188
	@Merge(MergeMode.RELATION)
189
	@NotNull
190
	@Valid
191
	private Set<NameRelationship> relationsFromThisName = new HashSet<NameRelationship>();
192

    
193
    @XmlElementWrapper(name = "RelationsToThisName")
194
    @XmlElement(name = "RelationToThisName")
195
    @XmlIDREF
196
    @XmlSchemaType(name = "IDREF")
197
    @OneToMany(mappedBy="relatedTo", fetch= FetchType.LAZY)
198
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE , CascadeType.DELETE_ORPHAN })
199
	@Merge(MergeMode.RELATION)
200
	@NotNull
201
	@Valid
202
	private Set<NameRelationship> relationsToThisName = new HashSet<NameRelationship>();
203

    
204
    @XmlElementWrapper(name = "NomenclaturalStatuses")
205
    @XmlElement(name = "NomenclaturalStatus")
206
    @OneToMany(fetch= FetchType.LAZY)
207
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE,CascadeType.DELETE,CascadeType.DELETE_ORPHAN})
208
	@NotNull
209
	private Set<NomenclaturalStatus> status = new HashSet<NomenclaturalStatus>();
210

    
211
    @XmlElementWrapper(name = "TaxonBases")
212
    @XmlElement(name = "TaxonBase")
213
    @XmlIDREF
214
    @XmlSchemaType(name = "IDREF")
215
    @OneToMany(mappedBy="name", fetch= FetchType.LAZY)
216
    @NotNull
217
	private Set<TaxonBase> taxonBases = new HashSet<TaxonBase>();
218
    
219
    @XmlElement(name = "Rank")
220
	@XmlIDREF
221
	@XmlSchemaType(name = "IDREF")
222
	@ManyToOne(fetch = FetchType.EAGER)
223
	@CacheUpdate(value ="nameCache")
224
	@NotNull
225
	private Rank rank;
226

    
227
	@XmlElement(name = "NomenclaturalReference")
228
    @XmlIDREF
229
    @XmlSchemaType(name = "IDREF")
230
    @ManyToOne(fetch = FetchType.LAZY)
231
	@Cascade({CascadeType.SAVE_UPDATE})
232
	@CacheUpdate(noUpdate ="titleCache")
233
	@IndexedEmbedded
234
	private ReferenceBase nomenclaturalReference;
235
	
236
// ************* CONSTRUCTORS *************/	
237
	/** 
238
	 * Class constructor: creates a new empty taxon name.
239
	 * 
240
	 * @see #TaxonNameBase(Rank)
241
	 * @see #TaxonNameBase(HomotypicalGroup)
242
	 * @see #TaxonNameBase(Rank, HomotypicalGroup)
243
	 */
244
	public TaxonNameBase() {
245
		super();
246
	}
247
	/** 
248
	 * Class constructor: creates a new taxon name
249
	 * only containing its {@link Rank rank}.
250
	 * 
251
	 * @param  rank  the rank to be assigned to <i>this</i> taxon name
252
	 * @see    		 #TaxonNameBase()
253
	 * @see    		 #TaxonNameBase(HomotypicalGroup)
254
	 * @see    		 #TaxonNameBase(Rank, HomotypicalGroup)
255
	 */
256
	public TaxonNameBase(Rank rank) {
257
		this(rank, null);
258
	}
259
	/** 
260
	 * Class constructor: creates a new taxon name
261
	 * only containing its {@link HomotypicalGroup homotypical group}.
262
	 * The new taxon name will be also added to the set of taxon names
263
	 * belonging to this homotypical group.
264
	 * 
265
	 * @param  homotypicalGroup  the homotypical group to which <i>this</i> taxon name belongs
266
	 * @see    					 #TaxonNameBase()
267
	 * @see    					 #TaxonNameBase(Rank)
268
	 * @see    					 #TaxonNameBase(Rank, HomotypicalGroup)
269
	 */
270
	public TaxonNameBase(HomotypicalGroup homotypicalGroup) {
271
		this(null, homotypicalGroup);
272
	}
273
	/** 
274
	 * Class constructor: creates a new taxon name
275
	 * only containing its {@link Rank rank} and
276
	 * 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  rank  			 the rank to be assigned to <i>this</i> taxon name
281
	 * @param  homotypicalGroup  the homotypical group to which <i>this</i> taxon name belongs
282
	 * @see    					 #TaxonNameBase()
283
	 * @see    					 #TaxonNameBase(Rank)
284
	 * @see    					 #TaxonNameBase(HomotypicalGroup)
285
	 */
286
	public TaxonNameBase(Rank rank, HomotypicalGroup homotypicalGroup) {
287
		super();
288
		this.setRank(rank);
289
		if (homotypicalGroup == null){
290
			homotypicalGroup = new HomotypicalGroup();
291
		}
292
		homotypicalGroup.addTypifiedName(this);
293
	}
294
	
295
	abstract protected Map<String, java.lang.reflect.Field> getAllFields();
296
	
297
//********* METHODS **************************************/
298
	
299
	/**
300
	 * Returns the boolean value "false" since the components of <i>this</i> taxon name
301
	 * cannot follow the rules of a corresponding {@link NomenclaturalCode nomenclatural code}
302
	 * which is not defined for this class. The nomenclature code depends on
303
	 * the concrete name subclass ({@link BacterialName BacterialName},
304
	 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName},
305
	 * {@link ZoologicalName ZoologicalName} or {@link ViralName ViralName}) 
306
	 * to which a taxon name belongs.
307
	 *  
308
	 * @return  false
309
	 */
310
	@Transient
311
	public abstract boolean isCodeCompliant();
312
	
313
	public abstract String generateFullTitle();
314

    
315
	
316

    
317
	@Transient
318
	public List<Object> getTaggedName(){
319
		return getCacheStrategy().getTaggedName(this);
320
	}
321
	
322
	@Transient
323
	public String getFullTitleCache(){
324
		if (protectedFullTitleCache){
325
			return this.fullTitleCache;			
326
		}
327
		if (fullTitleCache == null ){
328
			this.fullTitleCache = getTruncatedCache(generateFullTitle());
329
		}
330
		return fullTitleCache;
331
	}
332

    
333
	
334
    public void setFullTitleCache(String fullTitleCache){
335
		setFullTitleCache(fullTitleCache, PROTECTED);
336
	}
337
	
338
	public void setFullTitleCache(String fullTitleCache, boolean protectCache){
339
		fullTitleCache = getTruncatedCache(fullTitleCache);
340
		this.fullTitleCache = fullTitleCache;
341
		this.setProtectedFullTitleCache(protectCache);
342
	}
343

    
344
	
345
	public boolean isProtectedFullTitleCache() {
346
		return protectedFullTitleCache;
347
	}
348

    
349
	public void setProtectedFullTitleCache(boolean protectedFullTitleCache) {
350
		this.protectedFullTitleCache = protectedFullTitleCache;
351
	}
352
	
353
	/** 
354
	 * Returns the set of all {@link NameRelationship name relationships}
355
	 * in which <i>this</i> taxon name is involved. A taxon name can be both source
356
	 * in some name relationships or target in some others.
357
	 *  
358
	 * @see    #getRelationsToThisName()
359
	 * @see    #getRelationsFromThisName()
360
	 * @see    #addNameRelationship(NameRelationship)
361
	 * @see    #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
362
	 * @see    #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
363
	 */
364
	@Transient
365
	public Set<NameRelationship> getNameRelations() {
366
		Set<NameRelationship> rels = new HashSet<NameRelationship>();
367
		rels.addAll(getRelationsFromThisName());
368
		rels.addAll(getRelationsToThisName());
369
		return rels;
370
	}
371
	
372
	/**
373
	 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
374
	 * and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
375
	 * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
376
	 * 
377
	 * @param toName		  the taxon name of the target for this new name relationship
378
	 * @param type			  the type of this new name relationship
379
	 * @param ruleConsidered  the string which specifies the rule on which this name relationship is based
380
	 * @see    				  #getRelationsToThisName()
381
	 * @see    				  #getNameRelations()
382
	 * @see    				  #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
383
	 * @see    				  #addNameRelationship(NameRelationship)
384
	 */
385
	public void addRelationshipToName(TaxonNameBase toName, NameRelationshipType type, String ruleConsidered){
386
		addRelationshipToName(toName, type, null, null, ruleConsidered);
387
		//		NameRelationship rel = new NameRelationship(toName, this, type, ruleConsidered);
388
	}
389
	
390
	/**
391
	 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
392
	 * and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
393
	 * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
394
	 * 
395
	 * @param toName		  the taxon name of the target for this new name relationship
396
	 * @param type			  the type of this new name relationship
397
	 * @param ruleConsidered  the string which specifies the rule on which this name relationship is based
398
	 * @return 
399
	 * @see    				  #getRelationsToThisName()
400
	 * @see    				  #getNameRelations()
401
	 * @see    				  #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
402
	 * @see    				  #addNameRelationship(NameRelationship)
403
	 */
404
	public NameRelationship addRelationshipToName(TaxonNameBase toName, NameRelationshipType type, ReferenceBase citation, String microCitation, String ruleConsidered){
405
		if (toName == null){
406
			throw new NullPointerException("Null is not allowed as name for a name relationship");
407
		}
408
		NameRelationship rel = new NameRelationship(toName, this, type, citation, microCitation, ruleConsidered);
409
		return rel;
410
	}
411
	
412
	/**
413
	 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
414
	 * and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
415
	 * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
416
	 * 
417
	 * @param fromName		  the taxon name of the source for this new name relationship
418
	 * @param type			  the type of this new name relationship
419
	 * @param ruleConsidered  the string which specifies the rule on which this name relationship is based
420
	 * @param citation		  the reference in which this relation was described
421
	 * @param microCitation	  the reference detail for this relation (e.g. page) 
422
	 * @see    				  #getRelationsFromThisName()
423
	 * @see    				  #getNameRelations()
424
	 * @see    				  #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
425
	 * @see    				  #addNameRelationship(NameRelationship)
426
	 */
427
	public void addRelationshipFromName(TaxonNameBase fromName, NameRelationshipType type, String ruleConsidered){
428
		fromName.addRelationshipToName(this, type, null, null, ruleConsidered);
429
//		NameRelationship rel = new NameRelationship(this, fromName, type, ruleConsidered);
430
	}
431
	/**
432
	 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
433
	 * and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
434
	 * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
435
	 * 
436
	 * @param fromName		  the taxon name of the source for this new name relationship
437
	 * @param type			  the type of this new name relationship
438
	 * @param ruleConsidered  the string which specifies the rule on which this name relationship is based
439
	 * @param citation		  the reference in which this relation was described
440
	 * @param microCitation	  the reference detail for this relation (e.g. page) 
441
	 * @see    				  #getRelationsFromThisName()
442
	 * @see    				  #getNameRelations()
443
	 * @see    				  #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
444
	 * @see    				  #addNameRelationship(NameRelationship)
445
	 */
446
	public void addRelationshipFromName(TaxonNameBase fromName, NameRelationshipType type, ReferenceBase citation, String microCitation, String ruleConsidered){
447
		fromName.addRelationshipToName(this, type, citation, microCitation, ruleConsidered);
448
	}
449

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

    
486
		if (nameRelation != null) {
487
			nameRelation.setToName(null);
488
			nameRelation.setFromName(null);
489
		}
490
		
491
		if (fromName != null) {
492
			fromName.removeNameRelationship(nameRelation);
493
		}
494
		
495
		if (toName != null) {
496
			toName.removeNameRelationship(nameRelation);
497
		}
498
		
499
		this.relationsToThisName.remove(nameRelation);
500
		this.relationsFromThisName.remove(nameRelation);
501
	}
502
		
503
	public void removeRelationToTaxonName(TaxonNameBase toTaxonName) {
504
		Set<NameRelationship> nameRelationships = new HashSet<NameRelationship>();
505
//		nameRelationships.addAll(this.getNameRelations());
506
		nameRelationships.addAll(this.getRelationsFromThisName());
507
		nameRelationships.addAll(this.getRelationsToThisName());
508
		for(NameRelationship nameRelationship : nameRelationships) {
509
			// remove name relationship from this side 
510
			if (nameRelationship.getFromName().equals(this) && nameRelationship.getToName().equals(toTaxonName)) {
511
				this.removeNameRelationship(nameRelationship);
512
			}
513
		}
514
	}
515

    
516
	
517
	/**
518
	 * Does exactly the same as the addNameRelationship method provided that
519
	 * the given relationship is a name relationship.
520
	 * 
521
	 * @param relation  the relationship to be added to one of <i>this</i> taxon name's name relationships sets
522
	 * @see    	   		#addNameRelationship(NameRelationship)
523
	 * @see    	   		#getNameRelations()
524
	 * @see    	   		NameRelationship
525
	 * @see    	   		eu.etaxonomy.cdm.model.common.RelationshipBase
526
	 */
527
	public void addRelationship(RelationshipBase relation) {
528
		if (relation instanceof NameRelationship){
529
			addNameRelationship((NameRelationship)relation);
530
			NameRelationshipType type = (NameRelationshipType)relation.getType();
531
			if (type != null && ( type.isBasionymRelation() || type.isReplacedSynonymRelation() ) ){
532
				TaxonNameBase fromName = ((NameRelationship)relation).getFromName();
533
				TaxonNameBase toName = ((NameRelationship)relation).getToName();
534
				fromName.mergeHomotypicGroups(toName);
535
			}		
536
		}else{
537
			logger.warn("Relationship not of type NameRelationship!");
538
			throw new IllegalArgumentException("Relationship not of type NameRelationship");
539
		}
540
	}
541

    
542
	
543
	/** 
544
	 * Returns the set of all {@link NameRelationship name relationships}
545
	 * in which <i>this</i> taxon name is involved as a source.
546
	 *  
547
	 * @see    #getNameRelations()
548
	 * @see    #getRelationsToThisName()
549
	 * @see    #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
550
	 */
551
	public Set<NameRelationship> getRelationsFromThisName() {
552
		if(relationsFromThisName == null) {
553
			this.relationsFromThisName = new HashSet<NameRelationship>();
554
		}
555
		return relationsFromThisName;
556
	}
557

    
558
	/** 
559
	 * Returns the set of all {@link NameRelationship name relationships}
560
	 * in which <i>this</i> taxon name is involved as a target.
561
	 *  
562
	 * @see    #getNameRelations()
563
	 * @see    #getRelationsFromThisName()
564
	 * @see    #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
565
	 */
566
	public Set<NameRelationship> getRelationsToThisName() {
567
		if(relationsToThisName == null) {
568
			this.relationsToThisName = new HashSet<NameRelationship>();
569
		}
570
		return relationsToThisName;
571
	}
572
	
573
	/** 
574
	 * Returns the set of {@link NomenclaturalStatus nomenclatural status} assigned
575
	 * to <i>this</i> taxon name according to its corresponding nomenclature code.
576
	 * This includes the {@link NomenclaturalStatusType type} of the nomenclatural status
577
	 * and the nomenclatural code rule considered.
578
	 *
579
	 * @see     NomenclaturalStatus
580
	 * @see     NomenclaturalStatusType
581
	 */
582
	public Set<NomenclaturalStatus> getStatus() {
583
		if(status == null) {
584
			this.status = new HashSet<NomenclaturalStatus>();
585
		}
586
		return status;
587
	}
588

    
589
	/** 
590
	 * Adds a new {@link NomenclaturalStatus nomenclatural status}
591
	 * to <i>this</i> taxon name's set of nomenclatural status.
592
	 *
593
	 * @param  nomStatus  the nomenclatural status to be added
594
	 * @see 			  #getStatus()
595
	 */
596
	public void addStatus(NomenclaturalStatus nomStatus) {
597
		this.status.add(nomStatus);
598
	}
599
	
600
	/** 
601
	 * Removes one element from the set of nomenclatural status of <i>this</i> taxon name.
602
	 * Type and ruleConsidered attributes of the nomenclatural status object
603
	 * will be nullified.
604
	 *
605
	 * @param  nomStatus  the nomenclatural status of <i>this</i> taxon name which should be deleted
606
	 * @see     		  #getStatus()
607
	 */
608
	public void removeStatus(NomenclaturalStatus nomStatus) {
609
		//TODO to be implemented?
610
		logger.warn("not yet fully implemented?");
611
		this.status.remove(nomStatus);
612
	}
613

    
614
	
615
	/**
616
	 * Indicates whether <i>this</i> taxon name is a {@link NameRelationshipType#BASIONYM() basionym}
617
	 * or a {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym}
618
	 * of any other taxon name. Returns "true", if a basionym or a replaced 
619
	 * synonym {@link NameRelationship relationship} from <i>this</i> taxon name to another taxon name exists,
620
	 * false otherwise (also in case <i>this</i> taxon name is the only one in the
621
	 * homotypical group).
622
	 */
623
	@Transient
624
	public boolean isOriginalCombination(){
625
		Set<NameRelationship> relationsFromThisName = this.getRelationsFromThisName();
626
		for (NameRelationship relation : relationsFromThisName) {
627
			if (relation.getType().isBasionymRelation() ||
628
					relation.getType().isReplacedSynonymRelation()) {
629
				return true;
630
			}
631
		}
632
		return false;
633
	}
634
	
635
	/**
636
	 * Returns the taxon name which is the {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
637
	 * The basionym of a taxon name is its epithet-bringing synonym.
638
	 * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
639
	 * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
640
	 * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
641
	 * 
642
	 * If more than one basionym exists one is choosen at radom.
643
	 * 
644
	 * If no basionym exists null is returned.
645
	 */
646
	@Transient
647
	public TaxonNameBase getBasionym(){
648
		Set<TaxonNameBase> basionyms = getBasionyms();
649
		if (basionyms.size() == 0){
650
			return null;
651
		}else{
652
			return basionyms.iterator().next();
653
		}
654
	}
655
	
656
	/**
657
	 * Returns the set of taxon names which are the {@link NameRelationshipType#BASIONYM() basionyms} of <i>this</i> taxon name.
658
	 * The basionym of a taxon name is its epithet-bringing synonym.
659
	 * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
660
	 * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
661
	 * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
662
	 */
663
	@Transient
664
	public Set<TaxonNameBase> getBasionyms(){
665
		Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
666
		Set<NameRelationship> rels = this.getRelationsToThisName();
667
		for (NameRelationship rel : rels){
668
			if (rel.getType().isBasionymRelation()){
669
				TaxonNameBase basionym = rel.getFromName();
670
				result.add(basionym);
671
			}
672
		}
673
		return result;
674
	}
675
	
676
	/**
677
	 * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
678
	 * The basionym {@link NameRelationship relationship} will be added to <i>this</i> taxon name
679
	 * and to the basionym. The basionym cannot have itself a basionym.
680
	 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
681
	 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
682
	 * 
683
	 * @param  basionym		the taxon name to be set as the basionym of <i>this</i> taxon name
684
	 * @see  				#getBasionym()
685
	 * @see  				#addBasionym(TaxonNameBase, String)
686
	 */
687
	public void addBasionym(T basionym){
688
		addBasionym(basionym, null, null, null);
689
	}
690
	/**
691
	 * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name
692
	 * and keeps the nomenclatural rule considered for it. The basionym
693
	 * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the basionym.
694
	 * The basionym cannot have itself a basionym.
695
	 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
696
	 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
697
	 * 
698
	 * @param  basionym			the taxon name to be set as the basionym of <i>this</i> taxon name
699
	 * @param  ruleConsidered	the string identifying the nomenclatural rule
700
	 * @return 
701
	 * @see  					#getBasionym()
702
	 * @see  					#addBasionym(TaxonNameBase)
703
	 */
704
	public NameRelationship addBasionym(T basionym, ReferenceBase citation, String microcitation, String ruleConsidered){
705
		if (basionym != null){
706
			return basionym.addRelationshipToName(this, NameRelationshipType.BASIONYM(), citation, microcitation, ruleConsidered);
707
		}else{
708
			return null;
709
		}
710
	}
711
	
712
	/**
713
	 * Assigns a taxon name as {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym} of <i>this</i> taxon name
714
	 * and keeps the nomenclatural rule considered for it. The replaced synonym
715
	 * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the replaced synonym.
716
	 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the replaced synonym
717
	 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
718
	 * 
719
	 * @param  basionym			the taxon name to be set as the basionym of <i>this</i> taxon name
720
	 * @param  ruleConsidered	the string identifying the nomenclatural rule
721
	 * @see  					#getBasionym()
722
	 * @see  					#addBasionym(TaxonNameBase)
723
	 */
724
	//TODO: Check if true: The replaced synonym cannot have itself a replaced synonym (?).
725
	public void addReplacedSynonym(T replacedSynonym, ReferenceBase citation, String microcitation, String ruleConsidered){
726
		if (replacedSynonym != null){
727
			replacedSynonym.addRelationshipToName(this, NameRelationshipType.REPLACED_SYNONYM(), citation, microcitation, ruleConsidered);
728
		}
729
	}
730
	
731
	/** 
732
	 * Removes the {@link NameRelationshipType#BASIONYM() basionym} {@link NameRelationship relationship} from the set of
733
	 * {@link #getRelationsToThisName() name relationships to} <i>this</i> taxon name. The same relationhip will be
734
	 * removed from the set of {@link #getRelationsFromThisName() name relationships from} the taxon name
735
	 * previously used as basionym.
736
	 *
737
	 * @see   #getBasionym()
738
	 * @see   #addBasionym(TaxonNameBase)
739
	 */
740
	public void removeBasionyms(){
741
		Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
742
		for (NameRelationship nameRelation : this.getRelationsToThisName()){
743
			if (nameRelation.getType().isBasionymRelation()){
744
				removeRelations.add(nameRelation);
745
			}
746
		}
747
		// Removing relations from a set through which we are iterating causes a
748
		// ConcurrentModificationException. Therefore, we delete the targeted
749
		// relations in a second step.
750
		for (NameRelationship relation : removeRelations){
751
			this.removeNameRelationship(relation);
752
		}
753
	}
754
	
755
	/** 
756
	 * Returns the taxonomic {@link Rank rank} of <i>this</i> taxon name.
757
	 *
758
	 * @see 	Rank
759
	 */
760
	public Rank getRank(){
761
		return this.rank;
762
	}
763
	
764
	/**
765
	 * @see  #getRank()
766
	 */
767
	public void setRank(Rank rank){
768
		this.rank = rank;
769
	}
770

    
771
	/** 
772
	 * Returns the {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} of <i>this</i> taxon name.
773
	 * The nomenclatural reference is here meant to be the one publication
774
	 * <i>this</i> taxon name was originally published in while fulfilling the formal
775
	 * requirements as specified by the corresponding {@link NomenclaturalCode nomenclatural code}.
776
	 *
777
	 * @see 	eu.etaxonomy.cdm.model.reference.INomenclaturalReference
778
	 * @see 	eu.etaxonomy.cdm.model.reference.ReferenceBase
779
	 */
780
	public INomenclaturalReference getNomenclaturalReference(){
781
		return this.nomenclaturalReference;
782
	}
783
	/**
784
	 * Assigns a {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} to <i>this</i> taxon name.
785
	 * The corresponding {@link eu.etaxonomy.cdm.model.reference.ReferenceBase.isNomenclaturallyRelevant nomenclaturally relevant flag} will be set to true
786
	 * as it is obviously used for nomenclatural purposes.
787
	 *
788
	 * @throws IllegalArgumentException if parameter <code>nomenclaturalReference</code> is not assignable from {@link INomenclaturalReference}
789
	 * @see  #getNomenclaturalReference()
790
	 */
791
	public void setNomenclaturalReference(INomenclaturalReference nomenclaturalReference){
792
		if(nomenclaturalReference != null){
793
			if(!INomenclaturalReference.class.isAssignableFrom(nomenclaturalReference.getClass())){
794
				throw new IllegalArgumentException("Parameter nomenclaturalReference is not assignable from INomenclaturalReference");
795
			}
796
			this.nomenclaturalReference = (ReferenceBase)nomenclaturalReference;
797
		} else {
798
			this.nomenclaturalReference = null;
799
		}
800
	}
801

    
802
	/** 
803
	 * Returns the appended phrase string assigned to <i>this</i> taxon name.
804
	 * The appended phrase is a non-atomised addition to a name. It is
805
	 * not ruled by a nomenclatural code.
806
	 */
807
	public String getAppendedPhrase(){
808
		return this.appendedPhrase;
809
	}
810
	
811
	/**
812
	 * @see  #getAppendedPhrase()
813
	 */
814
	public void setAppendedPhrase(String appendedPhrase){
815
		this.appendedPhrase = appendedPhrase;
816
	}
817

    
818
	/** 
819
	 * Returns the details string of the {@link #getNomenclaturalReference() nomenclatural reference} assigned
820
	 * to <i>this</i> taxon name. The details describe the exact localisation within
821
	 * the publication used as nomenclature reference. These are mostly
822
	 * (implicitly) pages but can also be figures or tables or any other
823
	 * element of a publication. A nomenclatural micro reference (details)
824
	 * requires the existence of a nomenclatural reference.
825
	 */
826
	//Details of the nomenclatural reference (protologue). 
827
	public String getNomenclaturalMicroReference(){
828
		return this.nomenclaturalMicroReference;
829
	}
830
	/**
831
	 * @see  #getNomenclaturalMicroReference()
832
	 */
833
	public void setNomenclaturalMicroReference(String nomenclaturalMicroReference){
834
		this.nomenclaturalMicroReference = nomenclaturalMicroReference;
835
	}
836

    
837
	/* (non-Javadoc)
838
	 * @see eu.etaxonomy.cdm.model.common.IParsable#getHasProblem()
839
	 */
840
	public int getParsingProblem(){
841
		return this.parsingProblem;
842
	}
843
	
844
	/* (non-Javadoc)
845
	 * @see eu.etaxonomy.cdm.model.common.IParsable#setHasProblem(int)
846
	 */
847
	public void setParsingProblem(int parsingProblem){
848
		this.parsingProblem = parsingProblem;
849
	}
850
	
851
	/* (non-Javadoc)
852
	 * @see eu.etaxonomy.cdm.model.common.IParsable#addProblem(eu.etaxonomy.cdm.strategy.parser.NameParserWarning)
853
	 */
854
	public void addParsingProblem(ParserProblem problem){
855
		parsingProblem = ParserProblem.addProblem(parsingProblem, problem);
856
	}
857
	
858
	/* (non-Javadoc)
859
	 * @see eu.etaxonomy.cdm.model.common.IParsable#removeParsingProblem(eu.etaxonomy.cdm.strategy.parser.ParserProblem)
860
	 */
861
	public void removeParsingProblem(ParserProblem problem) {
862
		parsingProblem = ParserProblem.removeProblem(parsingProblem, problem);
863
	}
864
	
865
	/**
866
	 * @param warnings
867
	 */
868
	public void addParsingProblems(int problems){
869
		parsingProblem = ParserProblem.addProblems(parsingProblem, problems);
870
	}
871
	
872
	/* (non-Javadoc)
873
	 * @see eu.etaxonomy.cdm.model.common.IParsable#hasProblem()
874
	 */
875
	public boolean hasProblem(){
876
		return parsingProblem != 0;
877
	}
878
	
879
	
880
	
881
	/* (non-Javadoc)
882
	 * @see eu.etaxonomy.cdm.model.common.IParsable#hasProblem(eu.etaxonomy.cdm.strategy.parser.ParserProblem)
883
	 */
884
	public boolean hasProblem(ParserProblem problem) {
885
		return getParsingProblems().contains(problem);
886
	}
887
	
888
	
889
	/* (non-Javadoc)
890
	 * @see eu.etaxonomy.cdm.model.common.IParsable#problemStarts()
891
	 */
892
	public int getProblemStarts(){
893
		return this.problemStarts;
894
	}
895
	
896
	/* (non-Javadoc)
897
	 * @see eu.etaxonomy.cdm.model.common.IParsable#setProblemStarts(int)
898
	 */
899
	public void setProblemStarts(int start) {
900
		this.problemStarts = start;
901
	}
902
	
903
	/* (non-Javadoc)
904
	 * @see eu.etaxonomy.cdm.model.common.IParsable#problemEnds()
905
	 */
906
	public int getProblemEnds(){
907
		return this.problemEnds;
908
	}
909

    
910
	/* (non-Javadoc)
911
	 * @see eu.etaxonomy.cdm.model.common.IParsable#setProblemEnds(int)
912
	 */
913
	public void setProblemEnds(int end) {
914
		this.problemEnds = end;
915
	}
916

    
917
//*********************** TYPE DESIGNATION *********************************************//	
918
	
919
	/** 
920
	 * Returns the set of {@link TypeDesignationBase type designations} assigned
921
	 * to <i>this</i> taxon name.
922
	 * @see     NameTypeDesignation
923
	 * @see     SpecimenTypeDesignation
924
	 */
925
	public Set<TypeDesignationBase> getTypeDesignations() {
926
		if(typeDesignations == null) {
927
			this.typeDesignations = new HashSet<TypeDesignationBase>();
928
		}
929
		return typeDesignations;
930
	}
931
	
932
	/** 
933
	 * Removes one element from the set of {@link TypeDesignationBase type designations} assigned to
934
	 * <i>this</i> taxon name. The type designation itself will be nullified.
935
	 *
936
	 * @param  typeDesignation  the type designation which should be deleted
937
	 */
938
	public void removeTypeDesignation(TypeDesignationBase typeDesignation) {
939
		logger.warn("not yet fully implemented: nullify the specimen type designation itself?");
940
		this.typeDesignations.remove(typeDesignation);
941
	}
942
	
943
	/** 
944
	 * Returns the set of {@link SpecimenTypeDesignation specimen type designations} assigned
945
	 * to <i>this</i> taxon name. The {@link Rank rank} of <i>this</i> taxon name is generally
946
	 * "species" or below. The specimen type designations include all the
947
	 * specimens on which the typification of this name is based (which are
948
	 * exclusively used to typify taxon names belonging to the same
949
	 * {@link HomotypicalGroup homotypical group} to which <i>this</i> taxon name
950
	 * belongs) and eventually the status of these designations.
951
	 *
952
	 * @see     SpecimenTypeDesignation
953
	 * @see     NameTypeDesignation
954
	 * @see     HomotypicalGroup
955
	 */
956
	@Transient
957
	public Set<SpecimenTypeDesignation> getSpecimenTypeDesignationsOfHomotypicalGroup() {
958
		return this.getHomotypicalGroup().getSpecimenTypeDesignations();
959
	}
960
	
961
//*********************** NAME TYPE DESIGNATION *********************************************//	
962
	
963
	/** 
964
	 * Returns the set of {@link NameTypeDesignation name type designations} assigned
965
	 * to <i>this</i> taxon name the rank of which must be above "species".
966
	 * The name type designations include all the taxon names used to typify
967
	 * <i>this</i> taxon name and eventually the rejected or conserved status
968
	 * of these designations.
969
	 *
970
	 * @see     NameTypeDesignation
971
	 * @see     SpecimenTypeDesignation
972
	 */
973
	@Transient
974
	public Set<NameTypeDesignation> getNameTypeDesignations() {
975
		Set<NameTypeDesignation> result = new HashSet<NameTypeDesignation>();
976
		for (TypeDesignationBase typeDesignation : this.typeDesignations){
977
			if (typeDesignation instanceof NameTypeDesignation){
978
				result.add((NameTypeDesignation)typeDesignation);
979
			}
980
		}
981
		return result;
982
	}
983
	
984
	/** 
985
	 * Creates and adds a new {@link NameTypeDesignation name type designation}
986
	 * to <i>this</i> taxon name's set of type designations.
987
	 *
988
	 * @param  typeSpecies				the taxon name to be used as type of <i>this</i> taxon name
989
	 * @param  citation					the reference for this new designation
990
	 * @param  citationMicroReference	the string with the details (generally pages) within the reference
991
	 * @param  originalNameString		the taxon name string used in the reference to assert this designation
992
	 * @param  isRejectedType			the boolean status for a rejected name type designation
993
	 * @param  isConservedType			the boolean status for a conserved name type designation
994
	 * @param  isLectoType				the boolean status for a lectotype name type designation
995
	 * @param  isNotDesignated			the boolean status for a name type designation without name type
996
	 * @param  addToAllHomotypicNames	the boolean indicating whether the name type designation should be
997
	 * 									added to all taxon names of the homotypical group this taxon name belongs to
998
	 * @return 
999
	 * @see 			  				#getNameTypeDesignations()
1000
	 * @see 			  				NameTypeDesignation
1001
	 * @see 			  				TypeDesignationBase#isNotDesignated()
1002
	 */
1003
	public NameTypeDesignation addNameTypeDesignation(TaxonNameBase typeSpecies, 
1004
				ReferenceBase citation, 
1005
				String citationMicroReference, 
1006
				String originalNameString, 
1007
				NameTypeDesignationStatus status,
1008
				boolean isRejectedType, 
1009
				boolean isConservedType, 
1010
				/*boolean isLectoType, */
1011
				boolean isNotDesignated, 
1012
				boolean addToAllHomotypicNames) {
1013
		NameTypeDesignation nameTypeDesignation = new NameTypeDesignation(typeSpecies, citation, citationMicroReference, originalNameString, status, isRejectedType, isConservedType, isNotDesignated);
1014
		//nameTypeDesignation.setLectoType(isLectoType);
1015
		addTypeDesignation(nameTypeDesignation, addToAllHomotypicNames);
1016
		return nameTypeDesignation;
1017
	}
1018
	
1019
	/** 
1020
	 * Creates and adds a new {@link NameTypeDesignation name type designation}
1021
	 * to <i>this</i> taxon name's set of type designations.
1022
	 *
1023
	 * @param  typeSpecies				the taxon name to be used as type of <i>this</i> taxon name
1024
	 * @param  citation					the reference for this new designation
1025
	 * @param  citationMicroReference	the string with the details (generally pages) within the reference
1026
	 * @param  originalNameString		the taxon name string used in the reference to assert this designation
1027
	 * @param  status                   the name type designation status
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
				ReferenceBase citation, 
1037
				String citationMicroReference, 
1038
				String originalNameString,
1039
				NameTypeDesignationStatus status,
1040
				boolean addToAllHomotypicNames) {
1041
		NameTypeDesignation nameTypeDesignation = new NameTypeDesignation(typeSpecies, status, citation, citationMicroReference, originalNameString);
1042
		addTypeDesignation(nameTypeDesignation, addToAllHomotypicNames);
1043
		return nameTypeDesignation;
1044
	}
1045
	
1046
//*********************** SPECIMEN TYPE DESIGNATION *********************************************//	
1047
	
1048
	/** 
1049
	 * Returns the set of {@link SpecimenTypeDesignation specimen type designations}
1050
	 * that typify <i>this</i> taxon name.
1051
	 */
1052
	@Transient
1053
	public Set<SpecimenTypeDesignation> getSpecimenTypeDesignations() {
1054
		Set<SpecimenTypeDesignation> result = new HashSet<SpecimenTypeDesignation>();
1055
		for (TypeDesignationBase typeDesignation : this.typeDesignations){
1056
			if (typeDesignation instanceof SpecimenTypeDesignation){
1057
				result.add((SpecimenTypeDesignation)typeDesignation);
1058
			}
1059
		}
1060
		return result;
1061
	}
1062

    
1063
	
1064
	/** 
1065
	 * Creates and adds a new {@link SpecimenTypeDesignation specimen type designation}
1066
	 * to <i>this</i> taxon name's set of type designations.
1067
	 *
1068
	 * @param  typeSpecimen				the specimen to be used as a type for <i>this</i> taxon name
1069
	 * @param  status					the specimen type designation status
1070
	 * @param  citation					the reference for this new specimen type designation
1071
	 * @param  citationMicroReference	the string with the details (generally pages) within the reference
1072
	 * @param  originalNameString		the taxon name used in the reference to assert this designation
1073
	 * @param  isNotDesignated			the boolean status for a specimen type designation without specimen type
1074
	 * @param  addToAllHomotypicNames	the boolean indicating whether the specimen type designation should be
1075
	 * 									added to all taxon names of the homotypical group the typified
1076
	 * 									taxon name belongs to
1077
	 * @return 
1078
	 * @see 			  				#getSpecimenTypeDesignations()
1079
	 * @see 			  				SpecimenTypeDesignationStatus
1080
	 * @see 			  				SpecimenTypeDesignation
1081
	 * @see 			  				TypeDesignationBase#isNotDesignated()
1082
	 */
1083
	public SpecimenTypeDesignation addSpecimenTypeDesignation(Specimen typeSpecimen, 
1084
				SpecimenTypeDesignationStatus status, 
1085
				ReferenceBase citation, 
1086
				String citationMicroReference, 
1087
				String originalNameString, 
1088
				boolean isNotDesignated, 
1089
				boolean addToAllHomotypicNames) {
1090
		SpecimenTypeDesignation specimenTypeDesignation = new SpecimenTypeDesignation(typeSpecimen, status, citation, citationMicroReference, originalNameString, isNotDesignated);
1091
		addTypeDesignation(specimenTypeDesignation, addToAllHomotypicNames);
1092
		return specimenTypeDesignation;
1093
	}
1094
	
1095
	//used by merge strategy
1096
	private boolean addTypeDesignation(TypeDesignationBase typeDesignation){
1097
		return addTypeDesignation(typeDesignation, true);
1098
	}
1099
	
1100
	/**
1101
	 * Adds a {@link TypeDesignationBase type designation} to <code>this</code> taxon name's set of type designations 
1102
	 * 
1103
	 * @param typeDesignation			the typeDesignation to be added to <code>this</code> taxon name
1104
	 * @param addToAllNames				the boolean indicating whether the type designation should be
1105
	 * 									added to all taxon names of the homotypical group the typified
1106
	 * 									taxon name belongs to
1107
	 * @return							true if the operation was succesful
1108
	 * 
1109
	 * @throws IllegalArgumentException	if the type designation already has typified names, an {@link IllegalArgumentException exception}
1110
	 * 									is thrown. We do this to prevent a type designation to be used for multiple taxon names.
1111
	 * 
1112
	 */
1113
	public boolean addTypeDesignation(TypeDesignationBase typeDesignation, boolean addToAllNames){
1114
		//at them moment typeDesignations are not persisted with the homotypical group
1115
		//so explicit adding to the homotypical group is not necessary.
1116
		if (typeDesignation != null){		
1117
			checkHomotypicalGroup(typeDesignation);
1118
			this.typeDesignations.add(typeDesignation);
1119
			typeDesignation.addTypifiedName(this);
1120
			
1121
			if (addToAllNames){
1122
				for (TaxonNameBase taxonName : this.getHomotypicalGroup().getTypifiedNames()){
1123
					if (taxonName != this){
1124
						taxonName.addTypeDesignation(typeDesignation, false);
1125
					}
1126
				}
1127
			}
1128
		}
1129
		return true;
1130
	}
1131
	
1132
	/**
1133
	 * Throws an Exception this type designation already has typified names from another homotypical group.
1134
	 * @param typeDesignation
1135
	 */
1136
	private void checkHomotypicalGroup(TypeDesignationBase typeDesignation) {
1137
		if(typeDesignation.getTypifiedNames().size() > 0){
1138
			Set<HomotypicalGroup> groups = new HashSet<HomotypicalGroup>();
1139
			Set<TaxonNameBase> names = typeDesignation.getTypifiedNames();
1140
			for (TaxonNameBase taxonName: names){
1141
				groups.add(taxonName.getHomotypicalGroup());
1142
			}
1143
			if (groups.size() > 1){
1144
				throw new IllegalArgumentException("TypeDesignation already has typified names from another homotypical group.");
1145
			}
1146
		}
1147
	}
1148
	
1149

    
1150
	
1151
//*********************** HOMOTYPICAL GROUP *********************************************//	
1152

    
1153
	
1154
	/** 
1155
	 * Returns the {@link HomotypicalGroup homotypical group} to which
1156
	 * <i>this</i> taxon name belongs. A homotypical group represents all taxon names
1157
	 * that share the same types.
1158
	 *
1159
	 * @see 	HomotypicalGroup
1160
	 */
1161
	
1162
	public HomotypicalGroup getHomotypicalGroup() {
1163
		return homotypicalGroup;
1164
	}
1165
	
1166
	/* 
1167
	 * @see #getHomotypicalGroup()
1168
	 */
1169
	public void setHomotypicalGroup(HomotypicalGroup homotypicalGroup) {
1170
		if (homotypicalGroup == null){
1171
			throw new IllegalArgumentException("HomotypicalGroup of name should never be null but was set to 'null'");
1172
		}
1173
		this.homotypicalGroup = homotypicalGroup;
1174
	}
1175
	
1176
	
1177

    
1178
// *************************************************************************//
1179
	
1180
	/** 
1181
	 * @see #getNomenclaturalReference()
1182
	 */
1183
	@Transient
1184
	public ReferenceBase getCitation(){
1185
		//TODO What is the purpose of this method differing from the getNomenclaturalReference method? 
1186
		logger.warn("getCitation not yet implemented");
1187
		return null;
1188
	}
1189

    
1190
	/** 
1191
	 * Returns the complete string containing the
1192
	 * {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation() nomenclatural reference citation}
1193
	 * and the {@link #getNomenclaturalMicroReference() details} assigned to <i>this</i> taxon name.
1194
	 * 
1195
	 * @return  the string containing the nomenclatural reference of <i>this</i> taxon name
1196
	 * @see		eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation()
1197
	 * @see		#getNomenclaturalReference()
1198
	 * @see		#getNomenclaturalMicroReference()
1199
	 */
1200
	@Transient
1201
	@Deprecated
1202
	public String getCitationString(){
1203
		logger.warn("getCitationString not yet implemented");
1204
		return null;
1205
	}
1206

    
1207
	/**
1208
	 * Returns the parsing problems 
1209
	 * @return
1210
	 */
1211
	public List<ParserProblem> getParsingProblems(){
1212
		return ParserProblem.warningList(this.parsingProblem);
1213
	}
1214

    
1215
	/**
1216
	 * Returns the string containing the publication date (generally only year)
1217
	 * of the {@link #getNomenclaturalReference() nomenclatural reference} for <i>this</i> taxon name, null if there is
1218
	 * no nomenclatural reference.
1219
	 * 
1220
	 * @return  the string containing the publication date of <i>this</i> taxon name
1221
	 * @see		eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getYear()
1222
	 */
1223
	@Transient
1224
	public String getReferenceYear(){
1225
		if (this.getNomenclaturalReference() != null ){
1226
			return this.getNomenclaturalReference().getYear();
1227
		}else{
1228
			return null;
1229
		}
1230
	}
1231

    
1232
	/** 
1233
	 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1234
	 * In this context a taxon base means the use of a taxon name by a reference
1235
	 * either as a {@link eu.etaxonomy.cdm.model.taxon.Taxon taxon} ("accepted/correct" name) or
1236
	 * as a (junior) {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
1237
	 * A taxon name can be used by several distinct {@link eu.etaxonomy.cdm.model.reference.ReferenceBase references} but only once
1238
	 * within a taxonomic treatment (identified by one reference).
1239
	 *
1240
	 * @see	#getTaxa()
1241
	 * @see	#getSynonyms()
1242
	 */
1243
	public Set<TaxonBase> getTaxonBases() {
1244
		if(taxonBases == null) {
1245
			this.taxonBases = new HashSet<TaxonBase>();
1246
		}
1247
		return this.taxonBases;
1248
	}
1249
	
1250
	/** 
1251
	 * Adds a new {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon base}
1252
	 * to the set of taxon bases using <i>this</i> taxon name.
1253
	 *
1254
	 * @param  taxonBase  the taxon base to be added
1255
	 * @see 			  #getTaxonBases()
1256
	 * @see 			  #removeTaxonBase(TaxonBase)
1257
	 */
1258
	//TODO protected
1259
	public void addTaxonBase(TaxonBase taxonBase){
1260
		Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonNameBase.class});
1261
		ReflectionUtils.makeAccessible(method);
1262
		ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {this});
1263
		taxonBases.add(taxonBase);
1264
	}
1265
	/** 
1266
	 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1267
	 *
1268
	 * @param  taxonBase	the taxon base which should be removed from the corresponding set
1269
	 * @see    				#getTaxonBases()
1270
	 * @see    				#addTaxonBase(TaxonBase)
1271
	 */
1272
	public void removeTaxonBase(TaxonBase taxonBase){
1273
		Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonNameBase.class});
1274
		ReflectionUtils.makeAccessible(method);
1275
		ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {null});
1276
		taxonBases.remove(taxonBase);
1277
	}
1278
	
1279
	/**
1280
	 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Taxon taxa} ("accepted/correct" names according to any
1281
	 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1282
	 * the set returned by getTaxonBases(). 
1283
	 * 
1284
	 * @see	eu.etaxonomy.cdm.model.taxon.Taxon
1285
	 * @see	#getTaxonBases()
1286
	 * @see	#getSynonyms()
1287
	 */
1288
	@Transient
1289
	public Set<Taxon> getTaxa(){
1290
		Set<Taxon> result = new HashSet<Taxon>();
1291
		for (TaxonBase taxonBase : this.taxonBases){
1292
			if (taxonBase instanceof Taxon){
1293
				result.add((Taxon)taxonBase);
1294
			}
1295
		}
1296
		return result;
1297
	}
1298
	
1299
	/**
1300
	 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Synonym (junior) synonyms} (according to any
1301
	 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1302
	 * the set returned by getTaxonBases(). 
1303
	 * 
1304
	 * @see	eu.etaxonomy.cdm.model.taxon.Synonym
1305
	 * @see	#getTaxonBases()
1306
	 * @see	#getTaxa()
1307
	 */
1308
	@Transient
1309
	public Set<Synonym> getSynonyms() {
1310
		Set<Synonym> result = new HashSet<Synonym>();
1311
		for (TaxonBase taxonBase : this.taxonBases){
1312
			if (taxonBase instanceof Synonym){
1313
				result.add((Synonym)taxonBase);
1314
			}
1315
		}
1316
		return result;
1317
	}
1318
	
1319
	
1320
// *********** DESCRIPTIONS *************************************	
1321

    
1322
	/**
1323
	 * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1324
	 * to <i>this</i> taxon name. A taxon name description is a piece of information
1325
	 * concerning the taxon name like for instance the content of its first
1326
	 * publication (protolog) or a picture of this publication.
1327
	 * 
1328
	 * @see	#addDescription(TaxonNameDescription)
1329
	 * @see	#removeDescription(TaxonNameDescription)
1330
	 * @see	eu.etaxonomy.cdm.model.description.TaxonNameDescription
1331
	 */
1332
	public Set<TaxonNameDescription> getDescriptions() {
1333
		return descriptions;
1334
	}
1335

    
1336
	/** 
1337
	 * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name description}
1338
	 * to the set of taxon name descriptions assigned to <i>this</i> taxon name. The
1339
	 * content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute} of the
1340
	 * taxon name description itself will be replaced with <i>this</i> taxon name.
1341
	 *
1342
	 * @param  description  the taxon name description to be added
1343
	 * @see					#getDescriptions()
1344
	 * @see 			  	#removeDescription(TaxonNameDescription)
1345
	 */
1346
	public void addDescription(TaxonNameDescription description) {
1347
		java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonNameBase.class);
1348
		ReflectionUtils.makeAccessible(field);
1349
		ReflectionUtils.setField(field, description, this);
1350
		descriptions.add(description);
1351
	}
1352
	/** 
1353
	 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1354
	 * to <i>this</i> taxon name. The content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute}
1355
	 * of the description itself will be set to "null".
1356
	 *
1357
	 * @param  description  the taxon name description which should be removed
1358
	 * @see     		  	#getDescriptions()
1359
	 * @see     		  	#addDescription(TaxonNameDescription)
1360
	 * @see 			  	eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName()
1361
	 */
1362
	public void removeDescription(TaxonNameDescription description) {
1363
		java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonNameBase.class);
1364
		ReflectionUtils.makeAccessible(field);
1365
		ReflectionUtils.setField(field, description, null);
1366
		descriptions.remove(description);
1367
	}
1368
	
1369
// *********** HOMOTYPIC GROUP METHODS **************************************************
1370

    
1371
	@Transient
1372
	public void mergeHomotypicGroups(TaxonNameBase name){
1373
		this.getHomotypicalGroup().merge(name.getHomotypicalGroup());
1374
		//HomotypicalGroup thatGroup = name.homotypicalGroup;
1375
		name.setHomotypicalGroup(this.homotypicalGroup);
1376
	}
1377
	
1378
	/**
1379
	 * Returns the boolean value indicating whether a given taxon name belongs
1380
	 * to the same {@link HomotypicalGroup homotypical group} as <i>this</i> taxon name (true)
1381
	 * or not (false). Returns "true" only if the homotypical groups of both
1382
	 * taxon names exist and if they are identical. 
1383
	 *
1384
	 * @param	homoTypicName  the taxon name the homotypical group of which is to be checked
1385
	 * @return  			   the boolean value of the check
1386
	 * @see     			   HomotypicalGroup
1387
	 */
1388
	@Transient
1389
	public boolean isHomotypic(TaxonNameBase homoTypicName) {
1390
		if (homoTypicName == null) {
1391
			return false;
1392
		}
1393
		HomotypicalGroup homotypicGroup = homoTypicName.getHomotypicalGroup();
1394
		if (homotypicGroup == null || this.getHomotypicalGroup() == null) {
1395
			return false;
1396
		}
1397
		if (homotypicGroup.equals(this.getHomotypicalGroup())) {
1398
			return true;
1399
		}
1400
		return false;
1401
	}
1402
	
1403
	
1404
    /**
1405
     * Checks whether name is a basionym for ALL names
1406
     * in its homotypical group.
1407
     * Returns <code>false</code> if there are no other names in the group
1408
     * @param name
1409
     * @return
1410
     */  
1411
	@Transient
1412
	public boolean isGroupsBasionym() {
1413
		Set<TaxonNameBase> typifiedNames = homotypicalGroup.getTypifiedNames();
1414
		
1415
		// Check whether there are any other names in the group
1416
	    if (typifiedNames.size() == 1) {
1417
	            return false;
1418
	    }
1419
	   
1420
	    boolean isBasionymToAll = true;
1421
	                   
1422
	    for (TaxonNameBase taxonName : typifiedNames) {
1423
	            if (!taxonName.equals(this)) {
1424
	                    if (! isBasionymFor(taxonName)) {
1425
	                            return false;
1426
	                    }
1427
	            }
1428
	    }
1429
	    return true;            
1430
	}
1431
	
1432
    /**
1433
     * Checks whether a basionym relationship exists between fromName and toName.
1434
     *
1435
     * @param fromName
1436
     * @param toName
1437
     * @return
1438
     */
1439
	@Transient
1440
	public boolean isBasionymFor(TaxonNameBase newCombinationName) {
1441
            Set<NameRelationship> relations = newCombinationName.getRelationsToThisName();
1442
            for (NameRelationship relation : relations) {
1443
                    if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1444
                                    relation.getFromName().equals(this)) {
1445
                            return true;
1446
                    }
1447
            }
1448
            return false;
1449
    }
1450
    
1451
    /**
1452
     * Creates a basionym relationship to all other names in this names homotypical
1453
     * group.
1454
     * 
1455
     * @see HomotypicalGroup.setGroupBasionym(TaxonNameBase basionymName)
1456

    
1457
     */
1458
	/* (non-Javadoc)
1459
	 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup#setGroupBasionym(TaxonNameBase)
1460
	 */
1461
	@Transient
1462
	public void makeGroupsBasionym() {
1463
        this.homotypicalGroup.setGroupBasionym(this);
1464
    }
1465
	
1466
	
1467
//*********  Rank comparison shortcuts   ********************//
1468
	/**
1469
	 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1470
	 * taxon name is higher than the genus rank (true) or not (false).
1471
	 * Suprageneric non viral names are monomials.
1472
	 * Returns false if rank is null.
1473
	 * 
1474
	 * @see  #isGenus()
1475
	 * @see  #isInfraGeneric()
1476
	 * @see  #isSpecies()
1477
	 * @see  #isInfraSpecific()
1478
	 */
1479
	@Transient
1480
	public boolean isSupraGeneric() {
1481
		if (rank == null){
1482
			return false;
1483
		}
1484
		return getRank().isSupraGeneric();
1485
	}
1486
	/**
1487
	 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1488
	 * taxon name is the genus rank (true) or not (false). Non viral names with
1489
	 * genus rank are monomials. Returns false if rank is null.
1490
	 *
1491
	 * @see  #isSupraGeneric()
1492
	 * @see  #isInfraGeneric()
1493
	 * @see  #isSpecies()
1494
	 * @see  #isInfraSpecific()
1495
	 */
1496
	@Transient
1497
	public boolean isGenus() {
1498
		if (rank == null){
1499
			return false;
1500
		}
1501
		return getRank().isGenus();
1502
	}
1503
	/**
1504
	 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1505
	 * taxon name is higher than the species rank and lower than the
1506
	 * genus rank (true) or not (false). Infrageneric non viral names are
1507
	 * binomials. Returns false if rank is null.
1508
	 *
1509
	 * @see  #isSupraGeneric()
1510
	 * @see  #isGenus()
1511
	 * @see  #isSpecies()
1512
	 * @see  #isInfraSpecific()
1513
	 */
1514
	@Transient
1515
	public boolean isInfraGeneric() {
1516
		if (rank == null){
1517
			return false;
1518
		}
1519
		return getRank().isInfraGeneric();
1520
	}
1521
	
1522
	/**
1523
	 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1524
	 * taxon name is higher than the species rank (true) or not (false).
1525
	 * Returns false if rank is null.
1526
	 * 
1527
	 * @see  #isGenus()
1528
	 * @see  #isInfraGeneric()
1529
	 * @see  #isSpecies()
1530
	 * @see  #isInfraSpecific()
1531
	 */
1532
	@Transient
1533
	public boolean isSupraSpecific(){
1534
		if (rank == null) {
1535
			return false;
1536
		}
1537
		return getRank().isHigher(Rank.SPECIES());
1538
	}
1539
	
1540
	/**
1541
	 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1542
	 * taxon name is the species rank (true) or not (false). Non viral names
1543
	 * with species rank are binomials.
1544
	 * Returns false if rank is null.
1545
	 *
1546
	 * @see  #isSupraGeneric()
1547
	 * @see  #isGenus()
1548
	 * @see  #isInfraGeneric()
1549
	 * @see  #isInfraSpecific()
1550
	 */
1551
	@Transient
1552
	public boolean isSpecies() {
1553
		if (rank == null){
1554
			return false;
1555
		}
1556
		return getRank().isSpecies();
1557
	}
1558
	/**
1559
	 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1560
	 * taxon name is lower than the species rank (true) or not (false).
1561
	 * Infraspecific non viral names are trinomials.
1562
	 * Returns false if rank is null.
1563
	 *
1564
	 * @see  #isSupraGeneric()
1565
	 * @see  #isGenus()
1566
	 * @see  #isInfraGeneric()
1567
	 * @see  #isSpecies()
1568
	 */
1569
	@Transient
1570
	public boolean isInfraSpecific() {
1571
		if (rank == null){
1572
			return false;
1573
		}
1574
		return getRank().isInfraSpecific();
1575
	}
1576
	
1577
	
1578
	/**
1579
	 * Returns null as the {@link NomenclaturalCode nomenclatural code} that governs
1580
	 * the construction of <i>this</i> taxon name since there is no specific
1581
	 * nomenclatural code defined. The real implementention takes place in the
1582
	 * subclasses {@link ViralName ViralName}, {@link BacterialName BacterialName},
1583
	 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
1584
	 * {@link ZoologicalName ZoologicalName}. Each taxon name is governed by one
1585
	 * and only one nomenclatural code. 
1586
	 *
1587
	 * @return  null
1588
	 * @see  	#isCodeCompliant()
1589
	 * @see  	#getHasProblem()
1590
	 */
1591
	abstract public NomenclaturalCode getNomenclaturalCode();
1592
	
1593
	/* (non-Javadoc)
1594
	 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1595
	 */
1596
	/**
1597
	 * Generates and returns the string with the scientific name of <i>this</i>
1598
	 * taxon name (only non viral taxon names can be generated from their
1599
	 * components). This string may be stored in the inherited
1600
	 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
1601
	 * This method overrides the generic and inherited
1602
	 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() method} from
1603
	 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
1604
	 *
1605
	 * @return  the string with the composed name of this non viral taxon name with authorship (and maybe year)
1606
	 * @see  	eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1607
	 * @see  	eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
1608
	 */
1609
//	@Override
1610
//	public abstract String generateTitle();
1611
	
1612
	/**
1613
	 * Creates a basionym relationship between this name and
1614
	 * 	each name in its homotypic group.
1615
	 * 
1616
	 * @param basionymName
1617
	 */
1618
	@Transient
1619
	public void setAsGroupsBasionym() {
1620

    
1621
		
1622
		HomotypicalGroup homotypicalGroup = this.getHomotypicalGroup();
1623
		
1624
		if (homotypicalGroup == null) {
1625
			return;
1626
		}
1627
		
1628
		Set<NameRelationship> relations = new HashSet<NameRelationship>();
1629
		Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
1630
		
1631
		for(TaxonNameBase<?, ?> typifiedName : homotypicalGroup.getTypifiedNames()){
1632
			
1633
			Set<NameRelationship> nameRelations = typifiedName.getRelationsFromThisName();
1634
			
1635
			for(NameRelationship nameRelation : nameRelations){
1636
				relations.add(nameRelation);
1637
			}
1638
		}
1639
		
1640
		for (NameRelationship relation : relations) {
1641
			
1642
			// If this is a basionym relation, and toName is in the homotypical group,
1643
			//	remove the relationship.
1644
			if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1645
					relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
1646
				removeRelations.add(relation);
1647
			}
1648
		}
1649
		
1650
		// Removing relations from a set through which we are iterating causes a 
1651
		//	ConcurrentModificationException. Therefore, we delete the targeted
1652
		//	relations in a second step.
1653
		for (NameRelationship relation : removeRelations) {
1654
			this.removeNameRelationship(relation);
1655
		}
1656
		
1657

    
1658
		for (TaxonNameBase<?, ?> name : homotypicalGroup.getTypifiedNames()) {
1659
			if (!name.equals(this)) {
1660
				
1661
				// First check whether the relationship already exists
1662
				if (!this.isBasionymFor(name)) {
1663
					
1664
					// Then create it
1665
					name.addRelationshipFromName(this, 
1666
							NameRelationshipType.BASIONYM(), null);
1667
				}
1668
			}
1669
		}
1670
	}
1671
	
1672
	/**
1673
	 * Removes basionym relationship between this name and
1674
	 * 	each name in its homotypic group.
1675
	 * 
1676
	 * @param basionymName
1677
	 */
1678
	@Transient
1679
	public void removeAsGroupsBasionym() {
1680

    
1681
		HomotypicalGroup homotypicalGroup = this.getHomotypicalGroup();
1682
		
1683
		if (homotypicalGroup == null) {
1684
			return;
1685
		}
1686
		
1687
		Set<NameRelationship> relations = new HashSet<NameRelationship>();
1688
		Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
1689
		
1690
		for(TaxonNameBase<?, ?> typifiedName : homotypicalGroup.getTypifiedNames()){
1691
			
1692
			Set<NameRelationship> nameRelations = typifiedName.getRelationsFromThisName();
1693
			
1694
			for(NameRelationship nameRelation : nameRelations){
1695
				relations.add(nameRelation);
1696
			}
1697
		}
1698
		
1699
		for (NameRelationship relation : relations) {
1700
			
1701
			// If this is a basionym relation, and toName is in the homotypical group,
1702
			//	and fromName is basionymName, remove the relationship.
1703
			if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1704
					relation.getFromName().equals(this) &&
1705
					relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
1706
				removeRelations.add(relation);
1707
			}
1708
		}
1709
		
1710
		// Removing relations from a set through which we are iterating causes a 
1711
		//	ConcurrentModificationException. Therefore, we delete the targeted
1712
		//	relations in a second step.
1713
		for (NameRelationship relation : removeRelations) {
1714
			this.removeNameRelationship(relation);
1715
		}
1716
	}
1717
}
(19-19/26)