Project

General

Profile

Download (58.7 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 EDIT
3
* European Distributed Institute of Taxonomy 
4
* http://www.e-taxonomy.eu
5
* 
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9

    
10
package eu.etaxonomy.cdm.model.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
	private Set<TaxonNameDescription> descriptions = new HashSet<TaxonNameDescription>();
134
	
135
    @XmlElement(name = "AppendedPhrase")
136
    @Field(index= org.hibernate.search.annotations.Index.TOKENIZED)
137
    @CacheUpdate(value ="nameCache")
138
    @NullOrNotEmpty
139
    @Size(max = 255)
140
	private String appendedPhrase;
141
	
142
    @XmlElement(name = "NomenclaturalMicroReference")
143
    @Field(index= org.hibernate.search.annotations.Index.TOKENIZED)
144
    @CacheUpdate(noUpdate ="titleCache")
145
    @NullOrNotEmpty
146
    @Size(max = 255)
147
	private String nomenclaturalMicroReference;
148
	
149
    @XmlAttribute
150
    @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
151
	private int parsingProblem = 0;
152
	
153
    @XmlAttribute
154
    @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
155
    private int problemStarts = -1;
156
    
157
    @XmlAttribute
158
    @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
159
    private int problemEnds = -1;
160
    
161
    @XmlElementWrapper(name = "TypeDesignations")
162
    @XmlElement(name = "TypeDesignation")
163
    @XmlIDREF
164
    @XmlSchemaType(name = "IDREF")
165
    @ManyToMany(fetch = FetchType.LAZY)
166
	//TODO @Cascade({CascadeType.DELETE_ORPHAN})
167
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
168
	@NotNull
169
	private Set<TypeDesignationBase> typeDesignations = new HashSet<TypeDesignationBase>();
170

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

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

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

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

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

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

    
309
	
310

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1105
	
1106
//*********************** HOMOTYPICAL GROUP *********************************************//	
1107

    
1108
	
1109
	/** 
1110
	 * Returns the {@link HomotypicalGroup homotypical group} to which
1111
	 * <i>this</i> taxon name belongs. A homotypical group represents all taxon names
1112
	 * that share the same types.
1113
	 *
1114
	 * @see 	HomotypicalGroup
1115
	 */
1116
	
1117
	public HomotypicalGroup getHomotypicalGroup() {
1118
		return homotypicalGroup;
1119
	}
1120
	
1121
	/* 
1122
	 * @see #getHomotypicalGroup()
1123
	 */
1124
	protected void setHomotypicalGroup(HomotypicalGroup homotypicalGroup) {
1125
		this.homotypicalGroup = homotypicalGroup;
1126
	}
1127
	
1128
	
1129

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

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

    
1159
	/**
1160
	 * Returns the parsing problems 
1161
	 * @return
1162
	 */
1163
	public List<ParserProblem> getParsingProblems(){
1164
		return ParserProblem.warningList(this.parsingProblem);
1165
	}
1166

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

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

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

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

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

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