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.beans.PropertyChangeEvent;
|
13
|
import java.beans.PropertyChangeListener;
|
14
|
import java.lang.reflect.Method;
|
15
|
import java.util.ArrayList;
|
16
|
import java.util.Collections;
|
17
|
import java.util.HashSet;
|
18
|
import java.util.List;
|
19
|
import java.util.Map;
|
20
|
import java.util.Set;
|
21
|
|
22
|
import javax.persistence.Column;
|
23
|
import javax.persistence.Entity;
|
24
|
import javax.persistence.FetchType;
|
25
|
import javax.persistence.Inheritance;
|
26
|
import javax.persistence.InheritanceType;
|
27
|
import javax.persistence.JoinTable;
|
28
|
import javax.persistence.ManyToMany;
|
29
|
import javax.persistence.ManyToOne;
|
30
|
import javax.persistence.OneToMany;
|
31
|
import javax.persistence.Table;
|
32
|
import javax.persistence.Transient;
|
33
|
import javax.validation.Valid;
|
34
|
import javax.validation.constraints.Min;
|
35
|
import javax.validation.constraints.NotNull;
|
36
|
import javax.validation.constraints.Pattern;
|
37
|
import javax.xml.bind.annotation.XmlAccessType;
|
38
|
import javax.xml.bind.annotation.XmlAccessorType;
|
39
|
import javax.xml.bind.annotation.XmlAttribute;
|
40
|
import javax.xml.bind.annotation.XmlElement;
|
41
|
import javax.xml.bind.annotation.XmlElementWrapper;
|
42
|
import javax.xml.bind.annotation.XmlIDREF;
|
43
|
import javax.xml.bind.annotation.XmlRootElement;
|
44
|
import javax.xml.bind.annotation.XmlSchemaType;
|
45
|
import javax.xml.bind.annotation.XmlType;
|
46
|
|
47
|
import org.apache.commons.lang.StringUtils;
|
48
|
import org.apache.log4j.Logger;
|
49
|
import org.hibernate.annotations.Cascade;
|
50
|
import org.hibernate.annotations.CascadeType;
|
51
|
import org.hibernate.annotations.Type;
|
52
|
import org.hibernate.envers.Audited;
|
53
|
import org.hibernate.search.annotations.Analyze;
|
54
|
import org.hibernate.search.annotations.Analyzer;
|
55
|
import org.hibernate.search.annotations.Field;
|
56
|
import org.hibernate.search.annotations.Fields;
|
57
|
import org.hibernate.search.annotations.Index;
|
58
|
import org.hibernate.search.annotations.Indexed;
|
59
|
import org.hibernate.search.annotations.IndexedEmbedded;
|
60
|
import org.hibernate.search.annotations.Store;
|
61
|
import org.hibernate.validator.constraints.NotEmpty;
|
62
|
import org.springframework.util.ReflectionUtils;
|
63
|
|
64
|
import eu.etaxonomy.cdm.common.CdmUtils;
|
65
|
import eu.etaxonomy.cdm.common.UTF8;
|
66
|
import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
|
67
|
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
|
68
|
import eu.etaxonomy.cdm.model.common.CdmBase;
|
69
|
import eu.etaxonomy.cdm.model.common.IIntextReferenceTarget;
|
70
|
import eu.etaxonomy.cdm.model.common.IParsable;
|
71
|
import eu.etaxonomy.cdm.model.common.IRelated;
|
72
|
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
|
73
|
import eu.etaxonomy.cdm.model.common.RelationshipBase;
|
74
|
import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
|
75
|
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
|
76
|
import eu.etaxonomy.cdm.model.description.IDescribable;
|
77
|
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
|
78
|
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
|
79
|
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
|
80
|
import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
|
81
|
import eu.etaxonomy.cdm.model.reference.Reference;
|
82
|
import eu.etaxonomy.cdm.model.taxon.Synonym;
|
83
|
import eu.etaxonomy.cdm.model.taxon.Taxon;
|
84
|
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
|
85
|
import eu.etaxonomy.cdm.model.term.DefinedTermBase;
|
86
|
import eu.etaxonomy.cdm.model.term.TermType;
|
87
|
import eu.etaxonomy.cdm.model.term.TermVocabulary;
|
88
|
import eu.etaxonomy.cdm.strategy.cache.TaggedText;
|
89
|
import eu.etaxonomy.cdm.strategy.cache.name.CacheUpdate;
|
90
|
import eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy;
|
91
|
import eu.etaxonomy.cdm.strategy.cache.name.TaxonNameDefaultCacheStrategy;
|
92
|
import eu.etaxonomy.cdm.strategy.match.IMatchable;
|
93
|
import eu.etaxonomy.cdm.strategy.match.Match;
|
94
|
import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
|
95
|
import eu.etaxonomy.cdm.strategy.match.MatchMode;
|
96
|
import eu.etaxonomy.cdm.strategy.merge.Merge;
|
97
|
import eu.etaxonomy.cdm.strategy.merge.MergeMode;
|
98
|
import eu.etaxonomy.cdm.strategy.parser.ParserProblem;
|
99
|
import eu.etaxonomy.cdm.validation.Level2;
|
100
|
import eu.etaxonomy.cdm.validation.Level3;
|
101
|
import eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank;
|
102
|
import eu.etaxonomy.cdm.validation.annotation.NameMustFollowCode;
|
103
|
import eu.etaxonomy.cdm.validation.annotation.NameMustHaveAuthority;
|
104
|
import eu.etaxonomy.cdm.validation.annotation.NoDuplicateNames;
|
105
|
import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
|
106
|
import eu.etaxonomy.cdm.validation.annotation.ValidTaxonomicYear;
|
107
|
|
108
|
/**
|
109
|
* The upmost (abstract) class for scientific taxon names regardless of any
|
110
|
* particular {@link NomenclaturalCode nomenclature code}. The scientific taxon name does not depend
|
111
|
* on the use made of it in a publication or a treatment
|
112
|
* ({@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon concept respectively potential taxon})
|
113
|
* as an {@link eu.etaxonomy.cdm.model.taxon.Taxon "accepted" respectively "correct" (taxon) name}
|
114
|
* or as a {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
|
115
|
* <P>
|
116
|
* This class corresponds partially to: <ul>
|
117
|
* <li> TaxonName according to the TDWG ontology
|
118
|
* <li> ScientificName and CanonicalName according to the TCS
|
119
|
* <li> ScientificName according to the ABCD schema
|
120
|
* </ul>
|
121
|
*
|
122
|
* @author m.doering
|
123
|
* @since 08-Nov-2007 13:06:57
|
124
|
*/
|
125
|
@XmlAccessorType(XmlAccessType.FIELD)
|
126
|
@XmlType(name = "TaxonName", propOrder = {
|
127
|
"nameType",
|
128
|
"appendedPhrase",
|
129
|
"nomenclaturalMicroReference",
|
130
|
"nomenclaturalReference",
|
131
|
"nomenclaturalSource",
|
132
|
"rank",
|
133
|
"fullTitleCache",
|
134
|
"protectedFullTitleCache",
|
135
|
"homotypicalGroup",
|
136
|
"typeDesignations",
|
137
|
"relationsFromThisName",
|
138
|
"relationsToThisName",
|
139
|
"status",
|
140
|
"descriptions",
|
141
|
"taxonBases",
|
142
|
"registrations",
|
143
|
|
144
|
"nameCache",
|
145
|
"genusOrUninomial",
|
146
|
"infraGenericEpithet",
|
147
|
"specificEpithet",
|
148
|
"infraSpecificEpithet",
|
149
|
"combinationAuthorship",
|
150
|
"exCombinationAuthorship",
|
151
|
"basionymAuthorship",
|
152
|
"exBasionymAuthorship",
|
153
|
"authorshipCache",
|
154
|
"protectedAuthorshipCache",
|
155
|
"protectedNameCache",
|
156
|
"hybridParentRelations",
|
157
|
"hybridChildRelations",
|
158
|
"hybridFormula",
|
159
|
"monomHybrid",
|
160
|
"binomHybrid",
|
161
|
"trinomHybrid",
|
162
|
|
163
|
"acronym",
|
164
|
|
165
|
"subGenusAuthorship",
|
166
|
"nameApprobation",
|
167
|
|
168
|
"breed",
|
169
|
"publicationYear",
|
170
|
"originalPublicationYear",
|
171
|
"inCombinationAuthorship",
|
172
|
"inBasionymAuthorship",
|
173
|
|
174
|
"anamorphic",
|
175
|
|
176
|
"cultivarName"
|
177
|
})
|
178
|
@XmlRootElement(name = "TaxonName")
|
179
|
@Entity
|
180
|
@Audited
|
181
|
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
|
182
|
@Table(name="TaxonName", indexes = {
|
183
|
@javax.persistence.Index(name = "taxonNameBaseTitleCacheIndex", columnList = "titleCache"),
|
184
|
@javax.persistence.Index(name = "taxonNameBaseNameCacheIndex", columnList = "nameCache") })
|
185
|
@NameMustFollowCode
|
186
|
@CorrectEpithetsForRank(groups = Level2.class)
|
187
|
@NameMustHaveAuthority(groups = Level2.class)
|
188
|
@NoDuplicateNames(groups = Level3.class)
|
189
|
@Indexed(index = "eu.etaxonomy.cdm.model.name.TaxonName")
|
190
|
public class TaxonName
|
191
|
extends IdentifiableEntity<INameCacheStrategy>
|
192
|
implements ITaxonNameBase, INonViralName, IViralName, IBacterialName, IZoologicalName,
|
193
|
IBotanicalName, ICultivarPlantName, IFungusName,
|
194
|
IParsable, IRelated, IMatchable, IIntextReferenceTarget, Cloneable,
|
195
|
IDescribable<TaxonNameDescription>{
|
196
|
|
197
|
private static final long serialVersionUID = -791164269603409712L;
|
198
|
private static final Logger logger = Logger.getLogger(TaxonName.class);
|
199
|
|
200
|
|
201
|
/**
|
202
|
* The {@link TermType type} of this term. Needs to be the same type in a {@link DefinedTermBase defined term}
|
203
|
* and in it's {@link TermVocabulary vocabulary}.
|
204
|
*/
|
205
|
@XmlAttribute(name ="NameType")
|
206
|
@Column(name="nameType", length=15)
|
207
|
@NotNull
|
208
|
@Type(type = "eu.etaxonomy.cdm.hibernate.EnumUserType",
|
209
|
parameters = {@org.hibernate.annotations.Parameter(name = "enumClass", value = "eu.etaxonomy.cdm.model.name.NomenclaturalCode")}
|
210
|
)
|
211
|
@Audited //needed ?
|
212
|
private NomenclaturalCode nameType;
|
213
|
|
214
|
@XmlElement(name = "FullTitleCache")
|
215
|
@Column(length=800, name="fullTitleCache") //see #1592
|
216
|
@Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
|
217
|
@CacheUpdate(noUpdate ="titleCache")
|
218
|
@NotEmpty(groups = Level2.class)
|
219
|
protected String fullTitleCache;
|
220
|
|
221
|
//if true titleCache will not be automatically generated/updated
|
222
|
@XmlElement(name = "ProtectedFullTitleCache")
|
223
|
@CacheUpdate(value ="fullTitleCache", noUpdate ="titleCache")
|
224
|
private boolean protectedFullTitleCache;
|
225
|
|
226
|
@XmlElementWrapper(name = "Descriptions")
|
227
|
@XmlElement(name = "Description")
|
228
|
@OneToMany(mappedBy="taxonName", fetch= FetchType.LAZY, orphanRemoval=true)
|
229
|
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
|
230
|
@NotNull
|
231
|
private Set<TaxonNameDescription> descriptions = new HashSet<>();
|
232
|
|
233
|
@XmlElement(name = "AppendedPhrase")
|
234
|
@Field
|
235
|
@CacheUpdate(value ="nameCache")
|
236
|
//TODO Val #3379
|
237
|
// @NullOrNotEmpty
|
238
|
@Column(length=255)
|
239
|
private String appendedPhrase;
|
240
|
|
241
|
//#6581
|
242
|
@XmlElement(name = "NomenclaturalMicroReference")
|
243
|
@Field
|
244
|
@CacheUpdate(noUpdate ="titleCache")
|
245
|
//TODO Val #3379
|
246
|
//@NullOrNotEmpty
|
247
|
@Column(length=255)
|
248
|
private String nomenclaturalMicroReference;
|
249
|
|
250
|
@XmlAttribute
|
251
|
@CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
|
252
|
private int parsingProblem = 0;
|
253
|
|
254
|
@XmlAttribute
|
255
|
@CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
|
256
|
private int problemStarts = -1;
|
257
|
|
258
|
@XmlAttribute
|
259
|
@CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
|
260
|
private int problemEnds = -1;
|
261
|
|
262
|
@XmlElementWrapper(name = "TypeDesignations")
|
263
|
@XmlElement(name = "TypeDesignation")
|
264
|
@XmlIDREF
|
265
|
@XmlSchemaType(name = "IDREF")
|
266
|
@ManyToMany(fetch = FetchType.LAZY)
|
267
|
@JoinTable(
|
268
|
name="TaxonName_TypeDesignationBase",
|
269
|
joinColumns=@javax.persistence.JoinColumn(name="TaxonName_id"),
|
270
|
inverseJoinColumns=@javax.persistence.JoinColumn(name="typedesignations_id")
|
271
|
)
|
272
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
273
|
@NotNull
|
274
|
private Set<TypeDesignationBase> typeDesignations = new HashSet<>();
|
275
|
|
276
|
@XmlElement(name = "HomotypicalGroup")
|
277
|
@XmlIDREF
|
278
|
@XmlSchemaType(name = "IDREF")
|
279
|
@ManyToOne(fetch = FetchType.LAZY)
|
280
|
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
|
281
|
@Match(MatchMode.IGNORE)
|
282
|
@CacheUpdate(noUpdate ="titleCache")
|
283
|
//TODO Val #3379
|
284
|
// @NotNull
|
285
|
private HomotypicalGroup homotypicalGroup;
|
286
|
|
287
|
@XmlElementWrapper(name = "RelationsFromThisName")
|
288
|
@XmlElement(name = "RelationFromThisName")
|
289
|
@OneToMany(mappedBy="relatedFrom", fetch= FetchType.LAZY, orphanRemoval=true)
|
290
|
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
|
291
|
@Merge(MergeMode.RELATION)
|
292
|
@NotNull
|
293
|
@Valid
|
294
|
private Set<NameRelationship> relationsFromThisName = new HashSet<>();
|
295
|
|
296
|
@XmlElementWrapper(name = "RelationsToThisName")
|
297
|
@XmlElement(name = "RelationToThisName")
|
298
|
@XmlIDREF
|
299
|
@XmlSchemaType(name = "IDREF")
|
300
|
@OneToMany(mappedBy="relatedTo", fetch= FetchType.LAZY, orphanRemoval=true)
|
301
|
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
|
302
|
@Merge(MergeMode.RELATION)
|
303
|
@NotNull
|
304
|
@Valid
|
305
|
private Set<NameRelationship> relationsToThisName = new HashSet<>();
|
306
|
|
307
|
@XmlElementWrapper(name = "NomenclaturalStatuses")
|
308
|
@XmlElement(name = "NomenclaturalStatus")
|
309
|
@OneToMany(fetch= FetchType.LAZY, orphanRemoval=true)
|
310
|
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE,CascadeType.DELETE})
|
311
|
@NotNull
|
312
|
@IndexedEmbedded(depth=1)
|
313
|
private Set<NomenclaturalStatus> status = new HashSet<>();
|
314
|
|
315
|
@XmlElementWrapper(name = "TaxonBases")
|
316
|
@XmlElement(name = "TaxonBase")
|
317
|
@XmlIDREF
|
318
|
@XmlSchemaType(name = "IDREF")
|
319
|
@OneToMany(mappedBy="name", fetch= FetchType.LAZY)
|
320
|
@NotNull
|
321
|
@IndexedEmbedded(depth=1)
|
322
|
private Set<TaxonBase> taxonBases = new HashSet<>();
|
323
|
|
324
|
@XmlElement(name = "Rank")
|
325
|
@XmlIDREF
|
326
|
@XmlSchemaType(name = "IDREF")
|
327
|
@ManyToOne(fetch = FetchType.EAGER)
|
328
|
@CacheUpdate(value ="nameCache")
|
329
|
//TODO Val #3379, handle maybe as groups = Level2.class ??
|
330
|
// @NotNull
|
331
|
@IndexedEmbedded(depth=1)
|
332
|
private Rank rank;
|
333
|
|
334
|
//#6581
|
335
|
@XmlElement(name = "NomenclaturalReference")
|
336
|
@XmlIDREF
|
337
|
@XmlSchemaType(name = "IDREF")
|
338
|
@ManyToOne(fetch = FetchType.LAZY)
|
339
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
340
|
@CacheUpdate(noUpdate ="titleCache")
|
341
|
@IndexedEmbedded
|
342
|
private Reference nomenclaturalReference;
|
343
|
|
344
|
//#6581
|
345
|
@XmlElement(name = "NomenclaturalSource")
|
346
|
@XmlIDREF
|
347
|
@XmlSchemaType(name = "IDREF")
|
348
|
@ManyToOne(fetch = FetchType.LAZY)
|
349
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
350
|
@CacheUpdate(noUpdate ="titleCache")
|
351
|
@IndexedEmbedded
|
352
|
private DescriptionElementSource nomenclaturalSource;
|
353
|
|
354
|
|
355
|
|
356
|
@XmlElementWrapper(name = "Registrations")
|
357
|
@XmlElement(name = "Registration")
|
358
|
@XmlIDREF
|
359
|
@XmlSchemaType(name = "IDREF")
|
360
|
@OneToMany(mappedBy="name", fetch= FetchType.LAZY)
|
361
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
362
|
@NotNull
|
363
|
@IndexedEmbedded(depth=1)
|
364
|
private Set<Registration> registrations = new HashSet<>();
|
365
|
|
366
|
//****** Non-ViralName attributes ***************************************/
|
367
|
|
368
|
@XmlElement(name = "NameCache")
|
369
|
@Fields({
|
370
|
@Field(name = "nameCache_tokenized"),
|
371
|
@Field(store = Store.YES, index = Index.YES, analyze = Analyze.YES)
|
372
|
})
|
373
|
@Analyzer(impl = org.apache.lucene.analysis.core.KeywordAnalyzer.class)
|
374
|
@Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.DEFINED,
|
375
|
cacheReplacedProperties={"genusOrUninomial", "infraGenericEpithet", "specificEpithet", "infraSpecificEpithet"} )
|
376
|
@NotEmpty(groups = Level2.class) // implicitly NotNull
|
377
|
@Column(length=255)
|
378
|
private String nameCache;
|
379
|
|
380
|
@XmlElement(name = "ProtectedNameCache")
|
381
|
@CacheUpdate(value="nameCache")
|
382
|
protected boolean protectedNameCache;
|
383
|
|
384
|
@XmlElement(name = "GenusOrUninomial")
|
385
|
@Field(analyze = Analyze.YES, indexNullAs=Field.DEFAULT_NULL_TOKEN)
|
386
|
@Match(MatchMode.EQUAL_REQUIRED)
|
387
|
@CacheUpdate("nameCache")
|
388
|
@Column(length=255)
|
389
|
@Pattern(regexp = "[A-Z][a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class, message="{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForUninomial.message}")
|
390
|
@NullOrNotEmpty
|
391
|
@NotNull(groups = Level2.class)
|
392
|
private String genusOrUninomial;
|
393
|
|
394
|
@XmlElement(name = "InfraGenericEpithet")
|
395
|
@Field(analyze = Analyze.YES,indexNullAs=Field.DEFAULT_NULL_TOKEN)
|
396
|
@CacheUpdate("nameCache")
|
397
|
//TODO Val #3379
|
398
|
// @NullOrNotEmpty
|
399
|
@Column(length=255)
|
400
|
@Pattern(regexp = "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class,message="{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}")
|
401
|
private String infraGenericEpithet;
|
402
|
|
403
|
@XmlElement(name = "SpecificEpithet")
|
404
|
@Field(analyze = Analyze.YES,indexNullAs=Field.DEFAULT_NULL_TOKEN)
|
405
|
@CacheUpdate("nameCache")
|
406
|
//TODO Val #3379
|
407
|
// @NullOrNotEmpty
|
408
|
@Column(length=255)
|
409
|
@Pattern(regexp = "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class, message = "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}")
|
410
|
private String specificEpithet;
|
411
|
|
412
|
@XmlElement(name = "InfraSpecificEpithet")
|
413
|
@Field(analyze = Analyze.YES,indexNullAs=Field.DEFAULT_NULL_TOKEN)
|
414
|
@CacheUpdate("nameCache")
|
415
|
//TODO Val #3379
|
416
|
// @NullOrNotEmpty
|
417
|
@Column(length=255)
|
418
|
@Pattern(regexp = "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class, message = "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}")
|
419
|
private String infraSpecificEpithet;
|
420
|
|
421
|
@XmlElement(name = "CombinationAuthorship")
|
422
|
@XmlIDREF
|
423
|
@XmlSchemaType(name = "IDREF")
|
424
|
@ManyToOne(fetch = FetchType.LAZY)
|
425
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
426
|
@CacheUpdate("authorshipCache")
|
427
|
@IndexedEmbedded
|
428
|
private TeamOrPersonBase<?> combinationAuthorship;
|
429
|
|
430
|
@XmlElement(name = "ExCombinationAuthorship")
|
431
|
@XmlIDREF
|
432
|
@XmlSchemaType(name = "IDREF")
|
433
|
@ManyToOne(fetch = FetchType.LAZY)
|
434
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
435
|
@CacheUpdate("authorshipCache")
|
436
|
@IndexedEmbedded
|
437
|
private TeamOrPersonBase<?> exCombinationAuthorship;
|
438
|
|
439
|
//#6943
|
440
|
@XmlElement(name = "InCombinationAuthorship")
|
441
|
@XmlIDREF
|
442
|
@XmlSchemaType(name = "IDREF")
|
443
|
@ManyToOne(fetch = FetchType.LAZY)
|
444
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
445
|
@CacheUpdate("authorshipCache")
|
446
|
@IndexedEmbedded
|
447
|
private TeamOrPersonBase<?> inCombinationAuthorship;
|
448
|
|
449
|
@XmlElement(name = "BasionymAuthorship")
|
450
|
@XmlIDREF
|
451
|
@XmlSchemaType(name = "IDREF")
|
452
|
@ManyToOne(fetch = FetchType.LAZY)
|
453
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
454
|
@CacheUpdate("authorshipCache")
|
455
|
@IndexedEmbedded
|
456
|
private TeamOrPersonBase<?> basionymAuthorship;
|
457
|
|
458
|
@XmlElement(name = "ExBasionymAuthorship")
|
459
|
@XmlIDREF
|
460
|
@XmlSchemaType(name = "IDREF")
|
461
|
@ManyToOne(fetch = FetchType.LAZY)
|
462
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
463
|
@CacheUpdate("authorshipCache")
|
464
|
@IndexedEmbedded
|
465
|
private TeamOrPersonBase<?> exBasionymAuthorship;
|
466
|
|
467
|
//#6943
|
468
|
@XmlElement(name = "InBasionymAuthorship")
|
469
|
@XmlIDREF
|
470
|
@XmlSchemaType(name = "IDREF")
|
471
|
@ManyToOne(fetch = FetchType.LAZY)
|
472
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
473
|
@CacheUpdate("authorshipCache")
|
474
|
@IndexedEmbedded
|
475
|
private TeamOrPersonBase<?> inBasionymAuthorship;
|
476
|
|
477
|
@XmlElement(name = "AuthorshipCache")
|
478
|
@Fields({
|
479
|
@Field(name = "authorshipCache_tokenized"),
|
480
|
@Field(analyze = Analyze.NO)
|
481
|
})
|
482
|
@Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.DEFINED,
|
483
|
cacheReplacedProperties={"combinationAuthorship", "basionymAuthorship", "exCombinationAuthorship", "exBasionymAuthorship"} )
|
484
|
//TODO Val #3379
|
485
|
// @NotNull
|
486
|
@Column(length=255)
|
487
|
@Pattern(regexp = "^[A-Za-z0-9 \\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-\\&\\,\\(\\)\\.]+$", groups=Level2.class, message = "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForAuthority.message}")
|
488
|
private String authorshipCache;
|
489
|
|
490
|
@XmlElement(name = "ProtectedAuthorshipCache")
|
491
|
@CacheUpdate("authorshipCache")
|
492
|
protected boolean protectedAuthorshipCache;
|
493
|
|
494
|
@XmlElementWrapper(name = "HybridRelationsFromThisName")
|
495
|
@XmlElement(name = "HybridRelationsFromThisName")
|
496
|
@OneToMany(mappedBy="relatedFrom", fetch = FetchType.LAZY)
|
497
|
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
|
498
|
@Merge(MergeMode.RELATION)
|
499
|
@NotNull
|
500
|
private Set<HybridRelationship> hybridParentRelations = new HashSet<>();
|
501
|
|
502
|
@XmlElementWrapper(name = "HybridRelationsToThisName")
|
503
|
@XmlElement(name = "HybridRelationsToThisName")
|
504
|
@OneToMany(mappedBy="relatedTo", fetch = FetchType.LAZY, orphanRemoval=true) //a hybrid relation can be deleted automatically if the child is deleted.
|
505
|
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
|
506
|
@Merge(MergeMode.RELATION)
|
507
|
@NotNull
|
508
|
private Set<HybridRelationship> hybridChildRelations = new HashSet<>();
|
509
|
|
510
|
|
511
|
//if set: this name is a hybrid formula (a hybrid that does not have an own name) and no
|
512
|
//other hybrid flags may be set. A
|
513
|
//hybrid name may not have either an authorteam nor other name components.
|
514
|
@XmlElement(name ="IsHybridFormula")
|
515
|
@CacheUpdate("nameCache")
|
516
|
private boolean hybridFormula = false;
|
517
|
|
518
|
@XmlElement(name ="IsMonomHybrid")
|
519
|
@CacheUpdate("nameCache")
|
520
|
private boolean monomHybrid = false;
|
521
|
|
522
|
@XmlElement(name ="IsBinomHybrid")
|
523
|
@CacheUpdate("nameCache")
|
524
|
private boolean binomHybrid = false;
|
525
|
|
526
|
@XmlElement(name ="IsTrinomHybrid")
|
527
|
@CacheUpdate("nameCache")
|
528
|
private boolean trinomHybrid = false;
|
529
|
|
530
|
// ViralName attributes ************************* /
|
531
|
|
532
|
@XmlElement(name = "Acronym")
|
533
|
@Field
|
534
|
//TODO Val #3379
|
535
|
// @NullOrNotEmpty
|
536
|
@Column(length=255)
|
537
|
private String acronym;
|
538
|
|
539
|
// BacterialName attributes ***********************/
|
540
|
|
541
|
//Author team and year of the subgenus name
|
542
|
@XmlElement(name = "SubGenusAuthorship")
|
543
|
@Field
|
544
|
private String subGenusAuthorship;
|
545
|
|
546
|
//Approbation of name according to approved list, validation list, or validly published, paper in IJSB after 1980
|
547
|
@XmlElement(name = "NameApprobation")
|
548
|
@Field
|
549
|
private String nameApprobation;
|
550
|
|
551
|
//ZOOLOGICAL NAME
|
552
|
|
553
|
//Name of the breed of an animal
|
554
|
@XmlElement(name = "Breed")
|
555
|
@Field
|
556
|
@NullOrNotEmpty
|
557
|
@Column(length=255)
|
558
|
private String breed;
|
559
|
|
560
|
@XmlElement(name = "PublicationYear")
|
561
|
@Field(analyze = Analyze.NO)
|
562
|
@CacheUpdate(value ="authorshipCache")
|
563
|
@Min(0)
|
564
|
private Integer publicationYear;
|
565
|
|
566
|
@XmlElement(name = "OriginalPublicationYear")
|
567
|
@Field(analyze = Analyze.NO)
|
568
|
@CacheUpdate(value ="authorshipCache")
|
569
|
@Min(0)
|
570
|
private Integer originalPublicationYear;
|
571
|
|
572
|
//Cultivar attribute(s)
|
573
|
|
574
|
//the characteristical name of the cultivar
|
575
|
@XmlElement(name = "CultivarName")
|
576
|
//TODO Val #3379
|
577
|
//@NullOrNotEmpty
|
578
|
@Column(length=255)
|
579
|
private String cultivarName;
|
580
|
|
581
|
// ************** FUNGUS name attributes
|
582
|
//to indicate that the type of the name is asexual or not
|
583
|
@XmlElement(name ="IsAnamorphic")
|
584
|
private boolean anamorphic = false;
|
585
|
|
586
|
// *************** FACTORY METHODS ********************************/
|
587
|
|
588
|
//see TaxonNameFactory
|
589
|
/**
|
590
|
* @param code
|
591
|
* @param rank
|
592
|
* @param homotypicalGroup
|
593
|
* @return
|
594
|
*/
|
595
|
protected static TaxonName NewInstance(NomenclaturalCode code, Rank rank,
|
596
|
HomotypicalGroup homotypicalGroup) {
|
597
|
TaxonName result = new TaxonName(code, rank, homotypicalGroup);
|
598
|
return result;
|
599
|
}
|
600
|
|
601
|
|
602
|
/**
|
603
|
* @param icnafp
|
604
|
* @param rank2
|
605
|
* @param genusOrUninomial2
|
606
|
* @param infraGenericEpithet2
|
607
|
* @param specificEpithet2
|
608
|
* @param infraSpecificEpithet2
|
609
|
* @param combinationAuthorship2
|
610
|
* @param nomenclaturalReference2
|
611
|
* @param nomenclMicroRef
|
612
|
* @param homotypicalGroup2
|
613
|
* @return
|
614
|
*/
|
615
|
public static TaxonName NewInstance(NomenclaturalCode code, Rank rank, String genusOrUninomial,
|
616
|
String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet,
|
617
|
TeamOrPersonBase combinationAuthorship, Reference nomenclaturalReference,
|
618
|
String nomenclMicroRef, HomotypicalGroup homotypicalGroup) {
|
619
|
TaxonName result = new TaxonName(code, rank, genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet, combinationAuthorship, nomenclaturalReference, nomenclMicroRef, homotypicalGroup);
|
620
|
return result;
|
621
|
}
|
622
|
|
623
|
|
624
|
// ************* CONSTRUCTORS *************/
|
625
|
/**
|
626
|
* Class constructor: creates a new empty taxon name.
|
627
|
* @param code
|
628
|
*
|
629
|
* @see #TaxonName(Rank)
|
630
|
* @see #TaxonName(HomotypicalGroup)
|
631
|
* @see #TaxonName(Rank, HomotypicalGroup)
|
632
|
*/
|
633
|
protected TaxonName() {
|
634
|
super();
|
635
|
rectifyNameCacheStrategy();
|
636
|
}
|
637
|
|
638
|
|
639
|
/**
|
640
|
* Class constructor: creates a new taxon name instance
|
641
|
* only containing its {@link Rank rank} and
|
642
|
* its {@link HomotypicalGroup homotypical group} and
|
643
|
* the {@link eu.etaxonomy.cdm.strategy.cache.name.TaxonNameDefaultCacheStrategy default cache strategy}.
|
644
|
* The new taxon name will be also added to the set of taxon names
|
645
|
* belonging to this homotypical group.
|
646
|
*
|
647
|
* @param rank the rank to be assigned to <i>this</i> taxon name
|
648
|
* @param homotypicalGroup the homotypical group to which <i>this</i> taxon name belongs
|
649
|
* @see #TaxonName()
|
650
|
* @see #TaxonName(Rank)
|
651
|
* @see #TaxonName(HomotypicalGroup)
|
652
|
*/
|
653
|
protected TaxonName(NomenclaturalCode type, Rank rank, HomotypicalGroup homotypicalGroup) {
|
654
|
this();
|
655
|
setNameType(type);
|
656
|
this.setRank(rank);
|
657
|
if (homotypicalGroup == null){
|
658
|
homotypicalGroup = HomotypicalGroup.NewInstance();
|
659
|
}
|
660
|
homotypicalGroup.addTypifiedName(this);
|
661
|
this.homotypicalGroup = homotypicalGroup;
|
662
|
}
|
663
|
|
664
|
|
665
|
/**
|
666
|
* Class constructor: creates a new non viral taxon name instance
|
667
|
* containing its {@link Rank rank},
|
668
|
* its {@link HomotypicalGroup homotypical group},
|
669
|
* its scientific name components, its {@link eu.etaxonomy.cdm.model.agent.TeamOrPersonBase author(team)},
|
670
|
* its {@link eu.etaxonomy.cdm.model.reference.Reference nomenclatural reference} and
|
671
|
* the {@link eu.etaxonomy.cdm.strategy.cache.name.TaxonNameDefaultCacheStrategy default cache strategy}.
|
672
|
* The new non viral taxon name instance will be also added to the set of
|
673
|
* non viral taxon names belonging to this homotypical group.
|
674
|
*
|
675
|
* @param rank the rank to be assigned to <i>this</i> non viral taxon name
|
676
|
* @param genusOrUninomial the string for <i>this</i> non viral taxon name
|
677
|
* if its rank is genus or higher or for the genus part
|
678
|
* if its rank is lower than genus
|
679
|
* @param infraGenericEpithet the string for the first epithet of
|
680
|
* <i>this</i> non viral taxon name if its rank is lower than genus
|
681
|
* and higher than species aggregate
|
682
|
* @param specificEpithet the string for the first epithet of
|
683
|
* <i>this</i> non viral taxon name if its rank is species aggregate or lower
|
684
|
* @param infraSpecificEpithet the string for the second epithet of
|
685
|
* <i>this</i> non viral taxon name if its rank is lower than species
|
686
|
* @param combinationAuthorship the author or the team who published <i>this</i> non viral taxon name
|
687
|
* @param nomenclaturalReference the nomenclatural reference where <i>this</i> non viral taxon name was published
|
688
|
* @param nomenclMicroRef the string with the details for precise location within the nomenclatural reference
|
689
|
* @param homotypicalGroup the homotypical group to which <i>this</i> non viral taxon name belongs
|
690
|
* @see #NewInstance(NomenclaturalCode, Rank, HomotypicalGroup)
|
691
|
* @see #NewInstance(NomenclaturalCode, Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup)
|
692
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy
|
693
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy
|
694
|
* @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
|
695
|
*/
|
696
|
protected TaxonName(NomenclaturalCode type, Rank rank, String genusOrUninomial,
|
697
|
String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet,
|
698
|
TeamOrPersonBase combinationAuthorship, Reference nomenclaturalReference,
|
699
|
String nomenclMicroRef, HomotypicalGroup homotypicalGroup) {
|
700
|
this(type, rank, homotypicalGroup);
|
701
|
setGenusOrUninomial(genusOrUninomial);
|
702
|
setInfraGenericEpithet (infraGenericEpithet);
|
703
|
setSpecificEpithet(specificEpithet);
|
704
|
setInfraSpecificEpithet(infraSpecificEpithet);
|
705
|
setCombinationAuthorship(combinationAuthorship);
|
706
|
setNomenclaturalReference(nomenclaturalReference);
|
707
|
this.setNomenclaturalMicroReference(nomenclMicroRef);
|
708
|
}
|
709
|
|
710
|
|
711
|
/**
|
712
|
* This method was originally needed to distinguish cache strategies
|
713
|
* depending on the name type. Now we have a unified cache strategy
|
714
|
* which does not require this anymore. Maybe we could even further remove this method.
|
715
|
*/
|
716
|
private void rectifyNameCacheStrategy(){
|
717
|
if (this.cacheStrategy == null){
|
718
|
this.cacheStrategy = TaxonNameDefaultCacheStrategy.NewInstance();
|
719
|
}
|
720
|
}
|
721
|
|
722
|
|
723
|
@Override
|
724
|
public void initListener(){
|
725
|
PropertyChangeListener listener = new PropertyChangeListener() {
|
726
|
@Override
|
727
|
public void propertyChange(PropertyChangeEvent e) {
|
728
|
boolean protectedByLowerCache = false;
|
729
|
//authorship cache
|
730
|
if (fieldHasCacheUpdateProperty(e.getPropertyName(), "authorshipCache")){
|
731
|
if (protectedAuthorshipCache){
|
732
|
protectedByLowerCache = true;
|
733
|
}else{
|
734
|
authorshipCache = null;
|
735
|
}
|
736
|
}
|
737
|
|
738
|
//nameCache
|
739
|
if (fieldHasCacheUpdateProperty(e.getPropertyName(), "nameCache")){
|
740
|
if (protectedNameCache){
|
741
|
protectedByLowerCache = true;
|
742
|
}else{
|
743
|
nameCache = null;
|
744
|
}
|
745
|
}
|
746
|
//title cache
|
747
|
if (! fieldHasNoUpdateProperty(e.getPropertyName(), "titleCache")){
|
748
|
if (isProtectedTitleCache()|| protectedByLowerCache == true ){
|
749
|
protectedByLowerCache = true;
|
750
|
}else{
|
751
|
titleCache = null;
|
752
|
}
|
753
|
}
|
754
|
//full title cache
|
755
|
if (! fieldHasNoUpdateProperty(e.getPropertyName(), "fullTitleCache")){
|
756
|
if (isProtectedFullTitleCache()|| protectedByLowerCache == true ){
|
757
|
protectedByLowerCache = true;
|
758
|
}else{
|
759
|
fullTitleCache = null;
|
760
|
}
|
761
|
}
|
762
|
}
|
763
|
};
|
764
|
addPropertyChangeListener(listener); //didn't use this.addXXX to make lsid.AssemblerTest run in cdmlib-remote
|
765
|
}
|
766
|
|
767
|
private static Map<String, java.lang.reflect.Field> allFields = null;
|
768
|
protected Map<String, java.lang.reflect.Field> getAllFields(){
|
769
|
if (allFields == null){
|
770
|
allFields = CdmUtils.getAllFields(this.getClass(), CdmBase.class, false, false, false, true);
|
771
|
}
|
772
|
return allFields;
|
773
|
}
|
774
|
|
775
|
/**
|
776
|
* @param propertyName
|
777
|
* @param string
|
778
|
* @return
|
779
|
*/
|
780
|
private boolean fieldHasCacheUpdateProperty(String propertyName, String cacheName) {
|
781
|
java.lang.reflect.Field field;
|
782
|
try {
|
783
|
field = getAllFields().get(propertyName);
|
784
|
if (field != null){
|
785
|
CacheUpdate updateAnnotation = field.getAnnotation(CacheUpdate.class);
|
786
|
if (updateAnnotation != null){
|
787
|
for (String value : updateAnnotation.value()){
|
788
|
if (cacheName.equals(value)){
|
789
|
return true;
|
790
|
}
|
791
|
}
|
792
|
}
|
793
|
}
|
794
|
return false;
|
795
|
} catch (SecurityException e1) {
|
796
|
throw e1;
|
797
|
}
|
798
|
}
|
799
|
|
800
|
private boolean fieldHasNoUpdateProperty(String propertyName, String cacheName) {
|
801
|
java.lang.reflect.Field field;
|
802
|
//do not update fields with the same name
|
803
|
if (cacheName.equals(propertyName)){
|
804
|
return true;
|
805
|
}
|
806
|
//evaluate annotation
|
807
|
try {
|
808
|
field = getAllFields().get(propertyName);
|
809
|
if (field != null){
|
810
|
CacheUpdate updateAnnotation = field.getAnnotation(CacheUpdate.class);
|
811
|
if (updateAnnotation != null){
|
812
|
for (String value : updateAnnotation.noUpdate()){
|
813
|
if (cacheName.equals(value)){
|
814
|
return true;
|
815
|
}
|
816
|
}
|
817
|
}
|
818
|
}
|
819
|
return false;
|
820
|
} catch (SecurityException e1) {
|
821
|
throw e1;
|
822
|
}
|
823
|
}
|
824
|
|
825
|
// ****************** GETTER / SETTER ****************************/
|
826
|
|
827
|
@Override
|
828
|
public NomenclaturalCode getNameType() {
|
829
|
return nameType;
|
830
|
}
|
831
|
|
832
|
@Override
|
833
|
public void setNameType(NomenclaturalCode nameType) {
|
834
|
this.nameType = nameType;
|
835
|
}
|
836
|
|
837
|
/**
|
838
|
* Returns the boolean value of the flag intended to protect (true)
|
839
|
* or not (false) the {@link #getNameCache() nameCache} (scientific name without author strings and year)
|
840
|
* string of <i>this</i> non viral taxon name.
|
841
|
*
|
842
|
* @return the boolean value of the protectedNameCache flag
|
843
|
* @see #getNameCache()
|
844
|
*/
|
845
|
@Override
|
846
|
public boolean isProtectedNameCache() {
|
847
|
return protectedNameCache;
|
848
|
}
|
849
|
|
850
|
/**
|
851
|
* @see #isProtectedNameCache()
|
852
|
*/
|
853
|
@Override
|
854
|
public void setProtectedNameCache(boolean protectedNameCache) {
|
855
|
this.protectedNameCache = protectedNameCache;
|
856
|
}
|
857
|
|
858
|
/**
|
859
|
* Returns either the scientific name string (without authorship) for <i>this</i>
|
860
|
* non viral taxon name if its rank is genus or higher (monomial) or the string for
|
861
|
* the genus part of it if its {@link Rank rank} is lower than genus (bi- or trinomial).
|
862
|
* Genus or uninomial strings begin with an upper case letter.
|
863
|
*
|
864
|
* @return the string containing the suprageneric name, the genus name or the genus part of <i>this</i> non viral taxon name
|
865
|
* @see #getNameCache()
|
866
|
*/
|
867
|
@Override
|
868
|
public String getGenusOrUninomial() {
|
869
|
return genusOrUninomial;
|
870
|
}
|
871
|
|
872
|
/**
|
873
|
* @see #getGenusOrUninomial()
|
874
|
*/
|
875
|
@Override
|
876
|
public void setGenusOrUninomial(String genusOrUninomial) {
|
877
|
this.genusOrUninomial = StringUtils.isBlank(genusOrUninomial) ? null : genusOrUninomial;
|
878
|
}
|
879
|
|
880
|
/**
|
881
|
* Returns the genus subdivision epithet string (infrageneric part) for
|
882
|
* <i>this</i> non viral taxon name if its {@link Rank rank} is infrageneric (lower than genus and
|
883
|
* higher than species aggregate: binomial). Genus subdivision epithet
|
884
|
* strings begin with an upper case letter.
|
885
|
*
|
886
|
* @return the string containing the infrageneric part of <i>this</i> non viral taxon name
|
887
|
* @see #getNameCache()
|
888
|
*/
|
889
|
@Override
|
890
|
public String getInfraGenericEpithet(){
|
891
|
return this.infraGenericEpithet;
|
892
|
}
|
893
|
|
894
|
/**
|
895
|
* @see #getInfraGenericEpithet()
|
896
|
*/
|
897
|
@Override
|
898
|
public void setInfraGenericEpithet(String infraGenericEpithet){
|
899
|
this.infraGenericEpithet = StringUtils.isBlank(infraGenericEpithet)? null : infraGenericEpithet;
|
900
|
}
|
901
|
|
902
|
/**
|
903
|
* Returns the species epithet string for <i>this</i> non viral taxon name if its {@link Rank rank} is
|
904
|
* species aggregate or lower (bi- or trinomial). Species epithet strings
|
905
|
* begin with a lower case letter.
|
906
|
*
|
907
|
* @return the string containing the species epithet of <i>this</i> non viral taxon name
|
908
|
* @see #getNameCache()
|
909
|
*/
|
910
|
@Override
|
911
|
public String getSpecificEpithet(){
|
912
|
return this.specificEpithet;
|
913
|
}
|
914
|
|
915
|
/**
|
916
|
* @see #getSpecificEpithet()
|
917
|
*/
|
918
|
@Override
|
919
|
public void setSpecificEpithet(String specificEpithet){
|
920
|
this.specificEpithet = StringUtils.isBlank(specificEpithet) ? null : specificEpithet;
|
921
|
}
|
922
|
|
923
|
/**
|
924
|
* Returns the species subdivision epithet string (infraspecific part) for
|
925
|
* <i>this</i> non viral taxon name if its {@link Rank rank} is infraspecific
|
926
|
* (lower than species: trinomial). Species subdivision epithet strings
|
927
|
* begin with a lower case letter.
|
928
|
*
|
929
|
* @return the string containing the infraspecific part of <i>this</i> non viral taxon name
|
930
|
* @see #getNameCache()
|
931
|
*/
|
932
|
@Override
|
933
|
public String getInfraSpecificEpithet(){
|
934
|
return this.infraSpecificEpithet;
|
935
|
}
|
936
|
|
937
|
/**
|
938
|
* @see #getInfraSpecificEpithet()
|
939
|
*/
|
940
|
@Override
|
941
|
public void setInfraSpecificEpithet(String infraSpecificEpithet){
|
942
|
this.infraSpecificEpithet = StringUtils.isBlank(infraSpecificEpithet)?null : infraSpecificEpithet;
|
943
|
}
|
944
|
|
945
|
/**
|
946
|
* Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that published <i>this</i> non viral
|
947
|
* taxon name.
|
948
|
*
|
949
|
* @return the nomenclatural author (team) of <i>this</i> non viral taxon name
|
950
|
* @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
|
951
|
* @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
|
952
|
*/
|
953
|
@Override
|
954
|
public TeamOrPersonBase<?> getCombinationAuthorship(){
|
955
|
return this.combinationAuthorship;
|
956
|
}
|
957
|
|
958
|
/**
|
959
|
* @see #getCombinationAuthorship()
|
960
|
*/
|
961
|
@Override
|
962
|
public void setCombinationAuthorship(TeamOrPersonBase<?> combinationAuthorship){
|
963
|
this.combinationAuthorship = combinationAuthorship;
|
964
|
}
|
965
|
|
966
|
/**
|
967
|
* Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that contributed to
|
968
|
* the publication of <i>this</i> non viral taxon name as generally stated by
|
969
|
* the {@link #getCombinationAuthorship() combination author (team)} itself.<BR>
|
970
|
* An ex-author(-team) is an author(-team) to whom a taxon name was ascribed
|
971
|
* although it is not the author(-team) of a valid publication (for instance
|
972
|
* without the validating description or diagnosis in case of a name for a
|
973
|
* new taxon). The name of this ascribed authorship, followed by "ex", may
|
974
|
* be inserted before the name(s) of the publishing author(s) of the validly
|
975
|
* published name:<BR>
|
976
|
* <i>Lilium tianschanicum</i> was described by Grubov (1977) as a new species and
|
977
|
* its name was ascribed to Ivanova; since there is no indication that
|
978
|
* Ivanova provided the validating description, the name may be cited as
|
979
|
* <i>Lilium tianschanicum</i> N. A. Ivanova ex Grubov or <i>Lilium tianschanicum</i> Grubov.
|
980
|
* <P>
|
981
|
* The presence of an author (team) of <i>this</i> non viral taxon name is a
|
982
|
* condition for the existence of an ex author (team) for <i>this</i> same name.
|
983
|
*
|
984
|
* @return the nomenclatural ex author (team) of <i>this</i> non viral taxon name
|
985
|
* @see #getCombinationAuthorship()
|
986
|
* @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
|
987
|
* @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
|
988
|
*/
|
989
|
@Override
|
990
|
public TeamOrPersonBase<?> getExCombinationAuthorship(){
|
991
|
return this.exCombinationAuthorship;
|
992
|
}
|
993
|
/**
|
994
|
* @see #getExCombinationAuthorship()
|
995
|
*/
|
996
|
@Override
|
997
|
public void setExCombinationAuthorship(TeamOrPersonBase<?> exCombinationAuthorship){
|
998
|
this.exCombinationAuthorship = exCombinationAuthorship;
|
999
|
}
|
1000
|
|
1001
|
@Override
|
1002
|
public TeamOrPersonBase<?> getInCombinationAuthorship(){
|
1003
|
return this.inCombinationAuthorship;
|
1004
|
}
|
1005
|
@Override
|
1006
|
public void setInCombinationAuthorship(TeamOrPersonBase<?> inCombinationAuthorship) {
|
1007
|
this.inCombinationAuthorship = inCombinationAuthorship;
|
1008
|
}
|
1009
|
|
1010
|
/**
|
1011
|
* Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that published the original combination
|
1012
|
* on which <i>this</i> non viral taxon name is nomenclaturally based. Such an
|
1013
|
* author (team) can only exist if <i>this</i> non viral taxon name is a new
|
1014
|
* combination due to a taxonomical revision.
|
1015
|
*
|
1016
|
* @return the nomenclatural basionym author (team) of <i>this</i> non viral taxon name
|
1017
|
* @see #getCombinationAuthorship()
|
1018
|
* @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
|
1019
|
* @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
|
1020
|
*/
|
1021
|
@Override
|
1022
|
public TeamOrPersonBase<?> getBasionymAuthorship(){
|
1023
|
return basionymAuthorship;
|
1024
|
}
|
1025
|
|
1026
|
/**
|
1027
|
* @see #getBasionymAuthorship()
|
1028
|
*/
|
1029
|
@Override
|
1030
|
public void setBasionymAuthorship(TeamOrPersonBase<?> basionymAuthorship) {
|
1031
|
this.basionymAuthorship = basionymAuthorship;
|
1032
|
}
|
1033
|
|
1034
|
/**
|
1035
|
* Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that contributed to
|
1036
|
* the publication of the original combination <i>this</i> non viral taxon name is
|
1037
|
* based on. This should have been generally stated by
|
1038
|
* the {@link #getBasionymAuthorship() basionym author (team)} itself.
|
1039
|
* The presence of a basionym author (team) of <i>this</i> non viral taxon name is a
|
1040
|
* condition for the existence of an ex basionym author (team)
|
1041
|
* for <i>this</i> same name.
|
1042
|
*
|
1043
|
* @return the nomenclatural ex basionym author (team) of <i>this</i> non viral taxon name
|
1044
|
* @see #getBasionymAuthorship()
|
1045
|
* @see #getExCombinationAuthorship()
|
1046
|
* @see #getCombinationAuthorship()
|
1047
|
* @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
|
1048
|
* @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
|
1049
|
*/
|
1050
|
@Override
|
1051
|
public TeamOrPersonBase<?> getExBasionymAuthorship(){
|
1052
|
return exBasionymAuthorship;
|
1053
|
}
|
1054
|
|
1055
|
/**
|
1056
|
* @see #getExBasionymAuthorship()
|
1057
|
*/
|
1058
|
@Override
|
1059
|
public void setExBasionymAuthorship(TeamOrPersonBase<?> exBasionymAuthorship) {
|
1060
|
this.exBasionymAuthorship = exBasionymAuthorship;
|
1061
|
}
|
1062
|
|
1063
|
@Override
|
1064
|
public TeamOrPersonBase<?> getInBasionymAuthorship(){
|
1065
|
return this.inBasionymAuthorship;
|
1066
|
}
|
1067
|
@Override
|
1068
|
public void setInBasionymAuthorship(TeamOrPersonBase<?> inBasionymAuthorship) {
|
1069
|
this.inBasionymAuthorship = inBasionymAuthorship;
|
1070
|
}
|
1071
|
|
1072
|
/**
|
1073
|
* Returns the boolean value of the flag intended to protect (true)
|
1074
|
* or not (false) the {@link #getAuthorshipCache() authorshipCache} (complete authorship string)
|
1075
|
* of <i>this</i> non viral taxon name.
|
1076
|
*
|
1077
|
* @return the boolean value of the protectedAuthorshipCache flag
|
1078
|
* @see #getAuthorshipCache()
|
1079
|
*/
|
1080
|
@Override
|
1081
|
public boolean isProtectedAuthorshipCache() {
|
1082
|
return protectedAuthorshipCache;
|
1083
|
}
|
1084
|
|
1085
|
/**
|
1086
|
* @see #isProtectedAuthorshipCache()
|
1087
|
* @see #getAuthorshipCache()
|
1088
|
*/
|
1089
|
@Override
|
1090
|
public void setProtectedAuthorshipCache(boolean protectedAuthorshipCache) {
|
1091
|
this.protectedAuthorshipCache = protectedAuthorshipCache;
|
1092
|
}
|
1093
|
|
1094
|
/**
|
1095
|
* Returns the set of all {@link HybridRelationship hybrid relationships}
|
1096
|
* in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedFrom() parent}.
|
1097
|
*
|
1098
|
* @see #getHybridRelationships()
|
1099
|
* @see #getChildRelationships()
|
1100
|
* @see HybridRelationshipType
|
1101
|
*/
|
1102
|
@Override
|
1103
|
public Set<HybridRelationship> getHybridParentRelations() {
|
1104
|
if(hybridParentRelations == null) {
|
1105
|
this.hybridParentRelations = new HashSet<>();
|
1106
|
}
|
1107
|
return hybridParentRelations;
|
1108
|
}
|
1109
|
|
1110
|
private void setHybridParentRelations(Set<HybridRelationship> hybridParentRelations) {
|
1111
|
this.hybridParentRelations = hybridParentRelations;
|
1112
|
}
|
1113
|
|
1114
|
|
1115
|
/**
|
1116
|
* Returns the set of all {@link HybridRelationship hybrid relationships}
|
1117
|
* in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedTo() child}.
|
1118
|
*
|
1119
|
* @see #getHybridRelationships()
|
1120
|
* @see #getParentRelationships()
|
1121
|
* @see HybridRelationshipType
|
1122
|
*/
|
1123
|
@Override
|
1124
|
public Set<HybridRelationship> getHybridChildRelations() {
|
1125
|
if(hybridChildRelations == null) {
|
1126
|
this.hybridChildRelations = new HashSet<>();
|
1127
|
}
|
1128
|
return hybridChildRelations;
|
1129
|
}
|
1130
|
|
1131
|
private void setHybridChildRelations(Set<HybridRelationship> hybridChildRelations) {
|
1132
|
this.hybridChildRelations = hybridChildRelations;
|
1133
|
}
|
1134
|
|
1135
|
@Override
|
1136
|
public boolean isProtectedFullTitleCache() {
|
1137
|
return protectedFullTitleCache;
|
1138
|
}
|
1139
|
|
1140
|
@Override
|
1141
|
public void setProtectedFullTitleCache(boolean protectedFullTitleCache) {
|
1142
|
this.protectedFullTitleCache = protectedFullTitleCache;
|
1143
|
}
|
1144
|
|
1145
|
/**
|
1146
|
* Returns the boolean value of the flag indicating whether the name of <i>this</i>
|
1147
|
* botanical taxon name is a hybrid formula (true) or not (false). A hybrid
|
1148
|
* named by a hybrid formula (composed with its parent names by placing the
|
1149
|
* multiplication sign between them) does not have an own published name
|
1150
|
* and therefore has neither an {@link NonViralName#getAuthorshipCache() autorship}
|
1151
|
* nor other name components. If this flag is set no other hybrid flags may
|
1152
|
* be set.
|
1153
|
*
|
1154
|
* @return the boolean value of the isHybridFormula flag
|
1155
|
* @see #isMonomHybrid()
|
1156
|
* @see #isBinomHybrid()
|
1157
|
* @see #isTrinomHybrid()
|
1158
|
*/
|
1159
|
@Override
|
1160
|
@Transient
|
1161
|
@java.beans.Transient
|
1162
|
public boolean isHybridFormula(){
|
1163
|
return this.hybridFormula;
|
1164
|
}
|
1165
|
|
1166
|
/**
|
1167
|
* @see #isHybridFormula()
|
1168
|
*/
|
1169
|
@Override
|
1170
|
public void setHybridFormula(boolean hybridFormula){
|
1171
|
this.hybridFormula = hybridFormula;
|
1172
|
}
|
1173
|
|
1174
|
/**
|
1175
|
* Returns the boolean value of the flag indicating whether <i>this</i> botanical
|
1176
|
* taxon name is the name of an intergeneric hybrid (true) or not (false).
|
1177
|
* In this case the multiplication sign is placed before the scientific
|
1178
|
* name. If this flag is set no other hybrid flags may be set.
|
1179
|
*
|
1180
|
* @return the boolean value of the isMonomHybrid flag
|
1181
|
* @see #isHybridFormula()
|
1182
|
* @see #isBinomHybrid()
|
1183
|
* @see #isTrinomHybrid()
|
1184
|
*/
|
1185
|
@Override
|
1186
|
public boolean isMonomHybrid(){
|
1187
|
return this.monomHybrid;
|
1188
|
}
|
1189
|
|
1190
|
/**
|
1191
|
* @see #isMonomHybrid()
|
1192
|
* @see #isBinomHybrid()
|
1193
|
* @see #isTrinomHybrid()
|
1194
|
*/
|
1195
|
@Override
|
1196
|
public void setMonomHybrid(boolean monomHybrid){
|
1197
|
this.monomHybrid = monomHybrid;
|
1198
|
}
|
1199
|
|
1200
|
/**
|
1201
|
* Returns the boolean value of the flag indicating whether <i>this</i> botanical
|
1202
|
* taxon name is the name of an interspecific hybrid (true) or not (false).
|
1203
|
* In this case the multiplication sign is placed before the species
|
1204
|
* epithet. If this flag is set no other hybrid flags may be set.
|
1205
|
*
|
1206
|
* @return the boolean value of the isBinomHybrid flag
|
1207
|
* @see #isHybridFormula()
|
1208
|
* @see #isMonomHybrid()
|
1209
|
* @see #isTrinomHybrid()
|
1210
|
*/
|
1211
|
@Override
|
1212
|
public boolean isBinomHybrid(){
|
1213
|
return this.binomHybrid;
|
1214
|
}
|
1215
|
|
1216
|
/**
|
1217
|
* @see #isBinomHybrid()
|
1218
|
* @see #isMonomHybrid()
|
1219
|
* @see #isTrinomHybrid()
|
1220
|
*/
|
1221
|
@Override
|
1222
|
public void setBinomHybrid(boolean binomHybrid){
|
1223
|
this.binomHybrid = binomHybrid;
|
1224
|
}
|
1225
|
|
1226
|
@Override
|
1227
|
public boolean isTrinomHybrid(){
|
1228
|
return this.trinomHybrid;
|
1229
|
}
|
1230
|
|
1231
|
/**
|
1232
|
* @see #isTrinomHybrid()
|
1233
|
* @see #isBinomHybrid()
|
1234
|
* @see #isMonomHybrid()
|
1235
|
*/
|
1236
|
@Override
|
1237
|
public void setTrinomHybrid(boolean trinomHybrid){
|
1238
|
this.trinomHybrid = trinomHybrid;
|
1239
|
}
|
1240
|
|
1241
|
// ****************** VIRAL NAME ******************/
|
1242
|
|
1243
|
@Override
|
1244
|
public String getAcronym(){
|
1245
|
return this.acronym;
|
1246
|
}
|
1247
|
|
1248
|
/**
|
1249
|
* @see #getAcronym()
|
1250
|
*/
|
1251
|
@Override
|
1252
|
public void setAcronym(String acronym){
|
1253
|
this.acronym = StringUtils.isBlank(acronym)? null : acronym;
|
1254
|
}
|
1255
|
|
1256
|
// ****************** BACTERIAL NAME ******************/
|
1257
|
|
1258
|
@Override
|
1259
|
public String getSubGenusAuthorship(){
|
1260
|
return this.subGenusAuthorship;
|
1261
|
}
|
1262
|
|
1263
|
@Override
|
1264
|
public void setSubGenusAuthorship(String subGenusAuthorship){
|
1265
|
this.subGenusAuthorship = subGenusAuthorship;
|
1266
|
}
|
1267
|
|
1268
|
|
1269
|
@Override
|
1270
|
public String getNameApprobation(){
|
1271
|
return this.nameApprobation;
|
1272
|
}
|
1273
|
|
1274
|
/**
|
1275
|
* @see #getNameApprobation()
|
1276
|
*/
|
1277
|
@Override
|
1278
|
public void setNameApprobation(String nameApprobation){
|
1279
|
this.nameApprobation = nameApprobation;
|
1280
|
}
|
1281
|
|
1282
|
//************ Zoological Name
|
1283
|
|
1284
|
|
1285
|
@Override
|
1286
|
public String getBreed(){
|
1287
|
return this.breed;
|
1288
|
}
|
1289
|
/**
|
1290
|
* @see #getBreed()
|
1291
|
*/
|
1292
|
@Override
|
1293
|
public void setBreed(String breed){
|
1294
|
this.breed = StringUtils.isBlank(breed) ? null : breed;
|
1295
|
}
|
1296
|
|
1297
|
|
1298
|
@Override
|
1299
|
public Integer getPublicationYear() {
|
1300
|
return publicationYear;
|
1301
|
}
|
1302
|
/**
|
1303
|
* @see #getPublicationYear()
|
1304
|
*/
|
1305
|
@Override
|
1306
|
public void setPublicationYear(Integer publicationYear) {
|
1307
|
this.publicationYear = publicationYear;
|
1308
|
}
|
1309
|
|
1310
|
|
1311
|
@Override
|
1312
|
public Integer getOriginalPublicationYear() {
|
1313
|
return originalPublicationYear;
|
1314
|
}
|
1315
|
/**
|
1316
|
* @see #getOriginalPublicationYear()
|
1317
|
*/
|
1318
|
@Override
|
1319
|
public void setOriginalPublicationYear(Integer originalPublicationYear) {
|
1320
|
this.originalPublicationYear = originalPublicationYear;
|
1321
|
}
|
1322
|
|
1323
|
// **** Cultivar Name ************
|
1324
|
|
1325
|
|
1326
|
@Override
|
1327
|
public String getCultivarName(){
|
1328
|
return this.cultivarName;
|
1329
|
}
|
1330
|
|
1331
|
/**
|
1332
|
* @see #getCultivarName()
|
1333
|
*/
|
1334
|
@Override
|
1335
|
public void setCultivarName(String cultivarName){
|
1336
|
this.cultivarName = StringUtils.isBlank(cultivarName) ? null : cultivarName;
|
1337
|
}
|
1338
|
|
1339
|
// **************** Fungus Name
|
1340
|
@Override
|
1341
|
public boolean isAnamorphic(){
|
1342
|
return this.anamorphic;
|
1343
|
}
|
1344
|
|
1345
|
/**
|
1346
|
* @see #isAnamorphic()
|
1347
|
*/
|
1348
|
@Override
|
1349
|
public void setAnamorphic(boolean anamorphic){
|
1350
|
this.anamorphic = anamorphic;
|
1351
|
}
|
1352
|
|
1353
|
|
1354
|
// **************** ADDER / REMOVE *************************/
|
1355
|
|
1356
|
/**
|
1357
|
* Adds the given {@link HybridRelationship hybrid relationship} to the set
|
1358
|
* of {@link #getHybridRelationships() hybrid relationships} of both non-viral names
|
1359
|
* involved in this hybrid relationship. One of both non-viral names
|
1360
|
* must be <i>this</i> non-viral name otherwise no addition will be carried
|
1361
|
* out. The {@link eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo() child
|
1362
|
* non viral taxon name} must be a hybrid, which means that one of its four hybrid flags must be set.
|
1363
|
*
|
1364
|
* @param relationship the hybrid relationship to be added
|
1365
|
* @see #isHybridFormula()
|
1366
|
* @see #isMonomHybrid()
|
1367
|
* @see #isBinomHybrid()
|
1368
|
* @see #isTrinomHybrid()
|
1369
|
* @see #getHybridRelationships()
|
1370
|
* @see #getParentRelationships()
|
1371
|
* @see #getChildRelationships()
|
1372
|
* @see #addRelationship(RelationshipBase)
|
1373
|
* @throws IllegalArgumentException
|
1374
|
*/
|
1375
|
protected void addHybridRelationship(HybridRelationship rel) {
|
1376
|
if (rel!=null && rel.getHybridName().equals(this)){
|
1377
|
this.hybridChildRelations.add(rel);
|
1378
|
}else if(rel!=null && rel.getParentName().equals(this)){
|
1379
|
this.hybridParentRelations.add(rel);
|
1380
|
}else{
|
1381
|
throw new IllegalArgumentException("Hybrid relationship is either null or the relationship does not reference this name");
|
1382
|
}
|
1383
|
}
|
1384
|
|
1385
|
|
1386
|
/**
|
1387
|
* Removes one {@link HybridRelationship hybrid relationship} from the set of
|
1388
|
* {@link #getHybridRelationships() hybrid relationships} in which <i>this</i> botanical taxon name
|
1389
|
* is involved. The hybrid relationship will also be removed from the set
|
1390
|
* belonging to the second botanical taxon name involved.
|
1391
|
*
|
1392
|
* @param relationship the hybrid relationship which should be deleted from the corresponding sets
|
1393
|
* @see #getHybridRelationships()
|
1394
|
*/
|
1395
|
@Override
|
1396
|
public void removeHybridRelationship(HybridRelationship hybridRelation) {
|
1397
|
if (hybridRelation == null) {
|
1398
|
return;
|
1399
|
}
|
1400
|
|
1401
|
TaxonName parent = hybridRelation.getParentName();
|
1402
|
TaxonName child = hybridRelation.getHybridName();
|
1403
|
if (this.equals(parent)){
|
1404
|
this.hybridParentRelations.remove(hybridRelation);
|
1405
|
child.hybridChildRelations.remove(hybridRelation);
|
1406
|
hybridRelation.setHybridName(null);
|
1407
|
hybridRelation.setParentName(null);
|
1408
|
}
|
1409
|
if (this.equals(child)){
|
1410
|
parent.hybridParentRelations.remove(hybridRelation);
|
1411
|
this.hybridChildRelations.remove(hybridRelation);
|
1412
|
hybridRelation.setHybridName(null);
|
1413
|
hybridRelation.setParentName(null);
|
1414
|
}
|
1415
|
}
|
1416
|
|
1417
|
//********* METHODS **************************************/
|
1418
|
|
1419
|
@Override
|
1420
|
public INameCacheStrategy getCacheStrategy() {
|
1421
|
rectifyNameCacheStrategy();
|
1422
|
return this.cacheStrategy;
|
1423
|
}
|
1424
|
|
1425
|
@Override
|
1426
|
public String generateFullTitle(){
|
1427
|
if (getCacheStrategy() == null){
|
1428
|
logger.warn("No CacheStrategy defined for taxon name: " + this.getUuid());
|
1429
|
return null;
|
1430
|
}else{
|
1431
|
return cacheStrategy.getFullTitleCache(this);
|
1432
|
}
|
1433
|
}
|
1434
|
|
1435
|
|
1436
|
@Override
|
1437
|
public void setFullTitleCache(String fullTitleCache){
|
1438
|
setFullTitleCache(fullTitleCache, PROTECTED);
|
1439
|
}
|
1440
|
|
1441
|
@Override
|
1442
|
public void setFullTitleCache(String fullTitleCache, boolean protectCache){
|
1443
|
fullTitleCache = getTruncatedCache(fullTitleCache);
|
1444
|
this.fullTitleCache = fullTitleCache;
|
1445
|
this.setProtectedFullTitleCache(protectCache);
|
1446
|
}
|
1447
|
|
1448
|
/** Checks if this name is an autonym.<BR>
|
1449
|
* An autonym is a taxon name that has equal specific and infra specific epithets.<BR>
|
1450
|
* {@link http://ibot.sav.sk/icbn/frameset/0010Ch2Sec1a006.htm#6.8. Vienna Code §6.8}
|
1451
|
* or a taxon name that has equal generic and infrageneric epithets (A22.2).<BR>
|
1452
|
* Only relevant for botanical names.
|
1453
|
* @return true, if name has Rank, Rank is below species and species epithet equals infraSpeciesEpithtet, else false
|
1454
|
*/
|
1455
|
@Override
|
1456
|
@Transient
|
1457
|
public boolean isAutonym(){
|
1458
|
if (isBotanical()){
|
1459
|
if (this.getRank() != null && this.getSpecificEpithet() != null && this.getInfraSpecificEpithet() != null &&
|
1460
|
this.isInfraSpecific() && this.getSpecificEpithet().trim().equals(this.getInfraSpecificEpithet().trim())){
|
1461
|
return true;
|
1462
|
}else if (this.getRank() != null && this.getGenusOrUninomial() != null && this.getInfraGenericEpithet() != null &&
|
1463
|
this.isInfraGeneric() && this.getGenusOrUninomial().trim().equals(this.getInfraGenericEpithet().trim())){
|
1464
|
return true;
|
1465
|
}else{
|
1466
|
return false;
|
1467
|
}
|
1468
|
}else{
|
1469
|
return false;
|
1470
|
}
|
1471
|
}
|
1472
|
|
1473
|
|
1474
|
@Override
|
1475
|
@Transient
|
1476
|
public List<TaggedText> getTaggedName(){
|
1477
|
INameCacheStrategy strat = getCacheStrategy();
|
1478
|
return strat.getTaggedTitle(this);
|
1479
|
}
|
1480
|
|
1481
|
@Override
|
1482
|
@Transient
|
1483
|
public String getFullTitleCache(){
|
1484
|
if (protectedFullTitleCache){
|
1485
|
return this.fullTitleCache;
|
1486
|
}
|
1487
|
updateAuthorshipCache();
|
1488
|
if (fullTitleCache == null ){
|
1489
|
this.fullTitleCache = getTruncatedCache(generateFullTitle());
|
1490
|
}
|
1491
|
return fullTitleCache;
|
1492
|
}
|
1493
|
|
1494
|
|
1495
|
@Override
|
1496
|
public String getTitleCache(){
|
1497
|
if(!protectedTitleCache) {
|
1498
|
updateAuthorshipCache();
|
1499
|
}
|
1500
|
return super.getTitleCache();
|
1501
|
}
|
1502
|
|
1503
|
@Override
|
1504
|
public void setTitleCache(String titleCache, boolean protectCache){
|
1505
|
super.setTitleCache(titleCache, protectCache);
|
1506
|
}
|
1507
|
|
1508
|
/**
|
1509
|
* Returns the concatenated and formated authorteams string including
|
1510
|
* basionym and combination authors of <i>this</i> non viral taxon name.
|
1511
|
* If the protectedAuthorshipCache flag is set this method returns the
|
1512
|
* string stored in the the authorshipCache attribute, otherwise it
|
1513
|
* generates the complete authorship string, returns it and stores it in
|
1514
|
* the authorshipCache attribute.
|
1515
|
*
|
1516
|
* @return the string with the concatenated and formated authorteams for <i>this</i> non viral taxon name
|
1517
|
* @see #generateAuthorship()
|
1518
|
*/
|
1519
|
@Override
|
1520
|
@Transient
|
1521
|
public String getAuthorshipCache() {
|
1522
|
if (protectedAuthorshipCache){
|
1523
|
return this.authorshipCache;
|
1524
|
}
|
1525
|
if (this.authorshipCache == null ){
|
1526
|
this.authorshipCache = generateAuthorship();
|
1527
|
}else{
|
1528
|
//TODO get isDirty of authors, make better if possible
|
1529
|
this.setAuthorshipCache(generateAuthorship(), protectedAuthorshipCache); //throw change event to inform higher caches
|
1530
|
|
1531
|
}
|
1532
|
return authorshipCache;
|
1533
|
}
|
1534
|
|
1535
|
|
1536
|
|
1537
|
|
1538
|
|
1539
|
|
1540
|
/**
|
1541
|
* Assigns an authorshipCache string to <i>this</i> non viral taxon name. Sets the isProtectedAuthorshipCache
|
1542
|
* flag to <code>true</code>.
|
1543
|
*
|
1544
|
* @param authorshipCache the string which identifies the complete authorship of <i>this</i> non viral taxon name
|
1545
|
* @see #getAuthorshipCache()
|
1546
|
*/
|
1547
|
@Override
|
1548
|
public void setAuthorshipCache(String authorshipCache) {
|
1549
|
setAuthorshipCache(authorshipCache, true);
|
1550
|
}
|
1551
|
|
1552
|
|
1553
|
/**
|
1554
|
* Assigns an authorshipCache string to <i>this</i> non viral taxon name.
|
1555
|
*
|
1556
|
* @param authorshipCache the string which identifies the complete authorship of <i>this</i> non viral taxon name
|
1557
|
* @param protectedAuthorshipCache if true the isProtectedAuthorshipCache flag is set to <code>true</code>, otherwise
|
1558
|
* the flag is set to <code>false</code>.
|
1559
|
* @see #getAuthorshipCache()
|
1560
|
*/
|
1561
|
@Override
|
1562
|
public void setAuthorshipCache(String authorshipCache, boolean protectedAuthorshipCache) {
|
1563
|
this.authorshipCache = authorshipCache;
|
1564
|
this.setProtectedAuthorshipCache(protectedAuthorshipCache);
|
1565
|
}
|
1566
|
|
1567
|
/**
|
1568
|
* Generates and returns a concatenated and formated author team string
|
1569
|
* including basionym and combination authors of <i>this</i> taxon name
|
1570
|
* according to the strategy defined in
|
1571
|
* {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(TaxonName) INonViralNameCacheStrategy}.
|
1572
|
*
|
1573
|
* @return the string with the concatenated and formatted author teams for <i>this</i> taxon name
|
1574
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(TaxonName)
|
1575
|
*/
|
1576
|
@Override
|
1577
|
public String generateAuthorship(){
|
1578
|
if (getCacheStrategy() == null){
|
1579
|
logger.warn("No CacheStrategy defined for taxon name: " + this.getUuid());
|
1580
|
return null;
|
1581
|
}else{
|
1582
|
return cacheStrategy.getAuthorshipCache(this);
|
1583
|
}
|
1584
|
}
|
1585
|
|
1586
|
|
1587
|
|
1588
|
/**
|
1589
|
* Tests if the given name has any authors.
|
1590
|
* @return false if no author ((ex)combination or (ex)basionym) exists, true otherwise
|
1591
|
*/
|
1592
|
@Override
|
1593
|
public boolean hasAuthors() {
|
1594
|
return (this.getCombinationAuthorship() != null ||
|
1595
|
this.getExCombinationAuthorship() != null ||
|
1596
|
this.getBasionymAuthorship() != null ||
|
1597
|
this.getExBasionymAuthorship() != null);
|
1598
|
}
|
1599
|
|
1600
|
/**
|
1601
|
* Shortcut. Returns the combination authors title cache. Returns null if no combination author exists.
|
1602
|
* @return
|
1603
|
*/
|
1604
|
@Override
|
1605
|
public String computeCombinationAuthorNomenclaturalTitle() {
|
1606
|
return computeNomenclaturalTitle(this.getCombinationAuthorship());
|
1607
|
}
|
1608
|
|
1609
|
/**
|
1610
|
* Shortcut. Returns the basionym authors title cache. Returns null if no basionym author exists.
|
1611
|
* @return
|
1612
|
*/
|
1613
|
@Override
|
1614
|
public String computeBasionymAuthorNomenclaturalTitle() {
|
1615
|
return computeNomenclaturalTitle(this.getBasionymAuthorship());
|
1616
|
}
|
1617
|
|
1618
|
|
1619
|
/**
|
1620
|
* Shortcut. Returns the ex-combination authors title cache. Returns null if no ex-combination author exists.
|
1621
|
* @return
|
1622
|
*/
|
1623
|
@Override
|
1624
|
public String computeExCombinationAuthorNomenclaturalTitle() {
|
1625
|
return computeNomenclaturalTitle(this.getExCombinationAuthorship());
|
1626
|
}
|
1627
|
|
1628
|
/**
|
1629
|
* Shortcut. Returns the ex-basionym authors title cache. Returns null if no exbasionym author exists.
|
1630
|
* @return
|
1631
|
*/
|
1632
|
@Override
|
1633
|
public String computeExBasionymAuthorNomenclaturalTitle() {
|
1634
|
return computeNomenclaturalTitle(this.getExBasionymAuthorship());
|
1635
|
}
|
1636
|
|
1637
|
private String computeNomenclaturalTitle(INomenclaturalAuthor author){
|
1638
|
if (author == null){
|
1639
|
return null;
|
1640
|
}else{
|
1641
|
return author.getNomenclaturalTitle();
|
1642
|
}
|
1643
|
}
|
1644
|
|
1645
|
/**
|
1646
|
* Returns the set of all {@link NameRelationship name relationships}
|
1647
|
* in which <i>this</i> taxon name is involved. A taxon name can be both source
|
1648
|
* in some name relationships or target in some others.
|
1649
|
*
|
1650
|
* @see #getRelationsToThisName()
|
1651
|
* @see #getRelationsFromThisName()
|
1652
|
* @see #addNameRelationship(NameRelationship)
|
1653
|
* @see #addRelationshipToName(TaxonName, NameRelationshipType, String)
|
1654
|
* @see #addRelationshipFromName(TaxonName, NameRelationshipType, String)
|
1655
|
*/
|
1656
|
@Override
|
1657
|
@Transient
|
1658
|
public Set<NameRelationship> getNameRelations() {
|
1659
|
Set<NameRelationship> rels = new HashSet<NameRelationship>();
|
1660
|
rels.addAll(getRelationsFromThisName());
|
1661
|
rels.addAll(getRelationsToThisName());
|
1662
|
return rels;
|
1663
|
}
|
1664
|
|
1665
|
/**
|
1666
|
* Creates a new {@link NameRelationship#NameRelationship(TaxonName, TaxonName, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
|
1667
|
* and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
|
1668
|
* to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
|
1669
|
*
|
1670
|
* @param toName the taxon name of the target for this new name relationship
|
1671
|
* @param type the type of this new name relationship
|
1672
|
* @param ruleConsidered the string which specifies the rule on which this name relationship is based
|
1673
|
* @return
|
1674
|
* @see #getRelationsToThisName()
|
1675
|
* @see #getNameRelations()
|
1676
|
* @see #addRelationshipFromName(TaxonName, NameRelationshipType, String)
|
1677
|
* @see #addNameRelationship(NameRelationship)
|
1678
|
*/
|
1679
|
@Override
|
1680
|
public NameRelationship addRelationshipToName(TaxonName toName, NameRelationshipType type, String ruleConsidered){
|
1681
|
return addRelationshipToName(toName, type, null, null, ruleConsidered);
|
1682
|
}
|
1683
|
|
1684
|
/**
|
1685
|
* Creates a new {@link NameRelationship#NameRelationship(TaxonName, TaxonName, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
|
1686
|
* and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
|
1687
|
* to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
|
1688
|
*
|
1689
|
* @param toName the taxon name of the target for this new name relationship
|
1690
|
* @param type the type of this new name relationship
|
1691
|
* @param ruleConsidered the string which specifies the rule on which this name relationship is based
|
1692
|
* @return
|
1693
|
* @see #getRelationsToThisName()
|
1694
|
* @see #getNameRelations()
|
1695
|
* @see #addRelationshipFromName(TaxonName, NameRelationshipType, String)
|
1696
|
* @see #addNameRelationship(NameRelationship)
|
1697
|
*/
|
1698
|
@Override
|
1699
|
public NameRelationship addRelationshipToName(TaxonName toName, NameRelationshipType type, Reference citation, String microCitation, String ruleConsidered){
|
1700
|
if (toName == null){
|
1701
|
throw new NullPointerException("Null is not allowed as name for a name relationship");
|
1702
|
}
|
1703
|
NameRelationship rel = new NameRelationship(toName, this, type, citation, microCitation, ruleConsidered);
|
1704
|
return rel;
|
1705
|
}
|
1706
|
|
1707
|
/**
|
1708
|
* Creates a new {@link NameRelationship#NameRelationship(TaxonName, TaxonName, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
|
1709
|
* and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
|
1710
|
* to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
|
1711
|
*
|
1712
|
* @param fromName the taxon name of the source for this new name relationship
|
1713
|
* @param type the type of this new name relationship
|
1714
|
* @param ruleConsidered the string which specifies the rule on which this name relationship is based
|
1715
|
* @param citation the reference in which this relation was described
|
1716
|
* @param microCitation the reference detail for this relation (e.g. page)
|
1717
|
* @see #getRelationsFromThisName()
|
1718
|
* @see #getNameRelations()
|
1719
|
* @see #addRelationshipToName(TaxonName, NameRelationshipType, String)
|
1720
|
* @see #addNameRelationship(NameRelationship)
|
1721
|
*/
|
1722
|
@Override
|
1723
|
public NameRelationship addRelationshipFromName(TaxonName fromName, NameRelationshipType type, String ruleConsidered){
|
1724
|
//fromName.addRelationshipToName(this, type, null, null, ruleConsidered);
|
1725
|
return this.addRelationshipFromName(fromName, type, null, null, ruleConsidered);
|
1726
|
}
|
1727
|
/**
|
1728
|
* Creates a new {@link NameRelationship#NameRelationship(TaxonName, TaxonName, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
|
1729
|
* and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
|
1730
|
* to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
|
1731
|
*
|
1732
|
* @param fromName the taxon name of the source for this new name relationship
|
1733
|
* @param type the type of this new name relationship
|
1734
|
* @param ruleConsidered the string which specifies the rule on which this name relationship is based
|
1735
|
* @param citation the reference in which this relation was described
|
1736
|
* @param microCitation the reference detail for this relation (e.g. page)
|
1737
|
* @see #getRelationsFromThisName()
|
1738
|
* @see #getNameRelations()
|
1739
|
* @see #addRelationshipToName(TaxonName, NameRelationshipType, String)
|
1740
|
* @see #addNameRelationship(NameRelationship)
|
1741
|
*/
|
1742
|
@Override
|
1743
|
public NameRelationship addRelationshipFromName(TaxonName fromName, NameRelationshipType type, Reference citation, String microCitation, String ruleConsidered){
|
1744
|
return fromName.addRelationshipToName(this, type, citation, microCitation, ruleConsidered);
|
1745
|
}
|
1746
|
|
1747
|
/**
|
1748
|
* Adds an existing {@link NameRelationship name relationship} either to the set of
|
1749
|
* {@link #getRelationsToThisName() relations to <i>this</i> taxon name} or to the set of
|
1750
|
* {@link #getRelationsFromThisName() relations from <i>this</i> taxon name}. If neither the
|
1751
|
* source nor the target of the name relationship match with <i>this</i> taxon name
|
1752
|
* no addition will be carried out.
|
1753
|
*
|
1754
|
* @param rel the name relationship to be added to one of <i>this</i> taxon name's name relationships sets
|
1755
|
* @see #getNameRelations()
|
1756
|
* @see #addRelationshipToName(TaxonName, NameRelationshipType, String)
|
1757
|
* @see #addRelationshipFromName(TaxonName, NameRelationshipType, String)
|
1758
|
*/
|
1759
|
protected void addNameRelationship(NameRelationship rel) {
|
1760
|
if (rel != null ){
|
1761
|
if (rel.getToName().equals(this)){
|
1762
|
this.relationsToThisName.add(rel);
|
1763
|
}else if(rel.getFromName().equals(this)){
|
1764
|
this.relationsFromThisName.add(rel);
|
1765
|
}
|
1766
|
NameRelationshipType type = rel.getType();
|
1767
|
if (type != null && ( type.isBasionymRelation() || type.isReplacedSynonymRelation() ) ){
|
1768
|
rel.getFromName().mergeHomotypicGroups(rel.getToName());
|
1769
|
}
|
1770
|
}else{
|
1771
|
throw new RuntimeException("NameRelationship is either null or the relationship does not reference this name");
|
1772
|
}
|
1773
|
}
|
1774
|
/**
|
1775
|
* Removes one {@link NameRelationship name relationship} from one of both sets of
|
1776
|
* {@link #getNameRelations() name relationships} in which <i>this</i> taxon name is involved.
|
1777
|
* The name relationship will also be removed from one of both sets belonging
|
1778
|
* to the second taxon name involved. Furthermore the fromName and toName
|
1779
|
* attributes of the name relationship object will be nullified.
|
1780
|
*
|
1781
|
* @param nameRelation the name relationship which should be deleted from one of both sets
|
1782
|
* @see #getNameRelations()
|
1783
|
*/
|
1784
|
@Override
|
1785
|
public void removeNameRelationship(NameRelationship nameRelation) {
|
1786
|
|
1787
|
TaxonName fromName = nameRelation.getFromName();
|
1788
|
TaxonName toName = nameRelation.getToName();
|
1789
|
|
1790
|
if (nameRelation != null) {
|
1791
|
nameRelation.setToName(null);
|
1792
|
nameRelation.setFromName(null);
|
1793
|
}
|
1794
|
|
1795
|
if (fromName != null) {
|
1796
|
fromName.removeNameRelationship(nameRelation);
|
1797
|
}
|
1798
|
|
1799
|
if (toName != null) {
|
1800
|
toName.removeNameRelationship(nameRelation);
|
1801
|
}
|
1802
|
|
1803
|
this.relationsToThisName.remove(nameRelation);
|
1804
|
this.relationsFromThisName.remove(nameRelation);
|
1805
|
}
|
1806
|
|
1807
|
@Override
|
1808
|
public void removeRelationToTaxonName(TaxonName toTaxonName) {
|
1809
|
Set<NameRelationship> nameRelationships = new HashSet<NameRelationship>();
|
1810
|
// nameRelationships.addAll(this.getNameRelations());
|
1811
|
nameRelationships.addAll(this.getRelationsFromThisName());
|
1812
|
nameRelationships.addAll(this.getRelationsToThisName());
|
1813
|
for(NameRelationship nameRelationship : nameRelationships) {
|
1814
|
// remove name relationship from this side
|
1815
|
if (nameRelationship.getFromName().equals(this) && nameRelationship.getToName().equals(toTaxonName)) {
|
1816
|
this.removeNameRelationship(nameRelationship);
|
1817
|
}
|
1818
|
}
|
1819
|
}
|
1820
|
|
1821
|
public void removeRelationWithTaxonName(TaxonName otherTaxonName, Direction direction, NameRelationshipType type) {
|
1822
|
|
1823
|
Set<NameRelationship> tmpRels = new HashSet<>(relationsWithThisName(direction));
|
1824
|
for(NameRelationship nameRelationship : tmpRels) {
|
1825
|
if (direction.equals(Direction.relatedFrom) && nameRelationship.getToName().equals(otherTaxonName) ||
|
1826
|
direction.equals(Direction.relatedTo) && nameRelationship.getFromName().equals(otherTaxonName)) {
|
1827
|
if (type == null || type.equals(nameRelationship.getType())){
|
1828
|
this.removeNameRelationship(nameRelationship);
|
1829
|
}
|
1830
|
}
|
1831
|
}
|
1832
|
}
|
1833
|
|
1834
|
|
1835
|
/**
|
1836
|
* If relation is of type NameRelationship, addNameRelationship is called;
|
1837
|
* if relation is of type HybridRelationship addHybridRelationship is called,
|
1838
|
* otherwise an IllegalArgumentException is thrown.
|
1839
|
*
|
1840
|
* @param relation the relationship to be added to one of <i>this</i> taxon name's name relationships sets
|
1841
|
* @see #addNameRelationship(NameRelationship)
|
1842
|
* @see #getNameRelations()
|
1843
|
* @see NameRelationship
|
1844
|
* @see RelationshipBase
|
1845
|
* @see #addHybridRelationship(HybridRelationship)
|
1846
|
|
1847
|
* @deprecated to be used by RelationshipBase only
|
1848
|
*/
|
1849
|
@Deprecated
|
1850
|
@Override
|
1851
|
public void addRelationship(RelationshipBase relation) {
|
1852
|
if (relation instanceof NameRelationship){
|
1853
|
addNameRelationship((NameRelationship)relation);
|
1854
|
|
1855
|
}else if (relation instanceof HybridRelationship){
|
1856
|
addHybridRelationship((HybridRelationship)relation);
|
1857
|
}else{
|
1858
|
logger.warn("Relationship not of type NameRelationship!");
|
1859
|
throw new IllegalArgumentException("Relationship not of type NameRelationship or HybridRelationship");
|
1860
|
}
|
1861
|
}
|
1862
|
|
1863
|
/**
|
1864
|
* Returns the set of all {@link NameRelationship name relationships}
|
1865
|
* in which <i>this</i> taxon name is involved as a source ("from"-side).
|
1866
|
*
|
1867
|
* @see #getNameRelations()
|
1868
|
* @see #getRelationsToThisName()
|
1869
|
* @see #addRelationshipFromName(TaxonName, NameRelationshipType, String)
|
1870
|
*/
|
1871
|
@Override
|
1872
|
public Set<NameRelationship> getRelationsFromThisName() {
|
1873
|
if(relationsFromThisName == null) {
|
1874
|
this.relationsFromThisName = new HashSet<>();
|
1875
|
}
|
1876
|
return relationsFromThisName;
|
1877
|
}
|
1878
|
|
1879
|
/**
|
1880
|
* Returns the set of all {@link NameRelationship name relationships}
|
1881
|
* in which <i>this</i> taxon name is involved as a target ("to"-side).
|
1882
|
*
|
1883
|
* @see #getNameRelations()
|
1884
|
* @see #getRelationsFromThisName()
|
1885
|
* @see #addRelationshipToName(TaxonName, NameRelationshipType, String)
|
1886
|
*/
|
1887
|
@Override
|
1888
|
public Set<NameRelationship> getRelationsToThisName() {
|
1889
|
if(relationsToThisName == null) {
|
1890
|
this.relationsToThisName = new HashSet<>();
|
1891
|
}
|
1892
|
return relationsToThisName;
|
1893
|
}
|
1894
|
|
1895
|
/**
|
1896
|
* Returns the set of {@link NomenclaturalStatus nomenclatural status} assigned
|
1897
|
* to <i>this</i> taxon name according to its corresponding nomenclature code.
|
1898
|
* This includes the {@link NomenclaturalStatusType type} of the nomenclatural status
|
1899
|
* and the nomenclatural code rule considered.
|
1900
|
*
|
1901
|
* @see NomenclaturalStatus
|
1902
|
* @see NomenclaturalStatusType
|
1903
|
*/
|
1904
|
@Override
|
1905
|
public Set<NomenclaturalStatus> getStatus() {
|
1906
|
if(status == null) {
|
1907
|
this.status = new HashSet<>();
|
1908
|
}
|
1909
|
return status;
|
1910
|
}
|
1911
|
|
1912
|
/**
|
1913
|
* Adds a new {@link NomenclaturalStatus nomenclatural status}
|
1914
|
* to <i>this</i> taxon name's set of nomenclatural status.
|
1915
|
*
|
1916
|
* @param nomStatus the nomenclatural status to be added
|
1917
|
* @see #getStatus()
|
1918
|
*/
|
1919
|
@Override
|
1920
|
public void addStatus(NomenclaturalStatus nomStatus) {
|
1921
|
this.status.add(nomStatus);
|
1922
|
}
|
1923
|
@Override
|
1924
|
public NomenclaturalStatus addStatus(NomenclaturalStatusType statusType, Reference citation, String microCitation) {
|
1925
|
NomenclaturalStatus newStatus = NomenclaturalStatus.NewInstance(statusType, citation, microCitation);
|
1926
|
this.status.add(newStatus);
|
1927
|
return newStatus;
|
1928
|
}
|
1929
|
|
1930
|
/**
|
1931
|
* Removes one element from the set of nomenclatural status of <i>this</i> taxon name.
|
1932
|
* Type and ruleConsidered attributes of the nomenclatural status object
|
1933
|
* will be nullified.
|
1934
|
*
|
1935
|
* @param nomStatus the nomenclatural status of <i>this</i> taxon name which should be deleted
|
1936
|
* @see #getStatus()
|
1937
|
*/
|
1938
|
@Override
|
1939
|
public void removeStatus(NomenclaturalStatus nomStatus) {
|
1940
|
//TODO to be implemented?
|
1941
|
logger.warn("not yet fully implemented?");
|
1942
|
this.status.remove(nomStatus);
|
1943
|
}
|
1944
|
|
1945
|
|
1946
|
/**
|
1947
|
* Generates the composed name string of <i>this</i> non viral taxon name without author
|
1948
|
* strings or year according to the strategy defined in
|
1949
|
* {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy INonViralNameCacheStrategy}.
|
1950
|
* The result might be stored in {@link #getNameCache() nameCache} if the
|
1951
|
* flag {@link #isProtectedNameCache() protectedNameCache} is not set.
|
1952
|
*
|
1953
|
* @return the string with the composed name of <i>this</i> non viral taxon name without authors or year
|
1954
|
* @see #getNameCache()
|
1955
|
*/
|
1956
|
protected String generateNameCache(){
|
1957
|
if (getCacheStrategy() == null){
|
1958
|
logger.warn("No CacheStrategy defined for taxon name: " + this.toString());
|
1959
|
return null;
|
1960
|
}else{
|
1961
|
return cacheStrategy.getNameCache(this);
|
1962
|
}
|
1963
|
}
|
1964
|
|
1965
|
/**
|
1966
|
* Returns or generates the nameCache (scientific name
|
1967
|
* without author strings and year) string for <i>this</i> non viral taxon name. If the
|
1968
|
* {@link #isProtectedNameCache() protectedNameCache} flag is not set (False)
|
1969
|
* the string will be generated according to a defined strategy,
|
1970
|
* otherwise the value of the actual nameCache string will be returned.
|
1971
|
*
|
1972
|
* @return the string which identifies <i>this</i> non viral taxon name (without authors or year)
|
1973
|
* @see #generateNameCache()
|
1974
|
*/
|
1975
|
@Override
|
1976
|
@Transient
|
1977
|
public String getNameCache() {
|
1978
|
if (protectedNameCache){
|
1979
|
return this.nameCache;
|
1980
|
}
|
1981
|
// is title dirty, i.e. equal NULL?
|
1982
|
if (nameCache == null){
|
1983
|
this.nameCache = generateNameCache();
|
1984
|
}
|
1985
|
return nameCache;
|
1986
|
}
|
1987
|
|
1988
|
/**
|
1989
|
* Assigns a nameCache string to <i>this</i> non viral taxon name and protects it from being overwritten.
|
1990
|
* Sets the protectedNameCache flag to <code>true</code>.
|
1991
|
*
|
1992
|
* @param nameCache the string which identifies <i>this</i> non viral taxon name (without authors or year)
|
1993
|
* @see #getNameCache()
|
1994
|
*/
|
1995
|
@Override
|
1996
|
public void setNameCache(String nameCache){
|
1997
|
setNameCache(nameCache, true);
|
1998
|
}
|
1999
|
|
2000
|
/**
|
2001
|
* Assigns a nameCache string to <i>this</i> non viral taxon name and protects it from being overwritten.
|
2002
|
* Sets the protectedNameCache flag to <code>true</code>.
|
2003
|
*
|
2004
|
* @param nameCache the string which identifies <i>this</i> non viral taxon name (without authors or year)
|
2005
|
* @param protectedNameCache if true teh protectedNameCache is set to <code>true</code> or otherwise set to
|
2006
|
* <code>false</code>
|
2007
|
* @see #getNameCache()
|
2008
|
*/
|
2009
|
@Override
|
2010
|
public void setNameCache(String nameCache, boolean protectedNameCache){
|
2011
|
this.nameCache = nameCache;
|
2012
|
this.setProtectedNameCache(protectedNameCache);
|
2013
|
}
|
2014
|
|
2015
|
|
2016
|
/**
|
2017
|
* Indicates whether <i>this</i> taxon name is a {@link NameRelationshipType#BASIONYM() basionym}
|
2018
|
* or a {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym}
|
2019
|
* of any other taxon name. Returns "true", if a basionym or a replaced
|
2020
|
* synonym {@link NameRelationship relationship} from <i>this</i> taxon name to another taxon name exists,
|
2021
|
* false otherwise (also in case <i>this</i> taxon name is the only one in the
|
2022
|
* homotypical group).
|
2023
|
*/
|
2024
|
@Override
|
2025
|
@Transient
|
2026
|
public boolean isOriginalCombination(){
|
2027
|
Set<NameRelationship> relationsFromThisName = this.getRelationsFromThisName();
|
2028
|
for (NameRelationship relation : relationsFromThisName) {
|
2029
|
if (relation.getType().isBasionymRelation() ||
|
2030
|
relation.getType().isReplacedSynonymRelation()) {
|
2031
|
return true;
|
2032
|
}
|
2033
|
}
|
2034
|
return false;
|
2035
|
}
|
2036
|
|
2037
|
/**
|
2038
|
* Indicates <i>this</i> taxon name is a {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym}
|
2039
|
* of any other taxon name. Returns "true", if a replaced
|
2040
|
* synonym {@link NameRelationship relationship} from <i>this</i> taxon name to another taxon name exists,
|
2041
|
* false otherwise (also in case <i>this</i> taxon name is the only one in the
|
2042
|
* homotypical group).
|
2043
|
*/
|
2044
|
@Override
|
2045
|
@Transient
|
2046
|
public boolean isReplacedSynonym(){
|
2047
|
Set<NameRelationship> relationsFromThisName = this.getRelationsFromThisName();
|
2048
|
for (NameRelationship relation : relationsFromThisName) {
|
2049
|
if (relation.getType().isReplacedSynonymRelation()) {
|
2050
|
return true;
|
2051
|
}
|
2052
|
}
|
2053
|
return false;
|
2054
|
}
|
2055
|
|
2056
|
/**
|
2057
|
* Returns the taxon name which is the {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
|
2058
|
* The basionym of a taxon name is its epithet-bringing synonym.
|
2059
|
* For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
|
2060
|
* Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
|
2061
|
* <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
|
2062
|
*
|
2063
|
* If more than one basionym exists one is choosen at radom.
|
2064
|
*
|
2065
|
* If no basionym exists null is returned.
|
2066
|
*/
|
2067
|
@Override
|
2068
|
@Transient
|
2069
|
public TaxonName getBasionym(){
|
2070
|
Set<TaxonName> basionyms = getBasionyms();
|
2071
|
if (basionyms.size() == 0){
|
2072
|
return null;
|
2073
|
}else{
|
2074
|
return basionyms.iterator().next();
|
2075
|
}
|
2076
|
}
|
2077
|
|
2078
|
/**
|
2079
|
* Returns the set of taxon names which are the {@link NameRelationshipType#BASIONYM() basionyms} of <i>this</i> taxon name.
|
2080
|
* The basionym of a taxon name is its epithet-bringing synonym.
|
2081
|
* For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
|
2082
|
* Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
|
2083
|
* <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
|
2084
|
*/
|
2085
|
@Override
|
2086
|
@Transient
|
2087
|
public Set<TaxonName> getBasionyms(){
|
2088
|
return getRelatedNames(Direction.relatedTo, NameRelationshipType.BASIONYM());
|
2089
|
}
|
2090
|
|
2091
|
/**
|
2092
|
*
|
2093
|
* @param direction
|
2094
|
* @param type
|
2095
|
* @return
|
2096
|
*/
|
2097
|
public Set<TaxonName> getRelatedNames(Direction direction, NameRelationshipType type) {
|
2098
|
return getRelatedNames(relationsWithThisName(direction), type);
|
2099
|
}
|
2100
|
|
2101
|
/**
|
2102
|
* @param rels
|
2103
|
* @param type
|
2104
|
* @return
|
2105
|
*/
|
2106
|
private Set<TaxonName> getRelatedNames(Set<NameRelationship> rels, NameRelationshipType type) {
|
2107
|
Set<TaxonName> result = new HashSet<>();
|
2108
|
for (NameRelationship rel : rels){
|
2109
|
if (rel.getType()!= null && rel.getType().isRelationshipType(type)){
|
2110
|
TaxonName basionym = rel.getFromName();
|
2111
|
result.add(basionym);
|
2112
|
}
|
2113
|
}
|
2114
|
return result;
|
2115
|
}
|
2116
|
|
2117
|
/**
|
2118
|
* Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
|
2119
|
* The basionym {@link NameRelationship relationship} will be added to <i>this</i> taxon name
|
2120
|
* and to the basionym. The basionym cannot have itself as a basionym.
|
2121
|
* The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
|
2122
|
* will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
|
2123
|
*
|
2124
|
* @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
|
2125
|
* @see #getBasionym()
|
2126
|
* @see #addBasionym(TaxonName, String)
|
2127
|
*/
|
2128
|
@Override
|
2129
|
public void addBasionym(TaxonName basionym){
|
2130
|
addBasionym(basionym, null, null, null);
|
2131
|
}
|
2132
|
/**
|
2133
|
* Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name
|
2134
|
* and keeps the nomenclatural rule considered for it. The basionym
|
2135
|
* {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the basionym.
|
2136
|
* The basionym cannot have itself as a basionym.
|
2137
|
* The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
|
2138
|
* will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
|
2139
|
*
|
2140
|
* @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
|
2141
|
* @param ruleConsidered the string identifying the nomenclatural rule
|
2142
|
* @return
|
2143
|
* @see #getBasionym()
|
2144
|
* @see #addBasionym(TaxonName)
|
2145
|
*/
|
2146
|
@Override
|
2147
|
public NameRelationship addBasionym(TaxonName basionym, Reference citation, String microcitation, String ruleConsidered){
|
2148
|
if (basionym != null){
|
2149
|
return basionym.addRelationshipToName(this, NameRelationshipType.BASIONYM(), citation, microcitation, ruleConsidered);
|
2150
|
}else{
|
2151
|
return null;
|
2152
|
}
|
2153
|
}
|
2154
|
|
2155
|
/**
|
2156
|
* Returns the set of taxon names which are the {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonyms} of <i>this</i> taxon name.
|
2157
|
*
|
2158
|
*/
|
2159
|
@Override
|
2160
|
@Transient
|
2161
|
public Set<TaxonName> getReplacedSynonyms(){
|
2162
|
|
2163
|
return getRelatedNames(Direction.relatedTo, NameRelationshipType.REPLACED_SYNONYM());
|
2164
|
}
|
2165
|
|
2166
|
/**
|
2167
|
* Assigns a taxon name as {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym} of <i>this</i> taxon name
|
2168
|
* and keeps the nomenclatural rule considered for it. The replaced synonym
|
2169
|
* {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the replaced synonym.
|
2170
|
* The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the replaced synonym
|
2171
|
* will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
|
2172
|
*
|
2173
|
* @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
|
2174
|
* @param ruleConsidered the string identifying the nomenclatural rule
|
2175
|
* @see #getBasionym()
|
2176
|
* @see #addBasionym(TaxonName)
|
2177
|
*/
|
2178
|
//TODO: Check if true: The replaced synonym cannot have itself a replaced synonym (?).
|
2179
|
@Override
|
2180
|
public void addReplacedSynonym(TaxonName replacedSynonym, Reference citation, String microcitation, String ruleConsidered){
|
2181
|
if (replacedSynonym != null){
|
2182
|
replacedSynonym.addRelationshipToName(this, NameRelationshipType.REPLACED_SYNONYM(), citation, microcitation, ruleConsidered);
|
2183
|
}
|
2184
|
}
|
2185
|
|
2186
|
/**
|
2187
|
* Removes the {@link NameRelationshipType#BASIONYM() basionym} {@link NameRelationship relationship} from the set of
|
2188
|
* {@link #getRelationsToThisName() name relationships to} <i>this</i> taxon name. The same relationhip will be
|
2189
|
* removed from the set of {@link #getRelationsFromThisName() name relationships from} the taxon name
|
2190
|
* previously used as basionym.
|
2191
|
*
|
2192
|
* @see #getBasionym()
|
2193
|
* @see #addBasionym(TaxonName)
|
2194
|
*/
|
2195
|
@Override
|
2196
|
public void removeBasionyms(){
|
2197
|
removeNameRelations(Direction.relatedTo, NameRelationshipType.BASIONYM());
|
2198
|
}
|
2199
|
|
2200
|
|
2201
|
/**
|
2202
|
* Removes all {@link NameRelationship relationships} of the given <code>type</code> from the set of
|
2203
|
* relations in the specified <code>direction</code> direction wich are related from or to this
|
2204
|
* <i>this</i> taxon name. The same relationship will be removed from the set of
|
2205
|
* reverse relations of the other taxon name.
|
2206
|
*
|
2207
|
* @param direction
|
2208
|
* @param type
|
2209
|
*/
|
2210
|
public void removeNameRelations(Direction direction, NameRelationshipType type) {
|
2211
|
Set<NameRelationship> relationsWithThisName = relationsWithThisName(direction);
|
2212
|
Set<NameRelationship> removeRelations = new HashSet<>();
|
2213
|
for (NameRelationship nameRelation : relationsWithThisName){
|
2214
|
if (nameRelation.getType().isRelationshipType(type)){
|
2215
|
removeRelations.add(nameRelation);
|
2216
|
}
|
2217
|
}
|
2218
|
// Removing relations from a set through which we are iterating causes a
|
2219
|
// ConcurrentModificationException. Therefore, we delete the targeted
|
2220
|
// relations in a second step.
|
2221
|
for (NameRelationship relation : removeRelations){
|
2222
|
this.removeNameRelationship(relation);
|
2223
|
}
|
2224
|
}
|
2225
|
|
2226
|
|
2227
|
/**
|
2228
|
* @param direction
|
2229
|
* @return
|
2230
|
*/
|
2231
|
protected Set<NameRelationship> relationsWithThisName(Direction direction) {
|
2232
|
|
2233
|
switch(direction) {
|
2234
|
case relatedTo:
|
2235
|
return this.getRelationsToThisName();
|
2236
|
case relatedFrom:
|
2237
|
return this.getRelationsFromThisName();
|
2238
|
default: throw new RuntimeException("invalid Direction:" + direction);
|
2239
|
}
|
2240
|
}
|
2241
|
|
2242
|
/**
|
2243
|
* Returns the taxonomic {@link Rank rank} of <i>this</i> taxon name.
|
2244
|
*
|
2245
|
* @see Rank
|
2246
|
*/
|
2247
|
@Override
|
2248
|
public Rank getRank(){
|
2249
|
return this.rank;
|
2250
|
}
|
2251
|
|
2252
|
/**
|
2253
|
* @see #getRank()
|
2254
|
*/
|
2255
|
@Override
|
2256
|
public void setRank(Rank rank){
|
2257
|
this.rank = rank;
|
2258
|
}
|
2259
|
|
2260
|
|
2261
|
@Override
|
2262
|
public Reference getNomenclaturalReference(){
|
2263
|
//#6581
|
2264
|
return this.nomenclaturalReference;
|
2265
|
// if (this.nomenclaturalSource == null){
|
2266
|
// return null;
|
2267
|
// }
|
2268
|
// return this.nomenclaturalSource.getCitation();
|
2269
|
}
|
2270
|
|
2271
|
@Override
|
2272
|
public DescriptionElementSource getNomenclaturalSource(){
|
2273
|
return this.nomenclaturalSource;
|
2274
|
}
|
2275
|
|
2276
|
protected DescriptionElementSource getNomenclaturalSource(boolean createIfNotExist){
|
2277
|
if (this.nomenclaturalSource == null){
|
2278
|
if (!createIfNotExist){
|
2279
|
return null;
|
2280
|
}
|
2281
|
this.nomenclaturalSource = DescriptionElementSource.NewInstance(OriginalSourceType.NomenclaturalReference);
|
2282
|
}
|
2283
|
return this.nomenclaturalSource;
|
2284
|
}
|
2285
|
|
2286
|
/**
|
2287
|
* Assigns a {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} to <i>this</i> taxon name.
|
2288
|
* The corresponding {@link eu.etaxonomy.cdm.model.reference.Reference.isNomenclaturallyRelevant nomenclaturally relevant flag} will be set to true
|
2289
|
* as it is obviously used for nomenclatural purposes.
|
2290
|
*
|
2291
|
* Shortcut to set the nomenclatural reference.
|
2292
|
*
|
2293
|
* @throws IllegalArgumentException if parameter <code>nomenclaturalReference</code> is not assignable from {@link INomenclaturalReference}
|
2294
|
* @see #getNomenclaturalReference()
|
2295
|
*/
|
2296
|
|
2297
|
@Override
|
2298
|
public void setNomenclaturalReference(Reference nomenclaturalReference){
|
2299
|
//#6581
|
2300
|
this.nomenclaturalReference = nomenclaturalReference;
|
2301
|
// getNomenclaturalSource(true).setCitation(nomenclaturalReference);
|
2302
|
// checkNullSource();
|
2303
|
}
|
2304
|
@Override
|
2305
|
public void setNomenclaturalReference(INomenclaturalReference nomenclaturalReference){
|
2306
|
setNomenclaturalReference(CdmBase.deproxy(nomenclaturalReference, Reference.class));
|
2307
|
}
|
2308
|
|
2309
|
//#6581
|
2310
|
private void checkNullSource() {
|
2311
|
if (this.nomenclaturalSource == null){
|
2312
|
return;
|
2313
|
}else if (this.nomenclaturalSource.getCitation() != null
|
2314
|
|| this.nomenclaturalSource.getCitationMicroReference() != null
|
2315
|
|| this.nomenclaturalSource.getNameUsedInSource() != null
|
2316
|
|| isBlank(this.nomenclaturalSource.getOriginalNameString())){
|
2317
|
//TODO what about supplemental data?
|
2318
|
return;
|
2319
|
}else{
|
2320
|
this.nomenclaturalSource = null;
|
2321
|
}
|
2322
|
}
|
2323
|
|
2324
|
|
2325
|
@Override
|
2326
|
public void setNomenclaturalSource(DescriptionElementSource nomenclaturalSource) throws IllegalArgumentException {
|
2327
|
if (!OriginalSourceType.NomenclaturalReference.equals(nomenclaturalSource.getType()) ){
|
2328
|
throw new IllegalArgumentException("Nomenclatural source must be of type " + OriginalSourceType.NomenclaturalReference.getMessage());
|
2329
|
}
|
2330
|
this.nomenclaturalSource = nomenclaturalSource;
|
2331
|
}
|
2332
|
|
2333
|
/**
|
2334
|
* Returns the appended phrase string assigned to <i>this</i> taxon name.
|
2335
|
* The appended phrase is a non-atomised addition to a name. It is
|
2336
|
* not ruled by a nomenclatural code.
|
2337
|
*/
|
2338
|
@Override
|
2339
|
public String getAppendedPhrase(){
|
2340
|
return this.appendedPhrase;
|
2341
|
}
|
2342
|
|
2343
|
/**
|
2344
|
* @see #getAppendedPhrase()
|
2345
|
*/
|
2346
|
@Override
|
2347
|
public void setAppendedPhrase(String appendedPhrase){
|
2348
|
this.appendedPhrase = StringUtils.isBlank(appendedPhrase)? null : appendedPhrase;
|
2349
|
}
|
2350
|
|
2351
|
/**
|
2352
|
* Returns the details string of the {@link #getNomenclaturalReference() nomenclatural reference} assigned
|
2353
|
* to <i>this</i> taxon name. The details describe the exact localisation within
|
2354
|
* the publication used as nomenclature reference. These are mostly
|
2355
|
* (implicitly) pages but can also be figures or tables or any other
|
2356
|
* element of a publication. A nomenclatural micro reference (details)
|
2357
|
* requires the existence of a nomenclatural reference.
|
2358
|
*/
|
2359
|
//Details of the nomenclatural reference (protologue).
|
2360
|
@Override
|
2361
|
public String getNomenclaturalMicroReference(){
|
2362
|
//#6581
|
2363
|
return this.nomenclaturalMicroReference;
|
2364
|
// if (this.nomenclaturalSource == null){
|
2365
|
// return null;
|
2366
|
// }
|
2367
|
// return this.nomenclaturalSource.getCitationMicroReference();
|
2368
|
}
|
2369
|
/**
|
2370
|
* @see #getNomenclaturalMicroReference()
|
2371
|
*/
|
2372
|
@Override
|
2373
|
public void setNomenclaturalMicroReference(String nomenclaturalMicroReference){
|
2374
|
//#6581
|
2375
|
this.nomenclaturalMicroReference = nomenclaturalMicroReference;
|
2376
|
// this.getNomenclaturalSource(true).setCitationMicroReference(StringUtils.isBlank(nomenclaturalMicroReference)? null : nomenclaturalMicroReference);
|
2377
|
// checkNullSource();
|
2378
|
}
|
2379
|
|
2380
|
@Override
|
2381
|
public int getParsingProblem(){
|
2382
|
return this.parsingProblem;
|
2383
|
}
|
2384
|
|
2385
|
@Override
|
2386
|
public void setParsingProblem(int parsingProblem){
|
2387
|
this.parsingProblem = parsingProblem;
|
2388
|
}
|
2389
|
|
2390
|
@Override
|
2391
|
public void addParsingProblem(ParserProblem problem){
|
2392
|
parsingProblem = ParserProblem.addProblem(parsingProblem, problem);
|
2393
|
}
|
2394
|
|
2395
|
@Override
|
2396
|
public void removeParsingProblem(ParserProblem problem) {
|
2397
|
parsingProblem = ParserProblem.removeProblem(parsingProblem, problem);
|
2398
|
}
|
2399
|
|
2400
|
/**
|
2401
|
* @param warnings
|
2402
|
*/
|
2403
|
@Override
|
2404
|
public void addParsingProblems(int problems){
|
2405
|
parsingProblem = ParserProblem.addProblems(parsingProblem, problems);
|
2406
|
}
|
2407
|
|
2408
|
@Override
|
2409
|
public boolean hasProblem(){
|
2410
|
return parsingProblem != 0;
|
2411
|
}
|
2412
|
|
2413
|
@Override
|
2414
|
public boolean hasProblem(ParserProblem problem) {
|
2415
|
return getParsingProblems().contains(problem);
|
2416
|
}
|
2417
|
|
2418
|
@Override
|
2419
|
public int getProblemStarts(){
|
2420
|
return this.problemStarts;
|
2421
|
}
|
2422
|
|
2423
|
@Override
|
2424
|
public void setProblemStarts(int start) {
|
2425
|
this.problemStarts = start;
|
2426
|
}
|
2427
|
|
2428
|
@Override
|
2429
|
public int getProblemEnds(){
|
2430
|
return this.problemEnds;
|
2431
|
}
|
2432
|
|
2433
|
@Override
|
2434
|
public void setProblemEnds(int end) {
|
2435
|
this.problemEnds = end;
|
2436
|
}
|
2437
|
|
2438
|
//*********************** TYPE DESIGNATION *********************************************//
|
2439
|
|
2440
|
/**
|
2441
|
* Returns the set of {@link TypeDesignationBase type designations} assigned
|
2442
|
* to <i>this</i> taxon name.
|
2443
|
* @see NameTypeDesignation
|
2444
|
* @see SpecimenTypeDesignation
|
2445
|
*/
|
2446
|
@Override
|
2447
|
public Set<TypeDesignationBase> getTypeDesignations() {
|
2448
|
if(typeDesignations == null) {
|
2449
|
this.typeDesignations = new HashSet<>();
|
2450
|
}
|
2451
|
return typeDesignations;
|
2452
|
}
|
2453
|
|
2454
|
/**
|
2455
|
* Removes one element from the set of {@link TypeDesignationBase type designations} assigned to
|
2456
|
* <i>this</i> taxon name. The type designation itself will be nullified.
|
2457
|
*
|
2458
|
* @param typeDesignation the type designation which should be deleted
|
2459
|
*/
|
2460
|
@Override
|
2461
|
@SuppressWarnings("deprecation")
|
2462
|
public void removeTypeDesignation(TypeDesignationBase<?> typeDesignation) {
|
2463
|
this.typeDesignations.remove(typeDesignation);
|
2464
|
typeDesignation.removeTypifiedName(this);
|
2465
|
}
|
2466
|
|
2467
|
/**
|
2468
|
* Returns the set of {@link SpecimenTypeDesignation specimen type designations} assigned
|
2469
|
* to <i>this</i> taxon name. The {@link Rank rank} of <i>this</i> taxon name is generally
|
2470
|
* "species" or below. The specimen type designations include all the
|
2471
|
* specimens on which the typification of this name is based (which are
|
2472
|
* exclusively used to typify taxon names belonging to the same
|
2473
|
* {@link HomotypicalGroup homotypical group} to which <i>this</i> taxon name
|
2474
|
* belongs) and eventually the status of these designations.
|
2475
|
*
|
2476
|
* @see SpecimenTypeDesignation
|
2477
|
* @see NameTypeDesignation
|
2478
|
* @see HomotypicalGroup
|
2479
|
*/
|
2480
|
@Override
|
2481
|
@Transient
|
2482
|
public Set<SpecimenTypeDesignation> getSpecimenTypeDesignationsOfHomotypicalGroup() {
|
2483
|
return this.getHomotypicalGroup().getSpecimenTypeDesignations();
|
2484
|
}
|
2485
|
|
2486
|
//*********************** NAME TYPE DESIGNATION *********************************************//
|
2487
|
|
2488
|
/**
|
2489
|
* Returns the set of {@link NameTypeDesignation name type designations} assigned
|
2490
|
* to <i>this</i> taxon name the rank of which must be above "species".
|
2491
|
* The name type designations include all the taxon names used to typify
|
2492
|
* <i>this</i> taxon name and eventually the rejected or conserved status
|
2493
|
* of these designations.
|
2494
|
*
|
2495
|
* @see NameTypeDesignation
|
2496
|
* @see SpecimenTypeDesignation
|
2497
|
*/
|
2498
|
@Override
|
2499
|
@Transient
|
2500
|
public Set<NameTypeDesignation> getNameTypeDesignations() {
|
2501
|
Set<NameTypeDesignation> result = new HashSet<>();
|
2502
|
for (TypeDesignationBase<?> typeDesignation : this.typeDesignations){
|
2503
|
if (typeDesignation.isInstanceOf(NameTypeDesignation.class)){
|
2504
|
result.add(CdmBase.deproxy(typeDesignation, NameTypeDesignation.class));
|
2505
|
}
|
2506
|
}
|
2507
|
return result;
|
2508
|
}
|
2509
|
|
2510
|
/**
|
2511
|
* Creates and adds a new {@link NameTypeDesignation name type designation}
|
2512
|
* to <i>this</i> taxon name's set of type designations.
|
2513
|
*
|
2514
|
* @param typeSpecies the taxon name to be used as type of <i>this</i> taxon name
|
2515
|
* @param citation the reference for this new designation
|
2516
|
* @param citationMicroReference the string with the details (generally pages) within the reference
|
2517
|
* @param originalNameString the taxon name string used in the reference to assert this designation
|
2518
|
* @param isRejectedType the boolean status for a rejected name type designation
|
2519
|
* @param isConservedType the boolean status for a conserved name type designation
|
2520
|
* @param isLectoType the boolean status for a lectotype name type designation
|
2521
|
* @param isNotDesignated the boolean status for a name type designation without name type
|
2522
|
* @param addToAllHomotypicNames the boolean indicating whether the name type designation should be
|
2523
|
* added to all taxon names of the homotypical group this taxon name belongs to
|
2524
|
* @return
|
2525
|
* @see #getNameTypeDesignations()
|
2526
|
* @see NameTypeDesignation
|
2527
|
* @see TypeDesignationBase#isNotDesignated()
|
2528
|
*/
|
2529
|
@Override
|
2530
|
public NameTypeDesignation addNameTypeDesignation(TaxonName typeSpecies,
|
2531
|
Reference citation,
|
2532
|
String citationMicroReference,
|
2533
|
String originalNameString,
|
2534
|
NameTypeDesignationStatus status,
|
2535
|
boolean isRejectedType,
|
2536
|
boolean isConservedType,
|
2537
|
/*boolean isLectoType, */
|
2538
|
boolean isNotDesignated,
|
2539
|
boolean addToAllHomotypicNames) {
|
2540
|
NameTypeDesignation nameTypeDesignation = new NameTypeDesignation(typeSpecies, citation, citationMicroReference, originalNameString, status, isRejectedType, isConservedType, isNotDesignated);
|
2541
|
//nameTypeDesignation.setLectoType(isLectoType);
|
2542
|
addTypeDesignation(nameTypeDesignation, addToAllHomotypicNames);
|
2543
|
return nameTypeDesignation;
|
2544
|
}
|
2545
|
|
2546
|
/**
|
2547
|
* Creates and adds a new {@link NameTypeDesignation name type designation}
|
2548
|
* to <i>this</i> taxon name's set of type designations.
|
2549
|
*
|
2550
|
* @param typeSpecies the taxon name to be used as type of <i>this</i> taxon name
|
2551
|
* @param citation the reference for this new designation
|
2552
|
* @param citationMicroReference the string with the details (generally pages) within the reference
|
2553
|
* @param originalNameString the taxon name string used in the reference to assert this designation
|
2554
|
* @param status the name type designation status
|
2555
|
* @param addToAllHomotypicNames the boolean indicating whether the name type designation should be
|
2556
|
* added to all taxon names of the homotypical group this taxon name belongs to
|
2557
|
* @return
|
2558
|
* @see #getNameTypeDesignations()
|
2559
|
* @see NameTypeDesignation
|
2560
|
* @see TypeDesignationBase#isNotDesignated()
|
2561
|
*/
|
2562
|
@Override
|
2563
|
public NameTypeDesignation addNameTypeDesignation(TaxonName typeSpecies,
|
2564
|
Reference citation,
|
2565
|
String citationMicroReference,
|
2566
|
String originalNameString,
|
2567
|
NameTypeDesignationStatus status,
|
2568
|
boolean addToAllHomotypicNames) {
|
2569
|
NameTypeDesignation nameTypeDesignation = new NameTypeDesignation(typeSpecies, status, citation, citationMicroReference, originalNameString);
|
2570
|
addTypeDesignation(nameTypeDesignation, addToAllHomotypicNames);
|
2571
|
return nameTypeDesignation;
|
2572
|
}
|
2573
|
|
2574
|
//*********************** SPECIMEN TYPE DESIGNATION *********************************************//
|
2575
|
|
2576
|
/**
|
2577
|
* Returns the set of {@link SpecimenTypeDesignation specimen type designations}
|
2578
|
* that typify <i>this</i> taxon name.
|
2579
|
*/
|
2580
|
@Override
|
2581
|
@Transient
|
2582
|
public Set<SpecimenTypeDesignation> getSpecimenTypeDesignations() {
|
2583
|
Set<SpecimenTypeDesignation> result = new HashSet<>();
|
2584
|
for (TypeDesignationBase<?> typeDesignation : this.typeDesignations){
|
2585
|
if (typeDesignation.isInstanceOf(SpecimenTypeDesignation.class)){
|
2586
|
result.add(CdmBase.deproxy(typeDesignation, SpecimenTypeDesignation.class));
|
2587
|
}
|
2588
|
}
|
2589
|
return result;
|
2590
|
}
|
2591
|
|
2592
|
|
2593
|
/**
|
2594
|
* Creates and adds a new {@link SpecimenTypeDesignation specimen type designation}
|
2595
|
* to <i>this</i> taxon name's set of type designations.
|
2596
|
*
|
2597
|
* @param typeSpecimen the specimen to be used as a type for <i>this</i> taxon name
|
2598
|
* @param status the specimen type designation status
|
2599
|
* @param citation the reference for this new specimen type designation
|
2600
|
* @param citationMicroReference the string with the details (generally pages) within the reference
|
2601
|
* @param originalNameString the taxon name used in the reference to assert this designation
|
2602
|
* @param isNotDesignated the boolean status for a specimen type designation without specimen type
|
2603
|
* @param addToAllHomotypicNames the boolean indicating whether the specimen type designation should be
|
2604
|
* added to all taxon names of the homotypical group the typified
|
2605
|
* taxon name belongs to
|
2606
|
* @return
|
2607
|
* @see #getSpecimenTypeDesignations()
|
2608
|
* @see SpecimenTypeDesignationStatus
|
2609
|
* @see SpecimenTypeDesignation
|
2610
|
* @see TypeDesignationBase#isNotDesignated()
|
2611
|
*/
|
2612
|
@Override
|
2613
|
public SpecimenTypeDesignation addSpecimenTypeDesignation(DerivedUnit typeSpecimen,
|
2614
|
SpecimenTypeDesignationStatus status,
|
2615
|
Reference citation,
|
2616
|
String citationMicroReference,
|
2617
|
String originalNameString,
|
2618
|
boolean isNotDesignated,
|
2619
|
boolean addToAllHomotypicNames) {
|
2620
|
SpecimenTypeDesignation specimenTypeDesignation = new SpecimenTypeDesignation(typeSpecimen, status, citation, citationMicroReference, originalNameString, isNotDesignated);
|
2621
|
addTypeDesignation(specimenTypeDesignation, addToAllHomotypicNames);
|
2622
|
return specimenTypeDesignation;
|
2623
|
}
|
2624
|
|
2625
|
//used by merge strategy
|
2626
|
private boolean addTypeDesignation(TypeDesignationBase typeDesignation){
|
2627
|
return addTypeDesignation(typeDesignation, true);
|
2628
|
}
|
2629
|
|
2630
|
/**
|
2631
|
* Adds a {@link TypeDesignationBase type designation} to <code>this</code> taxon name's set of type designations
|
2632
|
*
|
2633
|
* @param typeDesignation the typeDesignation to be added to <code>this</code> taxon name
|
2634
|
* @param addToAllNames the boolean indicating whether the type designation should be
|
2635
|
* added to all taxon names of the homotypical group the typified
|
2636
|
* taxon name belongs to
|
2637
|
* @return true if the operation was successful
|
2638
|
*
|
2639
|
* @throws IllegalArgumentException if the type designation already has typified names, an {@link IllegalArgumentException exception}
|
2640
|
* is thrown. We do this to prevent a type designation to be used for multiple taxon names.
|
2641
|
*
|
2642
|
*/
|
2643
|
@Override
|
2644
|
public boolean addTypeDesignation(TypeDesignationBase<?> typeDesignation, boolean addToAllNames){
|
2645
|
//currently typeDesignations are not persisted with the homotypical group
|
2646
|
//so explicit adding to the homotypical group is not necessary.
|
2647
|
if (typeDesignation != null){
|
2648
|
checkHomotypicalGroup(typeDesignation);
|
2649
|
this.typeDesignations.add(typeDesignation);
|
2650
|
typeDesignation.addTypifiedName(this);
|
2651
|
|
2652
|
if (addToAllNames){
|
2653
|
for (TaxonName taxonName : this.getHomotypicalGroup().getTypifiedNames()){
|
2654
|
if (taxonName != this){
|
2655
|
taxonName.addTypeDesignation(typeDesignation, false);
|
2656
|
}
|
2657
|
}
|
2658
|
}
|
2659
|
}
|
2660
|
return true;
|
2661
|
}
|
2662
|
|
2663
|
/**
|
2664
|
* Throws an Exception this type designation already has typified names from another homotypical group.
|
2665
|
* @param typeDesignation
|
2666
|
*/
|
2667
|
private void checkHomotypicalGroup(TypeDesignationBase<?> typeDesignation) {
|
2668
|
if(typeDesignation.getTypifiedNames().size() > 0){
|
2669
|
Set<HomotypicalGroup> groups = new HashSet<>();
|
2670
|
Set<TaxonName> names = typeDesignation.getTypifiedNames();
|
2671
|
for (TaxonName taxonName: names){
|
2672
|
groups.add(taxonName.getHomotypicalGroup());
|
2673
|
}
|
2674
|
if (groups.size() > 1){
|
2675
|
throw new IllegalArgumentException("TypeDesignation already has typified names from another homotypical group.");
|
2676
|
}
|
2677
|
}
|
2678
|
}
|
2679
|
|
2680
|
|
2681
|
|
2682
|
//*********************** HOMOTYPICAL GROUP *********************************************//
|
2683
|
|
2684
|
|
2685
|
/**
|
2686
|
* Returns the {@link HomotypicalGroup homotypical group} to which
|
2687
|
* <i>this</i> taxon name belongs. A homotypical group represents all taxon names
|
2688
|
* that share the same types.
|
2689
|
*
|
2690
|
* @see HomotypicalGroup
|
2691
|
*/
|
2692
|
|
2693
|
@Override
|
2694
|
public HomotypicalGroup getHomotypicalGroup() {
|
2695
|
if (homotypicalGroup == null){
|
2696
|
homotypicalGroup = new HomotypicalGroup();
|
2697
|
homotypicalGroup.typifiedNames.add(this);
|
2698
|
}
|
2699
|
return homotypicalGroup;
|
2700
|
}
|
2701
|
|
2702
|
/**
|
2703
|
* @see #getHomotypicalGroup()
|
2704
|
*/
|
2705
|
@Override
|
2706
|
public void setHomotypicalGroup(HomotypicalGroup homotypicalGroup) {
|
2707
|
if (homotypicalGroup == null){
|
2708
|
throw new IllegalArgumentException("HomotypicalGroup of name should never be null but was set to 'null'");
|
2709
|
}
|
2710
|
/*if (this.homotypicalGroup != null){
|
2711
|
this.homotypicalGroup.removeTypifiedName(this, false);
|
2712
|
}*/
|
2713
|
this.homotypicalGroup = homotypicalGroup;
|
2714
|
if (!this.homotypicalGroup.typifiedNames.contains(this)){
|
2715
|
this.homotypicalGroup.addTypifiedName(this);
|
2716
|
}
|
2717
|
}
|
2718
|
|
2719
|
|
2720
|
|
2721
|
// *************************************************************************//
|
2722
|
|
2723
|
/**
|
2724
|
* Returns the complete string containing the
|
2725
|
* {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation() nomenclatural reference citation}
|
2726
|
* and the {@link #getNomenclaturalMicroReference() details} assigned to <i>this</i> taxon name.
|
2727
|
*
|
2728
|
* @return the string containing the nomenclatural reference of <i>this</i> taxon name
|
2729
|
* @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation()
|
2730
|
* @see #getNomenclaturalReference()
|
2731
|
* @see #getNomenclaturalMicroReference()
|
2732
|
*/
|
2733
|
@Override
|
2734
|
@Transient
|
2735
|
public String getCitationString(){
|
2736
|
return getNomenclaturalReference().getNomenclaturalCitation(getNomenclaturalMicroReference());
|
2737
|
}
|
2738
|
|
2739
|
/**
|
2740
|
* Returns the parsing problems
|
2741
|
* @return
|
2742
|
*/
|
2743
|
@Override
|
2744
|
public List<ParserProblem> getParsingProblems(){
|
2745
|
return ParserProblem.warningList(this.parsingProblem);
|
2746
|
}
|
2747
|
|
2748
|
/**
|
2749
|
* Returns the string containing the publication date (generally only year)
|
2750
|
* of the {@link #getNomenclaturalReference() nomenclatural reference} for <i>this</i> taxon name, null if there is
|
2751
|
* no nomenclatural reference.
|
2752
|
*
|
2753
|
* @return the string containing the publication date of <i>this</i> taxon name
|
2754
|
* @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getYear()
|
2755
|
*/
|
2756
|
@Override
|
2757
|
@Transient
|
2758
|
@ValidTaxonomicYear(groups=Level3.class)
|
2759
|
public String getReferenceYear(){
|
2760
|
if (this.getNomenclaturalReference() != null ){
|
2761
|
return this.getNomenclaturalReference().getYear();
|
2762
|
}else{
|
2763
|
return null;
|
2764
|
}
|
2765
|
}
|
2766
|
|
2767
|
/**
|
2768
|
* Returns the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
|
2769
|
* In this context a taxon base means the use of a taxon name by a reference
|
2770
|
* either as a {@link eu.etaxonomy.cdm.model.taxon.Taxon taxon} ("accepted/correct" name) or
|
2771
|
* as a (junior) {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
|
2772
|
* A taxon name can be used by several distinct {@link eu.etaxonomy.cdm.model.reference.Reference references} but only once
|
2773
|
* within a taxonomic treatment (identified by one reference).
|
2774
|
*
|
2775
|
* @see #getTaxa()
|
2776
|
* @see #getSynonyms()
|
2777
|
*/
|
2778
|
@Override
|
2779
|
public Set<TaxonBase> getTaxonBases() {
|
2780
|
if(taxonBases == null) {
|
2781
|
this.taxonBases = new HashSet<TaxonBase>();
|
2782
|
}
|
2783
|
return this.taxonBases;
|
2784
|
}
|
2785
|
|
2786
|
/**
|
2787
|
* Adds a new {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon base}
|
2788
|
* to the set of taxon bases using <i>this</i> taxon name.
|
2789
|
*
|
2790
|
* @param taxonBase the taxon base to be added
|
2791
|
* @see #getTaxonBases()
|
2792
|
* @see #removeTaxonBase(TaxonBase)
|
2793
|
*/
|
2794
|
//TODO protected
|
2795
|
@Override
|
2796
|
public void addTaxonBase(TaxonBase taxonBase){
|
2797
|
Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonName.class});
|
2798
|
ReflectionUtils.makeAccessible(method);
|
2799
|
ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {this});
|
2800
|
taxonBases.add(taxonBase);
|
2801
|
}
|
2802
|
/**
|
2803
|
* Removes one element from the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
|
2804
|
*
|
2805
|
* @param taxonBase the taxon base which should be removed from the corresponding set
|
2806
|
* @see #getTaxonBases()
|
2807
|
* @see #addTaxonBase(TaxonBase)
|
2808
|
*/
|
2809
|
@Override
|
2810
|
public void removeTaxonBase(TaxonBase taxonBase){
|
2811
|
Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonName.class});
|
2812
|
ReflectionUtils.makeAccessible(method);
|
2813
|
ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {null});
|
2814
|
|
2815
|
|
2816
|
}
|
2817
|
|
2818
|
/**
|
2819
|
* Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Taxon taxa} ("accepted/correct" names according to any
|
2820
|
* reference) that are based on <i>this</i> taxon name. This set is a subset of
|
2821
|
* the set returned by getTaxonBases().
|
2822
|
*
|
2823
|
* @see eu.etaxonomy.cdm.model.taxon.Taxon
|
2824
|
* @see #getTaxonBases()
|
2825
|
* @see #getSynonyms()
|
2826
|
*/
|
2827
|
@Override
|
2828
|
@Transient
|
2829
|
public Set<Taxon> getTaxa(){
|
2830
|
Set<Taxon> result = new HashSet<>();
|
2831
|
for (TaxonBase taxonBase : this.taxonBases){
|
2832
|
if (taxonBase instanceof Taxon){
|
2833
|
result.add((Taxon)taxonBase);
|
2834
|
}
|
2835
|
}
|
2836
|
return result;
|
2837
|
}
|
2838
|
|
2839
|
/**
|
2840
|
* Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Synonym (junior) synonyms} (according to any
|
2841
|
* reference) that are based on <i>this</i> taxon name. This set is a subset of
|
2842
|
* the set returned by getTaxonBases().
|
2843
|
*
|
2844
|
* @see eu.etaxonomy.cdm.model.taxon.Synonym
|
2845
|
* @see #getTaxonBases()
|
2846
|
* @see #getTaxa()
|
2847
|
*/
|
2848
|
@Override
|
2849
|
@Transient
|
2850
|
public Set<Synonym> getSynonyms() {
|
2851
|
Set<Synonym> result = new HashSet<>();
|
2852
|
for (TaxonBase taxonBase : this.taxonBases){
|
2853
|
if (taxonBase instanceof Synonym){
|
2854
|
result.add((Synonym)taxonBase);
|
2855
|
}
|
2856
|
}
|
2857
|
return result;
|
2858
|
}
|
2859
|
|
2860
|
//******* REGISTRATION *****************/
|
2861
|
|
2862
|
@Override
|
2863
|
public Set<Registration> getRegistrations() {
|
2864
|
return this.registrations;
|
2865
|
}
|
2866
|
|
2867
|
|
2868
|
// ************* RELATIONSHIPS *****************************/
|
2869
|
|
2870
|
|
2871
|
/**
|
2872
|
* Returns the hybrid child relationships ordered by relationship type, or if equal
|
2873
|
* by title cache of the related names.
|
2874
|
* @see #getHybridParentRelations()
|
2875
|
*/
|
2876
|
@Override
|
2877
|
@Transient
|
2878
|
public List<HybridRelationship> getOrderedChildRelationships(){
|
2879
|
List<HybridRelationship> result = new ArrayList<HybridRelationship>();
|
2880
|
result.addAll(this.hybridChildRelations);
|
2881
|
Collections.sort(result);
|
2882
|
Collections.reverse(result);
|
2883
|
return result;
|
2884
|
|
2885
|
}
|
2886
|
|
2887
|
|
2888
|
/**
|
2889
|
* Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship}
|
2890
|
* to <i>this</i> botanical name. A HybridRelationship may be of type
|
2891
|
* "is first/second parent" or "is male/female parent". By invoking this
|
2892
|
* method <i>this</i> botanical name becomes a hybrid child of the parent
|
2893
|
* botanical name.
|
2894
|
*
|
2895
|
* @param parentName the botanical name of the parent for this new hybrid name relationship
|
2896
|
* @param type the type of this new name relationship
|
2897
|
* @param ruleConsidered the string which specifies the rule on which this name relationship is based
|
2898
|
* @return
|
2899
|
* @see #addHybridChild(BotanicalName, HybridRelationshipType,String )
|
2900
|
* @see #getRelationsToThisName()
|
2901
|
* @see #getNameRelations()
|
2902
|
* @see #addRelationshipFromName(TaxonName, NameRelationshipType, String)
|
2903
|
* @see #addNameRelationship(NameRelationship)
|
2904
|
*/
|
2905
|
@Override
|
2906
|
public HybridRelationship addHybridParent(INonViralName parentName, HybridRelationshipType type, String ruleConsidered){
|
2907
|
return new HybridRelationship(this, parentName, type, ruleConsidered);
|
2908
|
}
|
2909
|
|
2910
|
/**
|
2911
|
* Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship}
|
2912
|
* to <i>this</i> botanical name. A HybridRelationship may be of type
|
2913
|
* "is first/second parent" or "is male/female parent". By invoking this
|
2914
|
* method <i>this</i> botanical name becomes a parent of the hybrid child
|
2915
|
* botanical name.
|
2916
|
*
|
2917
|
* @param childName the botanical name of the child for this new hybrid name relationship
|
2918
|
* @param type the type of this new name relationship
|
2919
|
* @param ruleConsidered the string which specifies the rule on which this name relationship is based
|
2920
|
* @return
|
2921
|
* @see #addHybridParent(BotanicalName, HybridRelationshipType,String )
|
2922
|
* @see #getRelationsToThisName()
|
2923
|
* @see #getNameRelations()
|
2924
|
* @see #addRelationshipFromName(TaxonName, NameRelationshipType, String)
|
2925
|
* @see #addNameRelationship(NameRelationship)
|
2926
|
*/
|
2927
|
@Override
|
2928
|
public HybridRelationship addHybridChild(INonViralName childName, HybridRelationshipType type, String ruleConsidered){
|
2929
|
return new HybridRelationship(childName, this, type, ruleConsidered);
|
2930
|
}
|
2931
|
|
2932
|
@Override
|
2933
|
public void removeHybridChild(INonViralName child) {
|
2934
|
Set<HybridRelationship> hybridRelationships = new HashSet<HybridRelationship>();
|
2935
|
hybridRelationships.addAll(this.getHybridChildRelations());
|
2936
|
hybridRelationships.addAll(this.getHybridParentRelations());
|
2937
|
for(HybridRelationship hybridRelationship : hybridRelationships) {
|
2938
|
// remove name relationship from this side
|
2939
|
if (hybridRelationship.getParentName().equals(this) && hybridRelationship.getHybridName().equals(child)) {
|
2940
|
this.removeHybridRelationship(hybridRelationship);
|
2941
|
}
|
2942
|
}
|
2943
|
}
|
2944
|
|
2945
|
@Override
|
2946
|
public void removeHybridParent(INonViralName parent) {
|
2947
|
Set<HybridRelationship> hybridRelationships = new HashSet<HybridRelationship>();
|
2948
|
hybridRelationships.addAll(this.getHybridChildRelations());
|
2949
|
hybridRelationships.addAll(this.getHybridParentRelations());
|
2950
|
for(HybridRelationship hybridRelationship : hybridRelationships) {
|
2951
|
// remove name relationship from this side
|
2952
|
if (hybridRelationship.getParentName().equals(parent) && hybridRelationship.getHybridName().equals(this)) {
|
2953
|
this.removeHybridRelationship(hybridRelationship);
|
2954
|
}
|
2955
|
}
|
2956
|
}
|
2957
|
|
2958
|
|
2959
|
|
2960
|
// *********** DESCRIPTIONS *************************************
|
2961
|
|
2962
|
/**
|
2963
|
* Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
|
2964
|
* to <i>this</i> taxon name. A taxon name description is a piece of information
|
2965
|
* concerning the taxon name like for instance the content of its first
|
2966
|
* publication (protolog) or a picture of this publication.
|
2967
|
*
|
2968
|
* @see #addDescription(TaxonNameDescription)
|
2969
|
* @see #removeDescription(TaxonNameDescription)
|
2970
|
* @see eu.etaxonomy.cdm.model.description.TaxonNameDescription
|
2971
|
*/
|
2972
|
@Override
|
2973
|
public Set<TaxonNameDescription> getDescriptions() {
|
2974
|
return descriptions;
|
2975
|
}
|
2976
|
|
2977
|
/**
|
2978
|
* Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name description}
|
2979
|
* to the set of taxon name descriptions assigned to <i>this</i> taxon name. The
|
2980
|
* content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute} of the
|
2981
|
* taxon name description itself will be replaced with <i>this</i> taxon name.
|
2982
|
*
|
2983
|
* @param description the taxon name description to be added
|
2984
|
* @see #getDescriptions()
|
2985
|
* @see #removeDescription(TaxonNameDescription)
|
2986
|
*/
|
2987
|
@Override
|
2988
|
public void addDescription(TaxonNameDescription description) {
|
2989
|
java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonName.class);
|
2990
|
ReflectionUtils.makeAccessible(field);
|
2991
|
ReflectionUtils.setField(field, description, this);
|
2992
|
descriptions.add(description);
|
2993
|
}
|
2994
|
/**
|
2995
|
* Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
|
2996
|
* to <i>this</i> taxon name. The content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute}
|
2997
|
* of the description itself will be set to "null".
|
2998
|
*
|
2999
|
* @param description the taxon name description which should be removed
|
3000
|
* @see #getDescriptions()
|
3001
|
* @see #addDescription(TaxonNameDescription)
|
3002
|
* @see eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName()
|
3003
|
*/
|
3004
|
@Override
|
3005
|
public void removeDescription(TaxonNameDescription description) {
|
3006
|
java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonName.class);
|
3007
|
ReflectionUtils.makeAccessible(field);
|
3008
|
ReflectionUtils.setField(field, description, null);
|
3009
|
descriptions.remove(description);
|
3010
|
}
|
3011
|
|
3012
|
// *********** HOMOTYPIC GROUP METHODS **************************************************
|
3013
|
|
3014
|
@Override
|
3015
|
@Transient
|
3016
|
public void mergeHomotypicGroups(TaxonName name){
|
3017
|
this.getHomotypicalGroup().merge(name.getHomotypicalGroup());
|
3018
|
//HomotypicalGroup thatGroup = name.homotypicalGroup;
|
3019
|
name.setHomotypicalGroup(this.homotypicalGroup);
|
3020
|
}
|
3021
|
|
3022
|
/**
|
3023
|
* Returns the boolean value indicating whether a given taxon name belongs
|
3024
|
* to the same {@link HomotypicalGroup homotypical group} as <i>this</i> taxon name (true)
|
3025
|
* or not (false). Returns "true" only if the homotypical groups of both
|
3026
|
* taxon names exist and if they are identical.
|
3027
|
*
|
3028
|
* @param homoTypicName the taxon name the homotypical group of which is to be checked
|
3029
|
* @return the boolean value of the check
|
3030
|
* @see HomotypicalGroup
|
3031
|
*/
|
3032
|
@Override
|
3033
|
@Transient
|
3034
|
public boolean isHomotypic(TaxonName homoTypicName) {
|
3035
|
if (homoTypicName == null) {
|
3036
|
return false;
|
3037
|
}
|
3038
|
HomotypicalGroup homotypicGroup = homoTypicName.getHomotypicalGroup();
|
3039
|
if (homotypicGroup == null || this.getHomotypicalGroup() == null) {
|
3040
|
return false;
|
3041
|
}
|
3042
|
if (homotypicGroup.equals(this.getHomotypicalGroup())) {
|
3043
|
return true;
|
3044
|
}
|
3045
|
return false;
|
3046
|
}
|
3047
|
|
3048
|
|
3049
|
/**
|
3050
|
* Checks whether name is a basionym for ALL names
|
3051
|
* in its homotypical group.
|
3052
|
* Returns <code>false</code> if there are no other names in the group
|
3053
|
* @param name
|
3054
|
* @return
|
3055
|
*/
|
3056
|
@Override
|
3057
|
@Transient
|
3058
|
public boolean isGroupsBasionym() {
|
3059
|
if (homotypicalGroup == null){
|
3060
|
homotypicalGroup = HomotypicalGroup.NewInstance();
|
3061
|
homotypicalGroup.addTypifiedName(this);
|
3062
|
}
|
3063
|
Set<TaxonName> typifiedNames = homotypicalGroup.getTypifiedNames();
|
3064
|
|
3065
|
// Check whether there are any other names in the group
|
3066
|
if (typifiedNames.size() == 1) {
|
3067
|
return false;
|
3068
|
}
|
3069
|
|
3070
|
for (TaxonName taxonName : typifiedNames) {
|
3071
|
if (!taxonName.equals(this)) {
|
3072
|
if (! isBasionymFor(taxonName)) {
|
3073
|
return false;
|
3074
|
}
|
3075
|
}
|
3076
|
}
|
3077
|
return true;
|
3078
|
}
|
3079
|
|
3080
|
/**
|
3081
|
* Checks whether a basionym relationship exists between fromName and toName.
|
3082
|
*
|
3083
|
* @param fromName
|
3084
|
* @param toName
|
3085
|
* @return
|
3086
|
*/
|
3087
|
@Override
|
3088
|
@Transient
|
3089
|
public boolean isBasionymFor(TaxonName newCombinationName) {
|
3090
|
Set<NameRelationship> relations = newCombinationName.getRelationsToThisName();
|
3091
|
for (NameRelationship relation : relations) {
|
3092
|
if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
|
3093
|
relation.getFromName().equals(this)) {
|
3094
|
return true;
|
3095
|
}
|
3096
|
}
|
3097
|
return false;
|
3098
|
}
|
3099
|
|
3100
|
/**
|
3101
|
* Creates a basionym relationship to all other names in this names homotypical
|
3102
|
* group.
|
3103
|
*
|
3104
|
* @see HomotypicalGroup.setGroupBasionym(TaxonName basionymName)
|
3105
|
*/
|
3106
|
@Override
|
3107
|
@Transient
|
3108
|
public void makeGroupsBasionym() {
|
3109
|
this.homotypicalGroup.setGroupBasionym(this);
|
3110
|
}
|
3111
|
|
3112
|
|
3113
|
//********* Rank comparison shortcuts ********************//
|
3114
|
/**
|
3115
|
* Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
|
3116
|
* taxon name is higher than the genus rank (true) or not (false).
|
3117
|
* Suprageneric non viral names are monomials.
|
3118
|
* Returns false if rank is null.
|
3119
|
*
|
3120
|
* @see #isGenus()
|
3121
|
* @see #isInfraGeneric()
|
3122
|
* @see #isSpecies()
|
3123
|
* @see #isInfraSpecific()
|
3124
|
*/
|
3125
|
@Override
|
3126
|
@Transient
|
3127
|
public boolean isSupraGeneric() {
|
3128
|
if (rank == null){
|
3129
|
return false;
|
3130
|
}
|
3131
|
return getRank().isSupraGeneric();
|
3132
|
}
|
3133
|
/**
|
3134
|
* Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
|
3135
|
* taxon name is the genus rank (true) or not (false). Non viral names with
|
3136
|
* genus rank are monomials. Returns false if rank is null.
|
3137
|
*
|
3138
|
* @see #isSupraGeneric()
|
3139
|
* @see #isInfraGeneric()
|
3140
|
* @see #isSpecies()
|
3141
|
* @see #isInfraSpecific()
|
3142
|
*/
|
3143
|
@Override
|
3144
|
@Transient
|
3145
|
public boolean isGenus() {
|
3146
|
if (rank == null){
|
3147
|
return false;
|
3148
|
}
|
3149
|
return getRank().isGenus();
|
3150
|
}
|
3151
|
|
3152
|
@Override
|
3153
|
@Transient
|
3154
|
public boolean isGenusOrSupraGeneric() {
|
3155
|
return isGenus()|| isSupraGeneric();
|
3156
|
}
|
3157
|
/**
|
3158
|
* Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
|
3159
|
* taxon name is higher than the species rank and lower than the
|
3160
|
* genus rank (true) or not (false). Infrageneric non viral names are
|
3161
|
* binomials. Returns false if rank is null.
|
3162
|
*
|
3163
|
* @see #isSupraGeneric()
|
3164
|
* @see #isGenus()
|
3165
|
* @see #isSpecies()
|
3166
|
* @see #isInfraSpecific()
|
3167
|
*/
|
3168
|
@Override
|
3169
|
@Transient
|
3170
|
public boolean isInfraGeneric() {
|
3171
|
if (rank == null){
|
3172
|
return false;
|
3173
|
}
|
3174
|
return getRank().isInfraGeneric();
|
3175
|
}
|
3176
|
|
3177
|
/**
|
3178
|
* Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
|
3179
|
* taxon name is higher than the species rank (true) or not (false).
|
3180
|
* Returns false if rank is null.
|
3181
|
*
|
3182
|
* @see #isGenus()
|
3183
|
* @see #isInfraGeneric()
|
3184
|
* @see #isSpecies()
|
3185
|
* @see #isInfraSpecific()
|
3186
|
*/
|
3187
|
@Override
|
3188
|
@Transient
|
3189
|
public boolean isSupraSpecific(){
|
3190
|
if (rank == null) {
|
3191
|
return false;
|
3192
|
}
|
3193
|
return getRank().isHigher(Rank.SPECIES());
|
3194
|
}
|
3195
|
|
3196
|
/**
|
3197
|
* Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
|
3198
|
* taxon name is the species rank (true) or not (false). Non viral names
|
3199
|
* with species rank are binomials.
|
3200
|
* Returns false if rank is null.
|
3201
|
*
|
3202
|
* @see #isSupraGeneric()
|
3203
|
* @see #isGenus()
|
3204
|
* @see #isInfraGeneric()
|
3205
|
* @see #isInfraSpecific()
|
3206
|
*/
|
3207
|
@Override
|
3208
|
@Transient
|
3209
|
public boolean isSpecies() {
|
3210
|
if (rank == null){
|
3211
|
return false;
|
3212
|
}
|
3213
|
return getRank().isSpecies();
|
3214
|
}
|
3215
|
/**
|
3216
|
* Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
|
3217
|
* taxon name is lower than the species rank (true) or not (false).
|
3218
|
* Infraspecific non viral names are trinomials.
|
3219
|
* Returns false if rank is null.
|
3220
|
*
|
3221
|
* @see #isSupraGeneric()
|
3222
|
* @see #isGenus()
|
3223
|
* @see #isInfraGeneric()
|
3224
|
* @see #isSpecies()
|
3225
|
*/
|
3226
|
@Override
|
3227
|
@Transient
|
3228
|
public boolean isInfraSpecific() {
|
3229
|
if (rank == null){
|
3230
|
return false;
|
3231
|
}
|
3232
|
return getRank().isInfraSpecific();
|
3233
|
}
|
3234
|
|
3235
|
/**
|
3236
|
* Returns true if this name's rank indicates a rank that aggregates species like species
|
3237
|
* aggregates or species groups, false otherwise. This methods currently returns false
|
3238
|
* for all user defined ranks.
|
3239
|
*
|
3240
|
*@see Rank#isSpeciesAggregate()
|
3241
|
*
|
3242
|
* @return
|
3243
|
*/
|
3244
|
@Override
|
3245
|
@Transient
|
3246
|
public boolean isSpeciesAggregate() {
|
3247
|
if (rank == null){
|
3248
|
return false;
|
3249
|
}
|
3250
|
return getRank().isSpeciesAggregate();
|
3251
|
}
|
3252
|
|
3253
|
|
3254
|
/**
|
3255
|
* Returns null as the {@link NomenclaturalCode nomenclatural code} that governs
|
3256
|
* the construction of <i>this</i> taxon name since there is no specific
|
3257
|
* nomenclatural code defined. The real implementention takes place in the
|
3258
|
* subclasses {@link IBacterialName BacterialName},
|
3259
|
* {@link IBotanicalName BotanicalName}, {@link ICultivarPlantName CultivarPlantName} and
|
3260
|
* {@link IZoologicalName ZoologicalName}. Each taxon name is governed by one
|
3261
|
* and only one nomenclatural code.
|
3262
|
*
|
3263
|
* @return null
|
3264
|
* @see #isCodeCompliant()
|
3265
|
* @see #getHasProblem()
|
3266
|
* @deprecated use {@link #getNameType()} instead
|
3267
|
*/
|
3268
|
@Override
|
3269
|
@Deprecated
|
3270
|
@Transient
|
3271
|
@java.beans.Transient
|
3272
|
public NomenclaturalCode getNomenclaturalCode() {
|
3273
|
return nameType;
|
3274
|
}
|
3275
|
|
3276
|
|
3277
|
/**
|
3278
|
* Generates and returns the string with the scientific name of <i>this</i>
|
3279
|
* taxon name (only non viral taxon names can be generated from their
|
3280
|
* components). This string may be stored in the inherited
|
3281
|
* {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
|
3282
|
* This method overrides the generic and inherited
|
3283
|
* {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() method} from
|
3284
|
* {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
|
3285
|
*
|
3286
|
* @return the string with the composed name of this non viral taxon name with authorship (and maybe year)
|
3287
|
* @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
|
3288
|
* @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
|
3289
|
*/
|
3290
|
// @Override
|
3291
|
// public abstract String generateTitle();
|
3292
|
|
3293
|
/**
|
3294
|
* Creates a basionym relationship between this name and
|
3295
|
* each name in its homotypic group.
|
3296
|
*
|
3297
|
* @param basionymName
|
3298
|
*/
|
3299
|
@Override
|
3300
|
@Transient
|
3301
|
public void setAsGroupsBasionym() {
|
3302
|
|
3303
|
HomotypicalGroup homotypicalGroup = this.getHomotypicalGroup();
|
3304
|
if (homotypicalGroup == null) {
|
3305
|
return;
|
3306
|
}
|
3307
|
|
3308
|
Set<NameRelationship> relations = new HashSet<NameRelationship>();
|
3309
|
Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
|
3310
|
|
3311
|
for(TaxonName typifiedName : homotypicalGroup.getTypifiedNames()){
|
3312
|
|
3313
|
Set<NameRelationship> nameRelations = typifiedName.getRelationsFromThisName();
|
3314
|
|
3315
|
for(NameRelationship nameRelation : nameRelations){
|
3316
|
relations.add(nameRelation);
|
3317
|
}
|
3318
|
}
|
3319
|
|
3320
|
for (NameRelationship relation : relations) {
|
3321
|
|
3322
|
// If this is a basionym relation, and toName is in the homotypical group,
|
3323
|
// remove the relationship.
|
3324
|
if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
|
3325
|
relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
|
3326
|
removeRelations.add(relation);
|
3327
|
}
|
3328
|
}
|
3329
|
|
3330
|
// Removing relations from a set through which we are iterating causes a
|
3331
|
// ConcurrentModificationException. Therefore, we delete the targeted
|
3332
|
// relations in a second step.
|
3333
|
for (NameRelationship relation : removeRelations) {
|
3334
|
this.removeNameRelationship(relation);
|
3335
|
}
|
3336
|
|
3337
|
for (TaxonName name : homotypicalGroup.getTypifiedNames()) {
|
3338
|
if (!name.equals(this)) {
|
3339
|
|
3340
|
// First check whether the relationship already exists
|
3341
|
if (!this.isBasionymFor(name)) {
|
3342
|
|
3343
|
// Then create it
|
3344
|
name.addRelationshipFromName(this,
|
3345
|
NameRelationshipType.BASIONYM(), null);
|
3346
|
}
|
3347
|
}
|
3348
|
}
|
3349
|
}
|
3350
|
|
3351
|
/**
|
3352
|
* Removes basionym relationship between this name and
|
3353
|
* each name in its homotypic group.
|
3354
|
*
|
3355
|
* @param basionymName
|
3356
|
*/
|
3357
|
@Override
|
3358
|
@Transient
|
3359
|
public void removeAsGroupsBasionym() {
|
3360
|
|
3361
|
HomotypicalGroup homotypicalGroup = this.getHomotypicalGroup();
|
3362
|
|
3363
|
if (homotypicalGroup == null) {
|
3364
|
return;
|
3365
|
}
|
3366
|
|
3367
|
Set<NameRelationship> relations = new HashSet<NameRelationship>();
|
3368
|
Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
|
3369
|
|
3370
|
for(TaxonName typifiedName : homotypicalGroup.getTypifiedNames()){
|
3371
|
|
3372
|
Set<NameRelationship> nameRelations = typifiedName.getRelationsFromThisName();
|
3373
|
|
3374
|
for(NameRelationship nameRelation : nameRelations){
|
3375
|
relations.add(nameRelation);
|
3376
|
}
|
3377
|
}
|
3378
|
|
3379
|
for (NameRelationship relation : relations) {
|
3380
|
|
3381
|
// If this is a basionym relation, and toName is in the homotypical group,
|
3382
|
// and fromName is basionymName, remove the relationship.
|
3383
|
if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
|
3384
|
relation.getFromName().equals(this) &&
|
3385
|
relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
|
3386
|
removeRelations.add(relation);
|
3387
|
}
|
3388
|
}
|
3389
|
|
3390
|
// Removing relations from a set through which we are iterating causes a
|
3391
|
// ConcurrentModificationException. Therefore, we delete the targeted
|
3392
|
// relations in a second step.
|
3393
|
for (NameRelationship relation : removeRelations) {
|
3394
|
this.removeNameRelationship(relation);
|
3395
|
}
|
3396
|
}
|
3397
|
|
3398
|
|
3399
|
/**
|
3400
|
* Defines the last part of the name.
|
3401
|
* This is for infraspecific taxa, the infraspecific epithet,
|
3402
|
* for species the specific epithet, for infageneric taxa the infrageneric epithet
|
3403
|
* else the genusOrUninomial.
|
3404
|
* However, the result does not depend on the rank (which may be not correctly set
|
3405
|
* in case of dirty data) but returns the first name part which is not blank
|
3406
|
* considering the above order.
|
3407
|
* @return the first not blank name part in reverse order
|
3408
|
*/
|
3409
|
@Override
|
3410
|
public String getLastNamePart() {
|
3411
|
String result =
|
3412
|
StringUtils.isNotBlank(this.getInfraSpecificEpithet())?
|
3413
|
this.getInfraSpecificEpithet() :
|
3414
|
StringUtils.isNotBlank(this.getSpecificEpithet()) ?
|
3415
|
this.getSpecificEpithet():
|
3416
|
StringUtils.isNotBlank(this.getInfraGenericEpithet()) ?
|
3417
|
this.getInfraGenericEpithet():
|
3418
|
this.getGenusOrUninomial();
|
3419
|
return result;
|
3420
|
}
|
3421
|
|
3422
|
/**
|
3423
|
* {@inheritDoc}
|
3424
|
*/
|
3425
|
@Override
|
3426
|
public boolean isHybridName() {
|
3427
|
return this.isMonomHybrid() || this.isBinomHybrid() || this.isTrinomHybrid();
|
3428
|
}
|
3429
|
|
3430
|
/**
|
3431
|
* {@inheritDoc}
|
3432
|
*/
|
3433
|
@Override
|
3434
|
public boolean isHybrid() {
|
3435
|
return this.isHybridName() || this.isHybridFormula();
|
3436
|
}
|
3437
|
|
3438
|
// ***************** COMPARE ********************************/
|
3439
|
|
3440
|
@Override
|
3441
|
public int compareToName(TaxonName otherName){
|
3442
|
|
3443
|
int result = 0;
|
3444
|
|
3445
|
if (otherName == null) {
|
3446
|
throw new NullPointerException("Cannot compare to null.");
|
3447
|
}
|
3448
|
|
3449
|
//other
|
3450
|
otherName = deproxy(otherName);
|
3451
|
String otherNameCache = otherName.getNameCache();
|
3452
|
String otherTitleCache = otherName.getTitleCache();
|
3453
|
//TODO is this really necessary, is it not the normal way how name cache is filled for autonyms?
|
3454
|
if (otherName.isAutonym()){
|
3455
|
boolean isProtected = otherName.isProtectedNameCache();
|
3456
|
String oldNameCache = otherName.getNameCache();
|
3457
|
otherName.setProtectedNameCache(false);
|
3458
|
otherName.setNameCache(null, false);
|
3459
|
otherNameCache = otherName.getNameCache();
|
3460
|
otherName.setNameCache(oldNameCache, isProtected);
|
3461
|
}
|
3462
|
|
3463
|
//this
|
3464
|
String thisNameCache = this.getNameCache();
|
3465
|
String thisTitleCache = this.getTitleCache();
|
3466
|
|
3467
|
if (this.isAutonym()){
|
3468
|
boolean isProtected = this.isProtectedNameCache();
|
3469
|
String oldNameCache = this.getNameCache();
|
3470
|
this.setProtectedNameCache(false);
|
3471
|
this.setNameCache(null, false);
|
3472
|
thisNameCache = this.getNameCache();
|
3473
|
this.setNameCache(oldNameCache, isProtected);
|
3474
|
}
|
3475
|
|
3476
|
|
3477
|
// Compare name cache of taxon names
|
3478
|
if (CdmUtils.isNotBlank(otherNameCache) && CdmUtils.isNotBlank(thisNameCache)) {
|
3479
|
thisNameCache = normalizeName(thisNameCache);
|
3480
|
otherNameCache = normalizeName(otherNameCache);
|
3481
|
result = thisNameCache.compareTo(otherNameCache);
|
3482
|
}
|
3483
|
|
3484
|
// Compare title cache of taxon names
|
3485
|
if (result == 0){
|
3486
|
if ( (CdmUtils.isNotBlank(otherTitleCache) || CdmUtils.isNotBlank(thisTitleCache))) {
|
3487
|
thisTitleCache = normalizeName(thisTitleCache);
|
3488
|
otherTitleCache = normalizeName(otherTitleCache);
|
3489
|
result = CdmUtils.nullSafeCompareTo(thisTitleCache, otherTitleCache);
|
3490
|
}
|
3491
|
}
|
3492
|
|
3493
|
return result;
|
3494
|
}
|
3495
|
|
3496
|
static final String HYBRID_SIGN = UTF8.HYBRID.toString();
|
3497
|
static final String QUOT_SIGN = "[\\u02BA\\u0022\\u0022]";
|
3498
|
|
3499
|
/**
|
3500
|
* @param thisNameCache
|
3501
|
* @param HYBRID_SIGN
|
3502
|
* @param QUOT_SIGN
|
3503
|
* @return
|
3504
|
*/
|
3505
|
private String normalizeName(String thisNameCache) {
|
3506
|
thisNameCache = thisNameCache.replaceAll(HYBRID_SIGN, "");
|
3507
|
thisNameCache = thisNameCache.replaceAll(QUOT_SIGN, "");
|
3508
|
return thisNameCache;
|
3509
|
}
|
3510
|
|
3511
|
// ********************** INTERFACES ********************************************/
|
3512
|
|
3513
|
/**
|
3514
|
* Method to cast a interfaced name to a concrete name.
|
3515
|
* The method includes a deproxy to guarantee that no
|
3516
|
* class cast exception is thrown.
|
3517
|
*
|
3518
|
* @see #castAndDeproxy(Set)
|
3519
|
* @param interfacedName
|
3520
|
* @return
|
3521
|
*/
|
3522
|
public static TaxonName castAndDeproxy(ITaxonNameBase interfacedName){
|
3523
|
return deproxy(interfacedName, TaxonName.class);
|
3524
|
}
|
3525
|
|
3526
|
/**
|
3527
|
* Method to cast a set of interfaced names to concrete namex.
|
3528
|
* The method includes a deproxy to guarantee that no
|
3529
|
* class cast exception is thrown.
|
3530
|
*
|
3531
|
* @see #castAndDeproxy(ITaxonNameBase)
|
3532
|
* @param naminterfacedNames
|
3533
|
* @return
|
3534
|
*/
|
3535
|
public static Set<TaxonName> castAndDeproxy(Set<ITaxonNameBase> naminterfacedNames) {
|
3536
|
Set<TaxonName> result = new HashSet<>();
|
3537
|
for (ITaxonNameBase naminterfacedName : naminterfacedNames){
|
3538
|
result.add(castAndDeproxy(naminterfacedName));
|
3539
|
}
|
3540
|
return result;
|
3541
|
}
|
3542
|
|
3543
|
//************************ isType ***********************************************/
|
3544
|
|
3545
|
/**
|
3546
|
* @return
|
3547
|
*/
|
3548
|
@Override
|
3549
|
public boolean isNonViral() {
|
3550
|
return nameType.isNonViral();
|
3551
|
}
|
3552
|
|
3553
|
@Override
|
3554
|
public boolean isZoological(){
|
3555
|
return nameType.isZoological();
|
3556
|
}
|
3557
|
@Override
|
3558
|
public boolean isBotanical() {
|
3559
|
return nameType.isBotanical();
|
3560
|
}
|
3561
|
@Override
|
3562
|
public boolean isCultivar() {
|
3563
|
return nameType.isCultivar();
|
3564
|
}
|
3565
|
@Override
|
3566
|
public boolean isBacterial() {
|
3567
|
return nameType.isBacterial();
|
3568
|
}
|
3569
|
@Override
|
3570
|
public boolean isViral() {
|
3571
|
return nameType.isViral();
|
3572
|
}
|
3573
|
|
3574
|
// *********************** CACHES ***************************************************/
|
3575
|
|
3576
|
|
3577
|
@Override
|
3578
|
public boolean updateCaches() {
|
3579
|
boolean result = updateAuthorshipCache();
|
3580
|
result |= updateNameCache();
|
3581
|
result |= super.updateCaches();
|
3582
|
result |= updateFullTitleCache();
|
3583
|
return result;
|
3584
|
}
|
3585
|
|
3586
|
/**
|
3587
|
* Updates the authorship cache if any changes appeared in the authors nomenclatural caches.
|
3588
|
* Deletes the titleCache and the fullTitleCache if not protected and if any change has happened.
|
3589
|
* @return <code>true</code> if something changed
|
3590
|
*/
|
3591
|
private boolean updateAuthorshipCache() {
|
3592
|
//updates the authorship cache if necessary and via the listener updates all higher caches
|
3593
|
if (protectedAuthorshipCache == false){
|
3594
|
String oldCache = this.authorshipCache;
|
3595
|
String newCache = cacheStrategy.getAuthorshipCache(this);
|
3596
|
if (!CdmUtils.nullSafeEqual(oldCache, newCache)){
|
3597
|
this.setAuthorshipCache(null, false);
|
3598
|
this.getAuthorshipCache();
|
3599
|
return true;
|
3600
|
}
|
3601
|
}
|
3602
|
return false;
|
3603
|
}
|
3604
|
|
3605
|
/**
|
3606
|
* @return
|
3607
|
*/
|
3608
|
private boolean updateNameCache() {
|
3609
|
//updates the name cache if necessary and via the listener updates all higher caches
|
3610
|
if (protectedNameCache == false){
|
3611
|
String oldCache = this.nameCache;
|
3612
|
String newCache = cacheStrategy.getNameCache(this);
|
3613
|
if (!CdmUtils.nullSafeEqual(oldCache, newCache)){
|
3614
|
this.setNameCache(null, false);
|
3615
|
this.getNameCache();
|
3616
|
return true;
|
3617
|
}
|
3618
|
}
|
3619
|
return false;
|
3620
|
}
|
3621
|
|
3622
|
|
3623
|
/**
|
3624
|
* @return
|
3625
|
*/
|
3626
|
private boolean updateFullTitleCache() {
|
3627
|
if (protectedFullTitleCache == false){
|
3628
|
String oldCache = this.fullTitleCache;
|
3629
|
String newCache = cacheStrategy.getFullTitleCache(this);
|
3630
|
if (!CdmUtils.nullSafeEqual(oldCache, newCache)){
|
3631
|
this.setFullTitleCache(null, false);
|
3632
|
this.getFullTitleCache();
|
3633
|
return true;
|
3634
|
}
|
3635
|
}
|
3636
|
return false;
|
3637
|
}
|
3638
|
|
3639
|
|
3640
|
//*********************** CLONE ********************************************************/
|
3641
|
|
3642
|
/**
|
3643
|
* Clones <i>this</i> taxon name. This is a shortcut that enables to create
|
3644
|
* a new instance that differs only slightly from <i>this</i> taxon name by
|
3645
|
* modifying only some of the attributes.<BR><BR>
|
3646
|
* Usages of this name in a taxon concept are <b>not</b> cloned.<BR>
|
3647
|
* <b>The name gets a newly created homotypical group</b><BR>
|
3648
|
* (CAUTION: this behavior needs to be discussed and may change in future).<BR><BR>
|
3649
|
* {@link TaxonNameDescription Name descriptions} are cloned and not reused.<BR>
|
3650
|
* {@link TypeDesignationBase Type designations} are cloned and not reused.<BR>
|
3651
|
*
|
3652
|
* @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
|
3653
|
* @see java.lang.Object#clone()
|
3654
|
*/
|
3655
|
@Override
|
3656
|
public Object clone() {
|
3657
|
TaxonName result;
|
3658
|
try {
|
3659
|
result = (TaxonName)super.clone();
|
3660
|
|
3661
|
//taxonBases -> empty
|
3662
|
result.taxonBases = new HashSet<>();
|
3663
|
|
3664
|
//empty caches
|
3665
|
if (! protectedFullTitleCache){
|
3666
|
result.fullTitleCache = null;
|
3667
|
}
|
3668
|
|
3669
|
//descriptions
|
3670
|
result.descriptions = new HashSet<>();
|
3671
|
for (TaxonNameDescription taxonNameDescription : getDescriptions()){
|
3672
|
TaxonNameDescription newDescription = (TaxonNameDescription)taxonNameDescription.clone();
|
3673
|
result.descriptions.add(newDescription);
|
3674
|
}
|
3675
|
|
3676
|
//status
|
3677
|
result.status = new HashSet<>();
|
3678
|
for (NomenclaturalStatus nomenclaturalStatus : getStatus()){
|
3679
|
NomenclaturalStatus newStatus = (NomenclaturalStatus)nomenclaturalStatus.clone();
|
3680
|
result.status.add(newStatus);
|
3681
|
}
|
3682
|
|
3683
|
|
3684
|
//to relations
|
3685
|
result.relationsToThisName = new HashSet<>();
|
3686
|
for (NameRelationship toRelationship : getRelationsToThisName()){
|
3687
|
NameRelationship newRelationship = (NameRelationship)toRelationship.clone();
|
3688
|
newRelationship.setRelatedTo(result);
|
3689
|
result.relationsToThisName.add(newRelationship);
|
3690
|
}
|
3691
|
|
3692
|
//from relations
|
3693
|
result.relationsFromThisName = new HashSet<>();
|
3694
|
for (NameRelationship fromRelationship : getRelationsFromThisName()){
|
3695
|
NameRelationship newRelationship = (NameRelationship)fromRelationship.clone();
|
3696
|
newRelationship.setRelatedFrom(result);
|
3697
|
result.relationsFromThisName.add(newRelationship);
|
3698
|
}
|
3699
|
|
3700
|
//type designations
|
3701
|
result.typeDesignations = new HashSet<>();
|
3702
|
for (TypeDesignationBase<?> typeDesignation : getTypeDesignations()){
|
3703
|
TypeDesignationBase<?> newDesignation = (TypeDesignationBase<?>)typeDesignation.clone();
|
3704
|
this.removeTypeDesignation(newDesignation);
|
3705
|
result.addTypeDesignation(newDesignation, false);
|
3706
|
}
|
3707
|
|
3708
|
//homotypicalGroup
|
3709
|
//TODO still needs to be discussed
|
3710
|
result.homotypicalGroup = HomotypicalGroup.NewInstance();
|
3711
|
result.homotypicalGroup.addTypifiedName(this);
|
3712
|
|
3713
|
|
3714
|
//HybridChildRelations
|
3715
|
result.hybridChildRelations = new HashSet<>();
|
3716
|
for (HybridRelationship hybridRelationship : getHybridChildRelations()){
|
3717
|
HybridRelationship newChildRelationship = (HybridRelationship)hybridRelationship.clone();
|
3718
|
newChildRelationship.setRelatedTo(result);
|
3719
|
result.hybridChildRelations.add(newChildRelationship);
|
3720
|
}
|
3721
|
|
3722
|
//HybridParentRelations
|
3723
|
result.hybridParentRelations = new HashSet<>();
|
3724
|
for (HybridRelationship hybridRelationship : getHybridParentRelations()){
|
3725
|
HybridRelationship newParentRelationship = (HybridRelationship)hybridRelationship.clone();
|
3726
|
newParentRelationship.setRelatedFrom(result);
|
3727
|
result.hybridParentRelations.add(newParentRelationship);
|
3728
|
}
|
3729
|
|
3730
|
//empty caches
|
3731
|
if (! protectedNameCache){
|
3732
|
result.nameCache = null;
|
3733
|
}
|
3734
|
|
3735
|
//empty caches
|
3736
|
if (! protectedAuthorshipCache){
|
3737
|
result.authorshipCache = null;
|
3738
|
}
|
3739
|
|
3740
|
//no changes to: appendedPharse, nomenclaturalReference,
|
3741
|
//nomenclaturalMicroReference, parsingProblem, problemEnds, problemStarts
|
3742
|
//protectedFullTitleCache, rank
|
3743
|
//basionamyAuthorship, combinationAuthorship, exBasionymAuthorship, exCombinationAuthorship
|
3744
|
//genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
|
3745
|
//protectedAuthorshipCache, protectedNameCache,
|
3746
|
//binomHybrid, monomHybrid, trinomHybrid, hybridFormula,
|
3747
|
//acronym
|
3748
|
//subGenusAuthorship, nameApprobation
|
3749
|
//anamorphic
|
3750
|
//cultivarName
|
3751
|
return result;
|
3752
|
} catch (CloneNotSupportedException e) {
|
3753
|
logger.warn("Object does not implement cloneable");
|
3754
|
e.printStackTrace();
|
3755
|
return null;
|
3756
|
}
|
3757
|
|
3758
|
}
|
3759
|
|
3760
|
|
3761
|
}
|
3762
|
|