Project

General

Profile

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

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

    
49
import eu.etaxonomy.cdm.model.common.IParsable;
50
import eu.etaxonomy.cdm.model.common.IReferencedEntity;
51
import eu.etaxonomy.cdm.model.common.IRelated;
52
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
53
import eu.etaxonomy.cdm.model.common.RelationshipBase;
54
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
55
import eu.etaxonomy.cdm.model.occurrence.Specimen;
56
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
57
import eu.etaxonomy.cdm.model.reference.ReferenceBase;
58
import eu.etaxonomy.cdm.model.taxon.Synonym;
59
import eu.etaxonomy.cdm.model.taxon.Taxon;
60
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
61
import eu.etaxonomy.cdm.strategy.cache.name.CacheUpdate;
62
import eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy;
63
import eu.etaxonomy.cdm.strategy.match.IMatchable;
64
import eu.etaxonomy.cdm.strategy.match.Match;
65
import eu.etaxonomy.cdm.strategy.match.MatchMode;
66
import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
67
import eu.etaxonomy.cdm.strategy.merge.Merge;
68
import eu.etaxonomy.cdm.strategy.merge.MergeMode;
69
import eu.etaxonomy.cdm.strategy.parser.ParserProblem;
70
import eu.etaxonomy.cdm.validation.Level2;
71
import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
72

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

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

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

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

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

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

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

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

    
310
	
311

    
312
	@Transient
313
	public List<Object> getTaggedName(){
314
		return getCacheStrategy().getTaggedName(this);
315
	}
316
	
317
	@Transient
318
	public String getFullTitleCache(){
319
		if (protectedFullTitleCache){
320
			return this.fullTitleCache;			
321
		}
322
		if (fullTitleCache == null ){
323
			this.fullTitleCache = getTruncatedCache(generateFullTitle());
324
		}
325
		return fullTitleCache;
326
	}
327

    
328
	
329
    public void setFullTitleCache(String fullTitleCache){
330
		setFullTitleCache(fullTitleCache, PROTECTED);
331
	}
332
	
333
	public void setFullTitleCache(String fullTitleCache, boolean protectCache){
334
		fullTitleCache = getTruncatedCache(fullTitleCache);
335
		this.fullTitleCache = fullTitleCache;
336
		this.setProtectedFullTitleCache(protectCache);
337
	}
338

    
339
	
340
	public boolean isProtectedFullTitleCache() {
341
		return protectedFullTitleCache;
342
	}
343

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

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

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

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

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

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

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

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

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

    
795
	/** 
796
	 * Returns the appended phrase string assigned to <i>this</i> taxon name.
797
	 * The appended phrase is a non-atomised addition to a name. It is
798
	 * not ruled by a nomenclatural code.
799
	 */
800
	public String getAppendedPhrase(){
801
		return this.appendedPhrase;
802
	}
803
	
804
	/**
805
	 * @see  #getAppendedPhrase()
806
	 */
807
	public void setAppendedPhrase(String appendedPhrase){
808
		this.appendedPhrase = appendedPhrase;
809
	}
810

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

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

    
903
	/* (non-Javadoc)
904
	 * @see eu.etaxonomy.cdm.model.common.IParsable#setProblemEnds(int)
905
	 */
906
	public void setProblemEnds(int end) {
907
		this.problemEnds = end;
908
	}
909

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

    
1052
	
1053
	/** 
1054
	 * Creates and adds a new {@link SpecimenTypeDesignation specimen type designation}
1055
	 * to <i>this</i> taxon name's set of type designations.
1056
	 *
1057
	 * @param  typeSpecimen				the specimen to be used as a type for <i>this</i> taxon name
1058
	 * @param  status					the specimen type designation status
1059
	 * @param  citation					the reference for this new specimen type designation
1060
	 * @param  citationMicroReference	the string with the details (generally pages) within the reference
1061
	 * @param  originalNameString		the taxon name used in the reference to assert this designation
1062
	 * @param  isNotDesignated			the boolean status for a specimen type designation without specimen type
1063
	 * @param  addToAllHomotypicNames	the boolean indicating whether the specimen type designation should be
1064
	 * 									added to all taxon names of the homotypical group the typified
1065
	 * 									taxon name belongs to
1066
	 * @see 			  				#getSpecimenTypeDesignations()
1067
	 * @see 			  				SpecimenTypeDesignationStatus
1068
	 * @see 			  				SpecimenTypeDesignation
1069
	 * @see 			  				TypeDesignationBase#isNotDesignated()
1070
	 */
1071
	public void addSpecimenTypeDesignation(Specimen typeSpecimen, 
1072
				SpecimenTypeDesignationStatus status, 
1073
				ReferenceBase citation, 
1074
				String citationMicroReference, 
1075
				String originalNameString, 
1076
				boolean isNotDesignated, 
1077
				boolean addToAllHomotypicNames) {
1078
		SpecimenTypeDesignation specimenTypeDesignation = new SpecimenTypeDesignation(typeSpecimen, status, citation, citationMicroReference, originalNameString, isNotDesignated);
1079
		addTypeDesignation(specimenTypeDesignation, addToAllHomotypicNames);
1080
	}
1081
	
1082
	//used by merge strategy
1083
	private boolean addTypeDesignation(TypeDesignationBase typeDesignation){
1084
		return addTypeDesignation(typeDesignation, true);
1085
	}
1086
	
1087
	private boolean addTypeDesignation(TypeDesignationBase typeDesignation, boolean addToAllNames){
1088
		//at them moment typeDesignations are not persisted with the homotypical group
1089
		//so explicit adding to the homotypical group is not necessary.
1090
		if (typeDesignation != null){
1091
			this.typeDesignations.add(typeDesignation);
1092
			typeDesignation.addTypifiedName(this);
1093
			
1094
			if (addToAllNames){
1095
				for (TaxonNameBase taxonName : this.getHomotypicalGroup().getTypifiedNames()){
1096
					if (taxonName != this){
1097
						taxonName.addTypeDesignation(typeDesignation, false);
1098
					}
1099
				}
1100
			}
1101
		}
1102
		return true;
1103
	}
1104
	
1105

    
1106
	
1107
//*********************** HOMOTYPICAL GROUP *********************************************//	
1108

    
1109
	
1110
	/** 
1111
	 * Returns the {@link HomotypicalGroup homotypical group} to which
1112
	 * <i>this</i> taxon name belongs. A homotypical group represents all taxon names
1113
	 * that share the same types.
1114
	 *
1115
	 * @see 	HomotypicalGroup
1116
	 */
1117
	
1118
	public HomotypicalGroup getHomotypicalGroup() {
1119
		return homotypicalGroup;
1120
	}
1121
	
1122
	/* 
1123
	 * @see #getHomotypicalGroup()
1124
	 */
1125
	protected void setHomotypicalGroup(HomotypicalGroup homotypicalGroup) {
1126
		if (homotypicalGroup == null){
1127
			throw new IllegalArgumentException("HomotypicalGroup of name should never be null but was set to 'null'");
1128
		}
1129
		this.homotypicalGroup = homotypicalGroup;
1130
	}
1131
	
1132
	
1133

    
1134
// *************************************************************************//
1135
	
1136
	/** 
1137
	 * @see #getNomenclaturalReference()
1138
	 */
1139
	@Transient
1140
	public ReferenceBase getCitation(){
1141
		//TODO What is the purpose of this method differing from the getNomenclaturalReference method? 
1142
		logger.warn("getCitation not yet implemented");
1143
		return null;
1144
	}
1145

    
1146
	/** 
1147
	 * Returns the complete string containing the
1148
	 * {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation() nomenclatural reference citation}
1149
	 * and the {@link #getNomenclaturalMicroReference() details} assigned to <i>this</i> taxon name.
1150
	 * 
1151
	 * @return  the string containing the nomenclatural reference of <i>this</i> taxon name
1152
	 * @see		eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation()
1153
	 * @see		#getNomenclaturalReference()
1154
	 * @see		#getNomenclaturalMicroReference()
1155
	 */
1156
	@Transient
1157
	@Deprecated
1158
	public String getCitationString(){
1159
		logger.warn("getCitationString not yet implemented");
1160
		return null;
1161
	}
1162

    
1163
	/**
1164
	 * Returns the parsing problems 
1165
	 * @return
1166
	 */
1167
	public List<ParserProblem> getParsingProblems(){
1168
		return ParserProblem.warningList(this.parsingProblem);
1169
	}
1170

    
1171
	/**
1172
	 * Returns the string containing the publication date (generally only year)
1173
	 * of the {@link #getNomenclaturalReference() nomenclatural reference} for <i>this</i> taxon name, null if there is
1174
	 * no nomenclatural reference.
1175
	 * 
1176
	 * @return  the string containing the publication date of <i>this</i> taxon name
1177
	 * @see		eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getYear()
1178
	 */
1179
	@Transient
1180
	public String getReferenceYear(){
1181
		if (this.getNomenclaturalReference() != null ){
1182
			return this.getNomenclaturalReference().getYear();
1183
		}else{
1184
			return null;
1185
		}
1186
	}
1187

    
1188
	/** 
1189
	 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1190
	 * In this context a taxon base means the use of a taxon name by a reference
1191
	 * either as a {@link eu.etaxonomy.cdm.model.taxon.Taxon taxon} ("accepted/correct" name) or
1192
	 * as a (junior) {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
1193
	 * A taxon name can be used by several distinct {@link eu.etaxonomy.cdm.model.reference.ReferenceBase references} but only once
1194
	 * within a taxonomic treatment (identified by one reference).
1195
	 *
1196
	 * @see	#getTaxa()
1197
	 * @see	#getSynonyms()
1198
	 */
1199
	public Set<TaxonBase> getTaxonBases() {
1200
		if(taxonBases == null) {
1201
			this.taxonBases = new HashSet<TaxonBase>();
1202
		}
1203
		return this.taxonBases;
1204
	}
1205
	
1206
	/** 
1207
	 * Adds a new {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon base}
1208
	 * to the set of taxon bases using <i>this</i> taxon name.
1209
	 *
1210
	 * @param  taxonBase  the taxon base to be added
1211
	 * @see 			  #getTaxonBases()
1212
	 * @see 			  #removeTaxonBase(TaxonBase)
1213
	 */
1214
	//TODO protected
1215
	public void addTaxonBase(TaxonBase taxonBase){
1216
		Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonNameBase.class});
1217
		ReflectionUtils.makeAccessible(method);
1218
		ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {this});
1219
		taxonBases.add(taxonBase);
1220
	}
1221
	/** 
1222
	 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1223
	 *
1224
	 * @param  taxonBase	the taxon base which should be removed from the corresponding set
1225
	 * @see    				#getTaxonBases()
1226
	 * @see    				#addTaxonBase(TaxonBase)
1227
	 */
1228
	public void removeTaxonBase(TaxonBase taxonBase){
1229
		Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonNameBase.class});
1230
		ReflectionUtils.makeAccessible(method);
1231
		ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {null});
1232
		taxonBases.remove(taxonBase);
1233
	}
1234
	
1235
	/**
1236
	 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Taxon taxa} ("accepted/correct" names according to any
1237
	 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1238
	 * the set returned by getTaxonBases(). 
1239
	 * 
1240
	 * @see	eu.etaxonomy.cdm.model.taxon.Taxon
1241
	 * @see	#getTaxonBases()
1242
	 * @see	#getSynonyms()
1243
	 */
1244
	@Transient
1245
	public Set<Taxon> getTaxa(){
1246
		Set<Taxon> result = new HashSet<Taxon>();
1247
		for (TaxonBase taxonBase : this.taxonBases){
1248
			if (taxonBase instanceof Taxon){
1249
				result.add((Taxon)taxonBase);
1250
			}
1251
		}
1252
		return result;
1253
	}
1254
	
1255
	/**
1256
	 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Synonym (junior) synonyms} (according to any
1257
	 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1258
	 * the set returned by getTaxonBases(). 
1259
	 * 
1260
	 * @see	eu.etaxonomy.cdm.model.taxon.Synonym
1261
	 * @see	#getTaxonBases()
1262
	 * @see	#getTaxa()
1263
	 */
1264
	@Transient
1265
	public Set<Synonym> getSynonyms() {
1266
		Set<Synonym> result = new HashSet<Synonym>();
1267
		for (TaxonBase taxonBase : this.taxonBases){
1268
			if (taxonBase instanceof Synonym){
1269
				result.add((Synonym)taxonBase);
1270
			}
1271
		}
1272
		return result;
1273
	}
1274
	
1275
	
1276
// *********** DESCRIPTIONS *************************************	
1277

    
1278
	/**
1279
	 * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1280
	 * to <i>this</i> taxon name. A taxon name description is a piece of information
1281
	 * concerning the taxon name like for instance the content of its first
1282
	 * publication (protolog) or a picture of this publication.
1283
	 * 
1284
	 * @see	#addDescription(TaxonNameDescription)
1285
	 * @see	#removeDescription(TaxonNameDescription)
1286
	 * @see	eu.etaxonomy.cdm.model.description.TaxonNameDescription
1287
	 */
1288
	public Set<TaxonNameDescription> getDescriptions() {
1289
		return descriptions;
1290
	}
1291

    
1292
	/** 
1293
	 * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name description}
1294
	 * to the set of taxon name descriptions assigned to <i>this</i> taxon name. The
1295
	 * content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute} of the
1296
	 * taxon name description itself will be replaced with <i>this</i> taxon name.
1297
	 *
1298
	 * @param  description  the taxon name description to be added
1299
	 * @see					#getDescriptions()
1300
	 * @see 			  	#removeDescription(TaxonNameDescription)
1301
	 */
1302
	public void addDescription(TaxonNameDescription description) {
1303
		java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonNameBase.class);
1304
		ReflectionUtils.makeAccessible(field);
1305
		ReflectionUtils.setField(field, description, this);
1306
		descriptions.add(description);
1307
	}
1308
	/** 
1309
	 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1310
	 * to <i>this</i> taxon name. The content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute}
1311
	 * of the description itself will be set to "null".
1312
	 *
1313
	 * @param  description  the taxon name description which should be removed
1314
	 * @see     		  	#getDescriptions()
1315
	 * @see     		  	#addDescription(TaxonNameDescription)
1316
	 * @see 			  	eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName()
1317
	 */
1318
	public void removeDescription(TaxonNameDescription description) {
1319
		java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonNameBase.class);
1320
		ReflectionUtils.makeAccessible(field);
1321
		ReflectionUtils.setField(field, description, null);
1322
		descriptions.remove(description);
1323
	}
1324
	
1325
// *********** HOMOTYPIC GROUP METHODS **************************************************
1326

    
1327
	@Transient
1328
	public void mergeHomotypicGroups(TaxonNameBase name){
1329
		this.getHomotypicalGroup().merge(name.getHomotypicalGroup());
1330
		//HomotypicalGroup thatGroup = name.homotypicalGroup;
1331
		name.setHomotypicalGroup(this.homotypicalGroup);
1332
	}
1333
	
1334
	/**
1335
	 * Returns the boolean value indicating whether a given taxon name belongs
1336
	 * to the same {@link HomotypicalGroup homotypical group} as <i>this</i> taxon name (true)
1337
	 * or not (false). Returns "true" only if the homotypical groups of both
1338
	 * taxon names exist and if they are identical. 
1339
	 *
1340
	 * @param	homoTypicName  the taxon name the homotypical group of which is to be checked
1341
	 * @return  			   the boolean value of the check
1342
	 * @see     			   HomotypicalGroup
1343
	 */
1344
	@Transient
1345
	public boolean isHomotypic(TaxonNameBase homoTypicName) {
1346
		if (homoTypicName == null) {
1347
			return false;
1348
		}
1349
		HomotypicalGroup homotypicGroup = homoTypicName.getHomotypicalGroup();
1350
		if (homotypicGroup == null || this.getHomotypicalGroup() == null) {
1351
			return false;
1352
		}
1353
		if (homotypicGroup.equals(this.getHomotypicalGroup())) {
1354
			return true;
1355
		}
1356
		return false;
1357
	}
1358
	
1359
	
1360
    /**
1361
     * Checks whether name is a basionym for ALL names
1362
     * in its homotypical group.
1363
     * Returns <code>false</code> if there are no other names in the group
1364
     * @param name
1365
     * @return
1366
     */  
1367
	@Transient
1368
	public boolean isGroupsBasionym() {
1369
		Set<TaxonNameBase> typifiedNames = homotypicalGroup.getTypifiedNames();
1370
		
1371
		// Check whether there are any other names in the group
1372
	    if (typifiedNames.size() == 1) {
1373
	            return false;
1374
	    }
1375
	   
1376
	    boolean isBasionymToAll = true;
1377
	                   
1378
	    for (TaxonNameBase taxonName : typifiedNames) {
1379
	            if (!taxonName.equals(this)) {
1380
	                    if (! isBasionymFor(taxonName)) {
1381
	                            return false;
1382
	                    }
1383
	            }
1384
	    }
1385
	    return true;            
1386
	}
1387
	
1388
    /**
1389
     * Checks whether a basionym relationship exists between fromName and toName.
1390
     *
1391
     * @param fromName
1392
     * @param toName
1393
     * @return
1394
     */
1395
	@Transient
1396
	public boolean isBasionymFor(TaxonNameBase newCombinationName) {
1397
            Set<NameRelationship> relations = newCombinationName.getRelationsToThisName();
1398
            for (NameRelationship relation : relations) {
1399
                    if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1400
                                    relation.getFromName().equals(this)) {
1401
                            return true;
1402
                    }
1403
            }
1404
            return false;
1405
    }
1406
    
1407
    /**
1408
     * Creates a basionym relationship to all other names in this names homotypical
1409
     * group.
1410
     * 
1411
     * @see HomotypicalGroup.setGroupBasionym(TaxonNameBase basionymName)
1412

    
1413
     */
1414
	/* (non-Javadoc)
1415
	 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup#setGroupBasionym(TaxonNameBase)
1416
	 */
1417
	@Transient
1418
	public void makeGroupsBasionym() {
1419
        this.homotypicalGroup.setGroupBasionym(this);
1420
    }
1421
	
1422
	
1423
//*********  Rank comparison shortcuts   ********************//
1424
	/**
1425
	 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1426
	 * taxon name is higher than the genus rank (true) or not (false).
1427
	 * Suprageneric non viral names are monomials.
1428
	 * Returns false if rank is null.
1429
	 * 
1430
	 * @see  #isGenus()
1431
	 * @see  #isInfraGeneric()
1432
	 * @see  #isSpecies()
1433
	 * @see  #isInfraSpecific()
1434
	 */
1435
	@Transient
1436
	public boolean isSupraGeneric() {
1437
		if (rank == null){
1438
			return false;
1439
		}
1440
		return getRank().isSupraGeneric();
1441
	}
1442
	/**
1443
	 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1444
	 * taxon name is the genus rank (true) or not (false). Non viral names with
1445
	 * genus rank are monomials. Returns false if rank is null.
1446
	 *
1447
	 * @see  #isSupraGeneric()
1448
	 * @see  #isInfraGeneric()
1449
	 * @see  #isSpecies()
1450
	 * @see  #isInfraSpecific()
1451
	 */
1452
	@Transient
1453
	public boolean isGenus() {
1454
		if (rank == null){
1455
			return false;
1456
		}
1457
		return getRank().isGenus();
1458
	}
1459
	/**
1460
	 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1461
	 * taxon name is higher than the species rank and lower than the
1462
	 * genus rank (true) or not (false). Infrageneric non viral names are
1463
	 * binomials. Returns false if rank is null.
1464
	 *
1465
	 * @see  #isSupraGeneric()
1466
	 * @see  #isGenus()
1467
	 * @see  #isSpecies()
1468
	 * @see  #isInfraSpecific()
1469
	 */
1470
	@Transient
1471
	public boolean isInfraGeneric() {
1472
		if (rank == null){
1473
			return false;
1474
		}
1475
		return getRank().isInfraGeneric();
1476
	}
1477
	/**
1478
	 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1479
	 * taxon name is the species rank (true) or not (false). Non viral names
1480
	 * with species rank are binomials.
1481
	 * Returns false if rank is null.
1482
	 *
1483
	 * @see  #isSupraGeneric()
1484
	 * @see  #isGenus()
1485
	 * @see  #isInfraGeneric()
1486
	 * @see  #isInfraSpecific()
1487
	 */
1488
	@Transient
1489
	public boolean isSpecies() {
1490
		if (rank == null){
1491
			return false;
1492
		}
1493
		return getRank().isSpecies();
1494
	}
1495
	/**
1496
	 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1497
	 * taxon name is lower than the species rank (true) or not (false).
1498
	 * Infraspecific non viral names are trinomials.
1499
	 * Returns false if rank is null.
1500
	 *
1501
	 * @see  #isSupraGeneric()
1502
	 * @see  #isGenus()
1503
	 * @see  #isInfraGeneric()
1504
	 * @see  #isSpecies()
1505
	 */
1506
	@Transient
1507
	public boolean isInfraSpecific() {
1508
		if (rank == null){
1509
			return false;
1510
		}
1511
		return getRank().isInfraSpecific();
1512
	}
1513
	
1514
	
1515
	/**
1516
	 * Returns null as the {@link NomenclaturalCode nomenclatural code} that governs
1517
	 * the construction of <i>this</i> taxon name since there is no specific
1518
	 * nomenclatural code defined. The real implementention takes place in the
1519
	 * subclasses {@link ViralName ViralName}, {@link BacterialName BacterialName},
1520
	 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
1521
	 * {@link ZoologicalName ZoologicalName}. Each taxon name is governed by one
1522
	 * and only one nomenclatural code. 
1523
	 *
1524
	 * @return  null
1525
	 * @see  	#isCodeCompliant()
1526
	 * @see  	#getHasProblem()
1527
	 */
1528
	abstract public NomenclaturalCode getNomenclaturalCode();
1529
	/* (non-Javadoc)
1530
	 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1531
	 */
1532
	/**
1533
	 * Generates and returns the string with the scientific name of <i>this</i>
1534
	 * taxon name (only non viral taxon names can be generated from their
1535
	 * components). This string may be stored in the inherited
1536
	 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
1537
	 * This method overrides the generic and inherited
1538
	 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() method} from
1539
	 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
1540
	 *
1541
	 * @return  the string with the composed name of this non viral taxon name with authorship (and maybe year)
1542
	 * @see  	eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1543
	 * @see  	eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
1544
	 */
1545
//	@Override
1546
//	public abstract String generateTitle();
1547
}
(19-19/25)