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