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
|
|
13
|
import java.beans.PropertyChangeEvent;
|
14
|
import java.beans.PropertyChangeListener;
|
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.JoinColumn;
|
26
|
import javax.persistence.ManyToOne;
|
27
|
import javax.persistence.OneToMany;
|
28
|
import javax.persistence.Transient;
|
29
|
import javax.validation.constraints.NotNull;
|
30
|
import javax.validation.constraints.Pattern;
|
31
|
import javax.xml.bind.annotation.XmlAccessType;
|
32
|
import javax.xml.bind.annotation.XmlAccessorType;
|
33
|
import javax.xml.bind.annotation.XmlElement;
|
34
|
import javax.xml.bind.annotation.XmlElementWrapper;
|
35
|
import javax.xml.bind.annotation.XmlIDREF;
|
36
|
import javax.xml.bind.annotation.XmlRootElement;
|
37
|
import javax.xml.bind.annotation.XmlSchemaType;
|
38
|
import javax.xml.bind.annotation.XmlType;
|
39
|
|
40
|
import org.apache.commons.lang.StringUtils;
|
41
|
import org.apache.log4j.Logger;
|
42
|
import org.hibernate.annotations.Cascade;
|
43
|
import org.hibernate.annotations.CascadeType;
|
44
|
import org.hibernate.envers.Audited;
|
45
|
import org.hibernate.search.annotations.Analyze;
|
46
|
import org.hibernate.search.annotations.Analyzer;
|
47
|
import org.hibernate.search.annotations.Field;
|
48
|
import org.hibernate.search.annotations.Fields;
|
49
|
import org.hibernate.search.annotations.Index;
|
50
|
import org.hibernate.search.annotations.Indexed;
|
51
|
import org.hibernate.search.annotations.IndexedEmbedded;
|
52
|
import org.hibernate.search.annotations.Store;
|
53
|
import org.hibernate.validator.constraints.NotEmpty;
|
54
|
import org.springframework.beans.factory.annotation.Configurable;
|
55
|
|
56
|
import eu.etaxonomy.cdm.common.CdmUtils;
|
57
|
import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
|
58
|
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
|
59
|
import eu.etaxonomy.cdm.model.common.CdmBase;
|
60
|
import eu.etaxonomy.cdm.model.common.RelationshipBase;
|
61
|
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
|
62
|
import eu.etaxonomy.cdm.model.reference.Reference;
|
63
|
import eu.etaxonomy.cdm.strategy.cache.name.CacheUpdate;
|
64
|
import eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy;
|
65
|
import eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy;
|
66
|
import eu.etaxonomy.cdm.strategy.match.Match;
|
67
|
import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
|
68
|
import eu.etaxonomy.cdm.strategy.match.MatchMode;
|
69
|
import eu.etaxonomy.cdm.strategy.merge.Merge;
|
70
|
import eu.etaxonomy.cdm.strategy.merge.MergeMode;
|
71
|
import eu.etaxonomy.cdm.validation.Level2;
|
72
|
import eu.etaxonomy.cdm.validation.Level3;
|
73
|
import eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank;
|
74
|
import eu.etaxonomy.cdm.validation.annotation.NameMustHaveAuthority;
|
75
|
import eu.etaxonomy.cdm.validation.annotation.NoDuplicateNames;
|
76
|
import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
|
77
|
|
78
|
/**
|
79
|
* The taxon name class for all non viral taxa. Parenthetical authorship is derived
|
80
|
* from basionym relationship. The scientific name including author strings and
|
81
|
* maybe year can be stored as a string in the inherited {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
|
82
|
* The year itself is an information obtained from the {@link eu.etaxonomy.cdm.model.reference.Reference#getYear() nomenclatural reference}.
|
83
|
* The scientific name string without author strings and year can be stored in the {@link #getNameCache() nameCache} attribute.
|
84
|
* <P>
|
85
|
* This class corresponds partially to: <ul>
|
86
|
* <li> TaxonName according to the TDWG ontology
|
87
|
* <li> ScientificName and CanonicalName according to the TCS
|
88
|
* <li> ScientificName according to the ABCD schema
|
89
|
* </ul>
|
90
|
*
|
91
|
* @author m.doering
|
92
|
* @version 1.0
|
93
|
* @created 08-Nov-2007 13:06:39
|
94
|
*/
|
95
|
@XmlAccessorType(XmlAccessType.FIELD)
|
96
|
@XmlType(name = "NonViralName", propOrder = {
|
97
|
"nameCache",
|
98
|
"genusOrUninomial",
|
99
|
"infraGenericEpithet",
|
100
|
"specificEpithet",
|
101
|
"infraSpecificEpithet",
|
102
|
"combinationAuthorship",
|
103
|
"exCombinationAuthorship",
|
104
|
"basionymAuthorship",
|
105
|
"exBasionymAuthorship",
|
106
|
"authorshipCache",
|
107
|
"protectedAuthorshipCache",
|
108
|
"protectedNameCache",
|
109
|
"hybridParentRelations",
|
110
|
"hybridChildRelations",
|
111
|
"hybridFormula",
|
112
|
"monomHybrid",
|
113
|
"binomHybrid",
|
114
|
"trinomHybrid"
|
115
|
})
|
116
|
@XmlRootElement(name = "NonViralName")
|
117
|
@Entity
|
118
|
@Indexed(index = "eu.etaxonomy.cdm.model.name.TaxonNameBase")
|
119
|
@Audited
|
120
|
@Configurable
|
121
|
@CorrectEpithetsForRank(groups = Level2.class)
|
122
|
@NameMustHaveAuthority(groups = Level2.class)
|
123
|
@NoDuplicateNames(groups = Level3.class)
|
124
|
public class NonViralName<T extends NonViralName> extends TaxonNameBase<T, INonViralNameCacheStrategy> implements Cloneable{
|
125
|
private static final long serialVersionUID = 4441110073881088033L;
|
126
|
private static final Logger logger = Logger.getLogger(NonViralName.class);
|
127
|
|
128
|
@XmlElement(name = "NameCache")
|
129
|
@Fields({
|
130
|
@Field(name = "nameCache_tokenized"),
|
131
|
@Field(store = Store.YES, index = Index.YES, analyze = Analyze.YES)
|
132
|
})
|
133
|
@Analyzer(impl = org.apache.lucene.analysis.core.KeywordAnalyzer.class)
|
134
|
@Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.DEFINED,
|
135
|
cacheReplacedProperties={"genusOrUninomial", "infraGenericEpithet", "specificEpithet", "infraSpecificEpithet"} )
|
136
|
@NotEmpty(groups = Level2.class) // implicitly NotNull
|
137
|
@Column(length=255)
|
138
|
private String nameCache;
|
139
|
|
140
|
@XmlElement(name = "ProtectedNameCache")
|
141
|
@CacheUpdate(value="nameCache")
|
142
|
protected boolean protectedNameCache;
|
143
|
|
144
|
@XmlElement(name = "GenusOrUninomial")
|
145
|
@Field(analyze = Analyze.YES,indexNullAs=Field.DEFAULT_NULL_TOKEN)
|
146
|
@Match(MatchMode.EQUAL_REQUIRED)
|
147
|
@CacheUpdate("nameCache")
|
148
|
@Column(length=255)
|
149
|
@Pattern(regexp = "[A-Z][a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class, message="{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForUninomial.message}")
|
150
|
@NullOrNotEmpty
|
151
|
@NotNull(groups = Level2.class)
|
152
|
private String genusOrUninomial;
|
153
|
|
154
|
@XmlElement(name = "InfraGenericEpithet")
|
155
|
@Field(analyze = Analyze.YES,indexNullAs=Field.DEFAULT_NULL_TOKEN)
|
156
|
@CacheUpdate("nameCache")
|
157
|
//TODO Val #3379
|
158
|
// @NullOrNotEmpty
|
159
|
@Column(length=255)
|
160
|
@Pattern(regexp = "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class,message="{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}")
|
161
|
private String infraGenericEpithet;
|
162
|
|
163
|
@XmlElement(name = "SpecificEpithet")
|
164
|
@Field(analyze = Analyze.YES,indexNullAs=Field.DEFAULT_NULL_TOKEN)
|
165
|
@CacheUpdate("nameCache")
|
166
|
//TODO Val #3379
|
167
|
// @NullOrNotEmpty
|
168
|
@Column(length=255)
|
169
|
@Pattern(regexp = "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class, message = "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}")
|
170
|
private String specificEpithet;
|
171
|
|
172
|
@XmlElement(name = "InfraSpecificEpithet")
|
173
|
@Field(analyze = Analyze.YES,indexNullAs=Field.DEFAULT_NULL_TOKEN)
|
174
|
@CacheUpdate("nameCache")
|
175
|
//TODO Val #3379
|
176
|
// @NullOrNotEmpty
|
177
|
@Column(length=255)
|
178
|
@Pattern(regexp = "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class, message = "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}")
|
179
|
private String infraSpecificEpithet;
|
180
|
|
181
|
@XmlElement(name = "CombinationAuthorship", type = TeamOrPersonBase.class)
|
182
|
@XmlIDREF
|
183
|
@XmlSchemaType(name = "IDREF")
|
184
|
@ManyToOne(fetch = FetchType.LAZY)
|
185
|
// @Target(TeamOrPersonBase.class)
|
186
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
187
|
@JoinColumn(name="combinationAuthorship_id")
|
188
|
@CacheUpdate("authorshipCache")
|
189
|
@IndexedEmbedded
|
190
|
private TeamOrPersonBase<?> combinationAuthorship;
|
191
|
|
192
|
@XmlElement(name = "ExCombinationAuthorship", type = TeamOrPersonBase.class)
|
193
|
@XmlIDREF
|
194
|
@XmlSchemaType(name = "IDREF")
|
195
|
@ManyToOne(fetch = FetchType.LAZY)
|
196
|
// @Target(TeamOrPersonBase.class)
|
197
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
198
|
@JoinColumn(name="exCombinationAuthorship_id")
|
199
|
@CacheUpdate("authorshipCache")
|
200
|
@IndexedEmbedded
|
201
|
private TeamOrPersonBase<?> exCombinationAuthorship;
|
202
|
|
203
|
@XmlElement(name = "BasionymAuthorship", type = TeamOrPersonBase.class)
|
204
|
@XmlIDREF
|
205
|
@XmlSchemaType(name = "IDREF")
|
206
|
@ManyToOne(fetch = FetchType.LAZY)
|
207
|
// @Target(TeamOrPersonBase.class)
|
208
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
209
|
@JoinColumn(name="basionymAuthorship_id")
|
210
|
@CacheUpdate("authorshipCache")
|
211
|
@IndexedEmbedded
|
212
|
private TeamOrPersonBase<?> basionymAuthorship;
|
213
|
|
214
|
@XmlElement(name = "ExBasionymAuthorship", type = TeamOrPersonBase.class)
|
215
|
@XmlIDREF
|
216
|
@XmlSchemaType(name = "IDREF")
|
217
|
@ManyToOne(fetch = FetchType.LAZY)
|
218
|
// @Target(TeamOrPersonBase.class)
|
219
|
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
|
220
|
@JoinColumn(name="exBasionymAuthorship_id")
|
221
|
@CacheUpdate("authorshipCache")
|
222
|
@IndexedEmbedded
|
223
|
private TeamOrPersonBase<?> exBasionymAuthorship;
|
224
|
|
225
|
@XmlElement(name = "AuthorshipCache")
|
226
|
@Fields({
|
227
|
@Field(name = "authorshipCache_tokenized"),
|
228
|
@Field(analyze = Analyze.NO)
|
229
|
})
|
230
|
@Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.DEFINED,
|
231
|
cacheReplacedProperties={"combinationAuthorship", "basionymAuthorship", "exCombinationAuthorship", "exBasionymAuthorship"} )
|
232
|
//TODO Val #3379
|
233
|
// @NotNull
|
234
|
@Column(length=255)
|
235
|
@Pattern(regexp = "^[A-Za-z0-9 \\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-\\&\\,\\(\\)\\.]+$", groups=Level2.class, message = "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForAuthority.message}")
|
236
|
private String authorshipCache;
|
237
|
|
238
|
@XmlElement(name = "ProtectedAuthorshipCache")
|
239
|
@CacheUpdate("authorshipCache")
|
240
|
protected boolean protectedAuthorshipCache;
|
241
|
|
242
|
@XmlElementWrapper(name = "HybridRelationsFromThisName")
|
243
|
@XmlElement(name = "HybridRelationsFromThisName")
|
244
|
@OneToMany(mappedBy="relatedFrom", fetch = FetchType.LAZY)
|
245
|
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
|
246
|
@Merge(MergeMode.RELATION)
|
247
|
@NotNull
|
248
|
private Set<HybridRelationship> hybridParentRelations = new HashSet<HybridRelationship>();
|
249
|
|
250
|
@XmlElementWrapper(name = "HybridRelationsToThisName")
|
251
|
@XmlElement(name = "HybridRelationsToThisName")
|
252
|
@OneToMany(mappedBy="relatedTo", fetch = FetchType.LAZY, orphanRemoval=true) //a hybrid relation can be deleted automatically if the child is deleted.
|
253
|
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
|
254
|
@Merge(MergeMode.RELATION)
|
255
|
@NotNull
|
256
|
private Set<HybridRelationship> hybridChildRelations = new HashSet<HybridRelationship>();
|
257
|
|
258
|
//if set: this name is a hybrid formula (a hybrid that does not have an own name) and no other hybrid flags may be set. A
|
259
|
//hybrid name may not have either an authorteam nor other name components.
|
260
|
@XmlElement(name ="IsHybridFormula")
|
261
|
@CacheUpdate("nameCache")
|
262
|
private boolean hybridFormula = false;
|
263
|
|
264
|
@XmlElement(name ="IsMonomHybrid")
|
265
|
@CacheUpdate("nameCache")
|
266
|
private boolean monomHybrid = false;
|
267
|
|
268
|
@XmlElement(name ="IsBinomHybrid")
|
269
|
@CacheUpdate("nameCache")
|
270
|
private boolean binomHybrid = false;
|
271
|
|
272
|
@XmlElement(name ="IsTrinomHybrid")
|
273
|
@CacheUpdate("nameCache")
|
274
|
private boolean trinomHybrid = false;
|
275
|
|
276
|
/**
|
277
|
* Creates a new non viral taxon name instance
|
278
|
* only containing its {@link common.Rank rank} and
|
279
|
* the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}.
|
280
|
*
|
281
|
* @param rank the rank to be assigned to <i>this</i> non viral taxon name
|
282
|
* @see #NewInstance(Rank, HomotypicalGroup)
|
283
|
* @see #NonViralName(Rank, HomotypicalGroup)
|
284
|
* @see #NonViralName()
|
285
|
* @see #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup)
|
286
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy
|
287
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy
|
288
|
* @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
|
289
|
*/
|
290
|
public static NonViralName NewInstance(Rank rank){
|
291
|
return new NonViralName(rank, null);
|
292
|
}
|
293
|
|
294
|
/**
|
295
|
* Creates a new non viral taxon name instance
|
296
|
* only containing its {@link common.Rank rank},
|
297
|
* its {@link HomotypicalGroup homotypical group} and
|
298
|
* the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}.
|
299
|
* The new non viral taxon name instance will be also added to the set of
|
300
|
* non viral taxon names belonging to this homotypical group.
|
301
|
*
|
302
|
* @param rank the rank to be assigned to <i>this</i> non viral taxon name
|
303
|
* @param homotypicalGroup the homotypical group to which <i>this</i> non viral taxon name belongs
|
304
|
* @see #NewInstance(Rank)
|
305
|
* @see #NonViralName(Rank, HomotypicalGroup)
|
306
|
* @see #NonViralName()
|
307
|
* @see #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup)
|
308
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy
|
309
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy
|
310
|
* @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
|
311
|
*/
|
312
|
public static NonViralName NewInstance(Rank rank, HomotypicalGroup homotypicalGroup){
|
313
|
return new NonViralName(rank, homotypicalGroup);
|
314
|
}
|
315
|
|
316
|
// ************************** CONSTRUCTORS *************/
|
317
|
|
318
|
//needed by hibernate
|
319
|
/**
|
320
|
* Class constructor: creates a new non viral taxon name instance
|
321
|
* only containing the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}.
|
322
|
*
|
323
|
* @see #NonViralName(Rank, HomotypicalGroup)
|
324
|
* @see #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup)
|
325
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy
|
326
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy
|
327
|
* @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
|
328
|
*/
|
329
|
protected NonViralName(){
|
330
|
super();
|
331
|
setNameCacheStrategy();
|
332
|
}
|
333
|
|
334
|
/**
|
335
|
* Class constructor: creates a new non viral taxon name instance
|
336
|
* only containing its {@link Rank rank},
|
337
|
* its {@link HomotypicalGroup homotypical group} and
|
338
|
* the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}.
|
339
|
* The new non viral taxon name instance will be also added to the set of
|
340
|
* non viral taxon names belonging to this homotypical group.
|
341
|
*
|
342
|
* @param rank the rank to be assigned to <i>this</i> non viral taxon name
|
343
|
* @param homotypicalGroup the homotypical group to which <i>this</i> non viral taxon name belongs
|
344
|
* @see #NonViralName()
|
345
|
* @see #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup)
|
346
|
* @see #NewInstance(Rank, HomotypicalGroup)
|
347
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy
|
348
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy
|
349
|
* @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
|
350
|
*/
|
351
|
protected NonViralName(Rank rank, HomotypicalGroup homotypicalGroup) {
|
352
|
super(rank, homotypicalGroup);
|
353
|
setNameCacheStrategy();
|
354
|
}
|
355
|
/**
|
356
|
* Class constructor: creates a new non viral taxon name instance
|
357
|
* containing its {@link Rank rank},
|
358
|
* its {@link HomotypicalGroup homotypical group},
|
359
|
* its scientific name components, its {@link eu.etaxonomy.cdm.model.agent.TeamOrPersonBase author(team)},
|
360
|
* its {@link eu.etaxonomy.cdm.model.reference.Reference nomenclatural reference} and
|
361
|
* the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}.
|
362
|
* The new non viral taxon name instance will be also added to the set of
|
363
|
* non viral taxon names belonging to this homotypical group.
|
364
|
*
|
365
|
* @param rank the rank to be assigned to <i>this</i> non viral taxon name
|
366
|
* @param genusOrUninomial the string for <i>this</i> non viral taxon name
|
367
|
* if its rank is genus or higher or for the genus part
|
368
|
* if its rank is lower than genus
|
369
|
* @param infraGenericEpithet the string for the first epithet of
|
370
|
* <i>this</i> non viral taxon name if its rank is lower than genus
|
371
|
* and higher than species aggregate
|
372
|
* @param specificEpithet the string for the first epithet of
|
373
|
* <i>this</i> non viral taxon name if its rank is species aggregate or lower
|
374
|
* @param infraSpecificEpithet the string for the second epithet of
|
375
|
* <i>this</i> non viral taxon name if its rank is lower than species
|
376
|
* @param combinationAuthorship the author or the team who published <i>this</i> non viral taxon name
|
377
|
* @param nomenclaturalReference the nomenclatural reference where <i>this</i> non viral taxon name was published
|
378
|
* @param nomenclMicroRef the string with the details for precise location within the nomenclatural reference
|
379
|
* @param homotypicalGroup the homotypical group to which <i>this</i> non viral taxon name belongs
|
380
|
* @see #NonViralName()
|
381
|
* @see #NonViralName(Rank, HomotypicalGroup)
|
382
|
* @see #NewInstance(Rank, HomotypicalGroup)
|
383
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy
|
384
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy
|
385
|
* @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
|
386
|
*/
|
387
|
protected NonViralName(Rank rank, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, TeamOrPersonBase combinationAuthorship, INomenclaturalReference nomenclaturalReference, String nomenclMicroRef, HomotypicalGroup homotypicalGroup) {
|
388
|
super(rank, homotypicalGroup);
|
389
|
setNameCacheStrategy();
|
390
|
setGenusOrUninomial(genusOrUninomial);
|
391
|
setInfraGenericEpithet (infraGenericEpithet);
|
392
|
setSpecificEpithet(specificEpithet);
|
393
|
setInfraSpecificEpithet(infraSpecificEpithet);
|
394
|
setCombinationAuthorship(combinationAuthorship);
|
395
|
setNomenclaturalReference(nomenclaturalReference);
|
396
|
this.setNomenclaturalMicroReference(nomenclMicroRef);
|
397
|
}
|
398
|
|
399
|
|
400
|
|
401
|
//**************************** METHODS **************************************/
|
402
|
|
403
|
|
404
|
private void setNameCacheStrategy(){
|
405
|
if (getClass() == NonViralName.class){
|
406
|
this.cacheStrategy = NonViralNameDefaultCacheStrategy.NewInstance();
|
407
|
}
|
408
|
}
|
409
|
|
410
|
@Override
|
411
|
public void initListener(){
|
412
|
PropertyChangeListener listener = new PropertyChangeListener() {
|
413
|
@Override
|
414
|
public void propertyChange(PropertyChangeEvent e) {
|
415
|
boolean protectedByLowerCache = false;
|
416
|
//authorship cache
|
417
|
if (fieldHasCacheUpdateProperty(e.getPropertyName(), "authorshipCache")){
|
418
|
if (protectedAuthorshipCache){
|
419
|
protectedByLowerCache = true;
|
420
|
}else{
|
421
|
authorshipCache = null;
|
422
|
}
|
423
|
}
|
424
|
|
425
|
//nameCache
|
426
|
if (fieldHasCacheUpdateProperty(e.getPropertyName(), "nameCache")){
|
427
|
if (protectedNameCache){
|
428
|
protectedByLowerCache = true;
|
429
|
}else{
|
430
|
nameCache = null;
|
431
|
}
|
432
|
}
|
433
|
//title cache
|
434
|
if (! fieldHasNoUpdateProperty(e.getPropertyName(), "titleCache")){
|
435
|
if (isProtectedTitleCache()|| protectedByLowerCache == true ){
|
436
|
protectedByLowerCache = true;
|
437
|
}else{
|
438
|
titleCache = null;
|
439
|
}
|
440
|
}
|
441
|
//full title cache
|
442
|
if (! fieldHasNoUpdateProperty(e.getPropertyName(), "fullTitleCache")){
|
443
|
if (isProtectedFullTitleCache()|| protectedByLowerCache == true ){
|
444
|
protectedByLowerCache = true;
|
445
|
}else{
|
446
|
fullTitleCache = null;
|
447
|
}
|
448
|
}
|
449
|
}
|
450
|
};
|
451
|
addPropertyChangeListener(listener); //didn't use this.addXXX to make lsid.AssemblerTest run in cdmlib-remote
|
452
|
}
|
453
|
|
454
|
private static Map<String, java.lang.reflect.Field> allFields = null;
|
455
|
@Override
|
456
|
protected Map<String, java.lang.reflect.Field> getAllFields(){
|
457
|
if (allFields == null){
|
458
|
allFields = CdmUtils.getAllFields(this.getClass(), CdmBase.class, false, false, false, true);
|
459
|
}
|
460
|
return allFields;
|
461
|
}
|
462
|
|
463
|
/**
|
464
|
* @param propertyName
|
465
|
* @param string
|
466
|
* @return
|
467
|
*/
|
468
|
private boolean fieldHasCacheUpdateProperty(String propertyName, String cacheName) {
|
469
|
java.lang.reflect.Field field;
|
470
|
try {
|
471
|
field = getAllFields().get(propertyName);
|
472
|
if (field != null){
|
473
|
CacheUpdate updateAnnotation = field.getAnnotation(CacheUpdate.class);
|
474
|
if (updateAnnotation != null){
|
475
|
for (String value : updateAnnotation.value()){
|
476
|
if (cacheName.equals(value)){
|
477
|
return true;
|
478
|
}
|
479
|
}
|
480
|
}
|
481
|
}
|
482
|
return false;
|
483
|
} catch (SecurityException e1) {
|
484
|
throw e1;
|
485
|
}
|
486
|
}
|
487
|
|
488
|
private boolean fieldHasNoUpdateProperty(String propertyName, String cacheName) {
|
489
|
java.lang.reflect.Field field;
|
490
|
//do not update fields with the same name
|
491
|
if (cacheName.equals(propertyName)){
|
492
|
return true;
|
493
|
}
|
494
|
//evaluate annotation
|
495
|
try {
|
496
|
field = getAllFields().get(propertyName);
|
497
|
if (field != null){
|
498
|
CacheUpdate updateAnnotation = field.getAnnotation(CacheUpdate.class);
|
499
|
if (updateAnnotation != null){
|
500
|
for (String value : updateAnnotation.noUpdate()){
|
501
|
if (cacheName.equals(value)){
|
502
|
return true;
|
503
|
}
|
504
|
}
|
505
|
}
|
506
|
}
|
507
|
return false;
|
508
|
} catch (SecurityException e1) {
|
509
|
throw e1;
|
510
|
}
|
511
|
}
|
512
|
|
513
|
|
514
|
/**
|
515
|
* Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that published <i>this</i> non viral
|
516
|
* taxon name.
|
517
|
*
|
518
|
* @return the nomenclatural author (team) of <i>this</i> non viral taxon name
|
519
|
* @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
|
520
|
* @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
|
521
|
*/
|
522
|
public TeamOrPersonBase<?> getCombinationAuthorship(){
|
523
|
return this.combinationAuthorship;
|
524
|
}
|
525
|
|
526
|
/**
|
527
|
* @see #getCombinationAuthorship()
|
528
|
*/
|
529
|
public void setCombinationAuthorship(TeamOrPersonBase<?> combinationAuthorship){
|
530
|
this.combinationAuthorship = combinationAuthorship;
|
531
|
}
|
532
|
|
533
|
/**
|
534
|
* Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that contributed to
|
535
|
* the publication of <i>this</i> non viral taxon name as generally stated by
|
536
|
* the {@link #getCombinationAuthorship() combination author (team)} itself.<BR>
|
537
|
* An ex-author(-team) is an author(-team) to whom a taxon name was ascribed
|
538
|
* although it is not the author(-team) of a valid publication (for instance
|
539
|
* without the validating description or diagnosis in case of a name for a
|
540
|
* new taxon). The name of this ascribed authorship, followed by "ex", may
|
541
|
* be inserted before the name(s) of the publishing author(s) of the validly
|
542
|
* published name:<BR>
|
543
|
* <i>Lilium tianschanicum</i> was described by Grubov (1977) as a new species and
|
544
|
* its name was ascribed to Ivanova; since there is no indication that
|
545
|
* Ivanova provided the validating description, the name may be cited as
|
546
|
* <i>Lilium tianschanicum</i> N. A. Ivanova ex Grubov or <i>Lilium tianschanicum</i> Grubov.
|
547
|
* <P>
|
548
|
* The presence of an author (team) of <i>this</i> non viral taxon name is a
|
549
|
* condition for the existence of an ex author (team) for <i>this</i> same name.
|
550
|
*
|
551
|
* @return the nomenclatural ex author (team) of <i>this</i> non viral taxon name
|
552
|
* @see #getCombinationAuthorship()
|
553
|
* @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
|
554
|
* @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
|
555
|
*/
|
556
|
public TeamOrPersonBase<?> getExCombinationAuthorship(){
|
557
|
return this.exCombinationAuthorship;
|
558
|
}
|
559
|
|
560
|
/**
|
561
|
* @see #getExCombinationAuthorship()
|
562
|
*/
|
563
|
public void setExCombinationAuthorship(TeamOrPersonBase<?> exCombinationAuthorship){
|
564
|
this.exCombinationAuthorship = exCombinationAuthorship;
|
565
|
}
|
566
|
|
567
|
/**
|
568
|
* Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that published the original combination
|
569
|
* on which <i>this</i> non viral taxon name is nomenclaturally based. Such an
|
570
|
* author (team) can only exist if <i>this</i> non viral taxon name is a new
|
571
|
* combination due to a taxonomical revision.
|
572
|
*
|
573
|
* @return the nomenclatural basionym author (team) of <i>this</i> non viral taxon name
|
574
|
* @see #getCombinationAuthorship()
|
575
|
* @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
|
576
|
* @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
|
577
|
*/
|
578
|
public TeamOrPersonBase<?> getBasionymAuthorship(){
|
579
|
return basionymAuthorship;
|
580
|
}
|
581
|
|
582
|
/**
|
583
|
* @see #getBasionymAuthorship()
|
584
|
*/
|
585
|
public void setBasionymAuthorship(TeamOrPersonBase<?> basionymAuthorship) {
|
586
|
this.basionymAuthorship = basionymAuthorship;
|
587
|
}
|
588
|
|
589
|
/**
|
590
|
* Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that contributed to
|
591
|
* the publication of the original combination <i>this</i> non viral taxon name is
|
592
|
* based on. This should have been generally stated by
|
593
|
* the {@link #getBasionymAuthorship() basionym author (team)} itself.
|
594
|
* The presence of a basionym author (team) of <i>this</i> non viral taxon name is a
|
595
|
* condition for the existence of an ex basionym author (team)
|
596
|
* for <i>this</i> same name.
|
597
|
*
|
598
|
* @return the nomenclatural ex basionym author (team) of <i>this</i> non viral taxon name
|
599
|
* @see #getBasionymAuthorship()
|
600
|
* @see #getExCombinationAuthorship()
|
601
|
* @see #getCombinationAuthorship()
|
602
|
* @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
|
603
|
* @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
|
604
|
*/
|
605
|
public TeamOrPersonBase<?> getExBasionymAuthorship(){
|
606
|
return exBasionymAuthorship;
|
607
|
}
|
608
|
|
609
|
/**
|
610
|
* @see #getExBasionymAuthorship()
|
611
|
*/
|
612
|
public void setExBasionymAuthorship(TeamOrPersonBase<?> exBasionymAuthorship) {
|
613
|
this.exBasionymAuthorship = exBasionymAuthorship;
|
614
|
}
|
615
|
/**
|
616
|
* Returns either the scientific name string (without authorship) for <i>this</i>
|
617
|
* non viral taxon name if its rank is genus or higher (monomial) or the string for
|
618
|
* the genus part of it if its {@link Rank rank} is lower than genus (bi- or trinomial).
|
619
|
* Genus or uninomial strings begin with an upper case letter.
|
620
|
*
|
621
|
* @return the string containing the suprageneric name, the genus name or the genus part of <i>this</i> non viral taxon name
|
622
|
* @see #getNameCache()
|
623
|
*/
|
624
|
public String getGenusOrUninomial() {
|
625
|
return genusOrUninomial;
|
626
|
}
|
627
|
|
628
|
/**
|
629
|
* @see #getGenusOrUninomial()
|
630
|
*/
|
631
|
public void setGenusOrUninomial(String genusOrUninomial) {
|
632
|
this.genusOrUninomial = StringUtils.isBlank(genusOrUninomial) ? null : genusOrUninomial;
|
633
|
}
|
634
|
|
635
|
/**
|
636
|
* Returns the genus subdivision epithet string (infrageneric part) for
|
637
|
* <i>this</i> non viral taxon name if its {@link Rank rank} is infrageneric (lower than genus and
|
638
|
* higher than species aggregate: binomial). Genus subdivision epithet
|
639
|
* strings begin with an upper case letter.
|
640
|
*
|
641
|
* @return the string containing the infrageneric part of <i>this</i> non viral taxon name
|
642
|
* @see #getNameCache()
|
643
|
*/
|
644
|
public String getInfraGenericEpithet(){
|
645
|
return this.infraGenericEpithet;
|
646
|
}
|
647
|
|
648
|
/**
|
649
|
* @see #getInfraGenericEpithet()
|
650
|
*/
|
651
|
public void setInfraGenericEpithet(String infraGenericEpithet){
|
652
|
this.infraGenericEpithet = StringUtils.isBlank(infraGenericEpithet)? null : infraGenericEpithet;
|
653
|
}
|
654
|
|
655
|
/**
|
656
|
* Returns the species epithet string for <i>this</i> non viral taxon name if its {@link Rank rank} is
|
657
|
* species aggregate or lower (bi- or trinomial). Species epithet strings
|
658
|
* begin with a lower case letter.
|
659
|
*
|
660
|
* @return the string containing the species epithet of <i>this</i> non viral taxon name
|
661
|
* @see #getNameCache()
|
662
|
*/
|
663
|
public String getSpecificEpithet(){
|
664
|
return this.specificEpithet;
|
665
|
}
|
666
|
|
667
|
/**
|
668
|
* @see #getSpecificEpithet()
|
669
|
*/
|
670
|
public void setSpecificEpithet(String specificEpithet){
|
671
|
this.specificEpithet = StringUtils.isBlank(specificEpithet) ? null : specificEpithet;
|
672
|
}
|
673
|
|
674
|
/**
|
675
|
* Returns the species subdivision epithet string (infraspecific part) for
|
676
|
* <i>this</i> non viral taxon name if its {@link Rank rank} is infraspecific
|
677
|
* (lower than species: trinomial). Species subdivision epithet strings
|
678
|
* begin with a lower case letter.
|
679
|
*
|
680
|
* @return the string containing the infraspecific part of <i>this</i> non viral taxon name
|
681
|
* @see #getNameCache()
|
682
|
*/
|
683
|
public String getInfraSpecificEpithet(){
|
684
|
return this.infraSpecificEpithet;
|
685
|
}
|
686
|
|
687
|
/**
|
688
|
* @see #getInfraSpecificEpithet()
|
689
|
*/
|
690
|
public void setInfraSpecificEpithet(String infraSpecificEpithet){
|
691
|
this.infraSpecificEpithet = StringUtils.isBlank(infraSpecificEpithet)?null : infraSpecificEpithet;
|
692
|
}
|
693
|
|
694
|
/**
|
695
|
* Generates and returns the string with the scientific name of <i>this</i>
|
696
|
* non viral taxon name including author strings and maybe year according to
|
697
|
* the strategy defined in
|
698
|
* {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy INonViralNameCacheStrategy}.
|
699
|
* This string may be stored in the inherited
|
700
|
* {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
|
701
|
* This method overrides the generic and inherited
|
702
|
* TaxonNameBase#generateTitle() method.
|
703
|
*
|
704
|
* @return the string with the composed name of <i>this</i> non viral taxon name with authorship (and maybe year)
|
705
|
* @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
|
706
|
* @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
|
707
|
* @see TaxonNameBase#generateTitle()
|
708
|
*/
|
709
|
// @Override
|
710
|
// public String generateTitle(){
|
711
|
// if (cacheStrategy == null){
|
712
|
// logger.warn("No CacheStrategy defined for nonViralName: " + this.getUuid());
|
713
|
// return null;
|
714
|
// }else{
|
715
|
// return cacheStrategy.getTitleCache(this);
|
716
|
// }
|
717
|
// }
|
718
|
|
719
|
@Override
|
720
|
public String generateFullTitle(){
|
721
|
if (cacheStrategy == null){
|
722
|
logger.warn("No CacheStrategy defined for nonViralName: " + this.getUuid());
|
723
|
return null;
|
724
|
}else{
|
725
|
return cacheStrategy.getFullTitleCache(this);
|
726
|
}
|
727
|
}
|
728
|
|
729
|
/**
|
730
|
* Generates the composed name string of <i>this</i> non viral taxon name without author
|
731
|
* strings or year according to the strategy defined in
|
732
|
* {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy INonViralNameCacheStrategy}.
|
733
|
* The result might be stored in {@link #getNameCache() nameCache} if the
|
734
|
* flag {@link #isProtectedNameCache() protectedNameCache} is not set.
|
735
|
*
|
736
|
* @return the string with the composed name of <i>this</i> non viral taxon name without authors or year
|
737
|
* @see #getNameCache()
|
738
|
*/
|
739
|
protected String generateNameCache(){
|
740
|
if (cacheStrategy == null){
|
741
|
logger.warn("No CacheStrategy defined for taxonName: " + this.toString());
|
742
|
return null;
|
743
|
}else{
|
744
|
return cacheStrategy.getNameCache(this);
|
745
|
}
|
746
|
}
|
747
|
|
748
|
/**
|
749
|
* Returns or generates the nameCache (scientific name
|
750
|
* without author strings and year) string for <i>this</i> non viral taxon name. If the
|
751
|
* {@link #isProtectedNameCache() protectedNameCache} flag is not set (False)
|
752
|
* the string will be generated according to a defined strategy,
|
753
|
* otherwise the value of the actual nameCache string will be returned.
|
754
|
*
|
755
|
* @return the string which identifies <i>this</i> non viral taxon name (without authors or year)
|
756
|
* @see #generateNameCache()
|
757
|
*/
|
758
|
@Transient
|
759
|
public String getNameCache() {
|
760
|
if (protectedNameCache){
|
761
|
return this.nameCache;
|
762
|
}
|
763
|
// is title dirty, i.e. equal NULL?
|
764
|
if (nameCache == null){
|
765
|
this.nameCache = generateNameCache();
|
766
|
}
|
767
|
return nameCache;
|
768
|
}
|
769
|
|
770
|
/**
|
771
|
* Assigns a nameCache string to <i>this</i> non viral taxon name and protects it from being overwritten.
|
772
|
* Sets the protectedNameCache flag to <code>true</code>.
|
773
|
*
|
774
|
* @param nameCache the string which identifies <i>this</i> non viral taxon name (without authors or year)
|
775
|
* @see #getNameCache()
|
776
|
*/
|
777
|
public void setNameCache(String nameCache){
|
778
|
setNameCache(nameCache, true);
|
779
|
}
|
780
|
|
781
|
/**
|
782
|
* Assigns a nameCache string to <i>this</i> non viral taxon name and protects it from being overwritten.
|
783
|
* Sets the protectedNameCache flag to <code>true</code>.
|
784
|
*
|
785
|
* @param nameCache the string which identifies <i>this</i> non viral taxon name (without authors or year)
|
786
|
* @param protectedNameCache if true teh protectedNameCache is set to <code>true</code> or otherwise set to
|
787
|
* <code>false</code>
|
788
|
* @see #getNameCache()
|
789
|
*/
|
790
|
public void setNameCache(String nameCache, boolean protectedNameCache){
|
791
|
this.nameCache = nameCache;
|
792
|
this.setProtectedNameCache(protectedNameCache);
|
793
|
}
|
794
|
|
795
|
/**
|
796
|
* Returns the boolean value of the flag intended to protect (true)
|
797
|
* or not (false) the {@link #getNameCache() nameCache} (scientific name without author strings and year)
|
798
|
* string of <i>this</i> non viral taxon name.
|
799
|
*
|
800
|
* @return the boolean value of the protectedNameCache flag
|
801
|
* @see #getNameCache()
|
802
|
*/
|
803
|
public boolean isProtectedNameCache() {
|
804
|
return protectedNameCache;
|
805
|
}
|
806
|
|
807
|
/**
|
808
|
* @see #isProtectedNameCache()
|
809
|
*/
|
810
|
public void setProtectedNameCache(boolean protectedNameCache) {
|
811
|
this.protectedNameCache = protectedNameCache;
|
812
|
}
|
813
|
|
814
|
|
815
|
/**
|
816
|
* Generates and returns a concatenated and formated authorteams string
|
817
|
* including basionym and combination authors of <i>this</i> non viral taxon name
|
818
|
* according to the strategy defined in
|
819
|
* {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(NonViralName) INonViralNameCacheStrategy}.
|
820
|
*
|
821
|
* @return the string with the concatenated and formated authorteams for <i>this</i> non viral taxon name
|
822
|
* @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(NonViralName)
|
823
|
*/
|
824
|
public String generateAuthorship(){
|
825
|
if (cacheStrategy == null){
|
826
|
logger.warn("No CacheStrategy defined for nonViralName: " + this.getUuid());
|
827
|
return null;
|
828
|
}else{
|
829
|
return ((INonViralNameCacheStrategy<T>)cacheStrategy).getAuthorshipCache((T)this);
|
830
|
}
|
831
|
}
|
832
|
|
833
|
/**
|
834
|
* Returns the concatenated and formated authorteams string including
|
835
|
* basionym and combination authors of <i>this</i> non viral taxon name.
|
836
|
* If the protectedAuthorshipCache flag is set this method returns the
|
837
|
* string stored in the the authorshipCache attribute, otherwise it
|
838
|
* generates the complete authorship string, returns it and stores it in
|
839
|
* the authorshipCache attribute.
|
840
|
*
|
841
|
* @return the string with the concatenated and formated authorteams for <i>this</i> non viral taxon name
|
842
|
* @see #generateAuthorship()
|
843
|
*/
|
844
|
@Transient
|
845
|
public String getAuthorshipCache() {
|
846
|
if (protectedAuthorshipCache){
|
847
|
return this.authorshipCache;
|
848
|
}
|
849
|
if (this.authorshipCache == null ){
|
850
|
this.authorshipCache = generateAuthorship();
|
851
|
}else{
|
852
|
//TODO get is Dirty of authors, make better if possible
|
853
|
this.setAuthorshipCache(generateAuthorship(), protectedAuthorshipCache); //throw change event to inform higher caches
|
854
|
|
855
|
}
|
856
|
return authorshipCache;
|
857
|
}
|
858
|
|
859
|
|
860
|
/**
|
861
|
* Updates the authorship cache if any changes appeared in the authors nomenclatural caches.
|
862
|
* Deletes the titleCache and the fullTitleCache if not protected and if any change has happened
|
863
|
* @return
|
864
|
*/
|
865
|
private void updateAuthorshipCache() {
|
866
|
//updates the authorship cache if necessary and via the listener updates all higher caches
|
867
|
if (protectedAuthorshipCache == false){
|
868
|
String oldCache = this.authorshipCache;
|
869
|
String newCache = this.getAuthorshipCache();
|
870
|
if ( (oldCache == null && newCache != null) || CdmUtils.nullSafeEqual(oldCache,newCache)){
|
871
|
this.setAuthorshipCache(this.getAuthorshipCache(), false);
|
872
|
}
|
873
|
}
|
874
|
}
|
875
|
|
876
|
/**
|
877
|
* Assigns an authorshipCache string to <i>this</i> non viral taxon name. Sets the isProtectedAuthorshipCache
|
878
|
* flag to <code>true</code>.
|
879
|
*
|
880
|
* @param authorshipCache the string which identifies the complete authorship of <i>this</i> non viral taxon name
|
881
|
* @see #getAuthorshipCache()
|
882
|
*/
|
883
|
public void setAuthorshipCache(String authorshipCache) {
|
884
|
setAuthorshipCache(authorshipCache, true);
|
885
|
}
|
886
|
|
887
|
@Override
|
888
|
@Transient
|
889
|
public String getFullTitleCache(){
|
890
|
updateAuthorshipCache();
|
891
|
return super.getFullTitleCache();
|
892
|
}
|
893
|
|
894
|
@Override
|
895
|
public String getTitleCache(){
|
896
|
if(!protectedTitleCache) {
|
897
|
updateAuthorshipCache();
|
898
|
}
|
899
|
|
900
|
return super.getTitleCache();
|
901
|
}
|
902
|
|
903
|
|
904
|
/**
|
905
|
* Assigns an authorshipCache string to <i>this</i> non viral taxon name.
|
906
|
*
|
907
|
* @param authorshipCache the string which identifies the complete authorship of <i>this</i> non viral taxon name
|
908
|
* @param protectedAuthorshipCache if true the isProtectedAuthorshipCache flag is set to <code>true</code>, otherwise
|
909
|
* the flag is set to <code>false</code>.
|
910
|
* @see #getAuthorshipCache()
|
911
|
*/
|
912
|
public void setAuthorshipCache(String authorshipCache, boolean protectedAuthorshipCache) {
|
913
|
this.authorshipCache = authorshipCache;
|
914
|
this.setProtectedAuthorshipCache(protectedAuthorshipCache);
|
915
|
}
|
916
|
|
917
|
@Override
|
918
|
public void setTitleCache(String titleCache, boolean protectCache){
|
919
|
super.setTitleCache(titleCache, protectCache);
|
920
|
}
|
921
|
|
922
|
/**
|
923
|
* Returns the boolean value "false" since the components of <i>this</i> taxon name
|
924
|
* cannot follow the rules of a corresponding {@link NomenclaturalCode nomenclatural code}
|
925
|
* which is not defined for this class. The nomenclature code depends on
|
926
|
* the concrete name subclass ({@link BacterialName BacterialName},
|
927
|
* {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} or
|
928
|
* {@link ZoologicalName ZoologicalName} to which <i>this</i> non viral taxon name belongs.
|
929
|
* This method overrides the isCodeCompliant method from the abstract
|
930
|
* {@link TaxonNameBase#isCodeCompliant() TaxonNameBase} class.
|
931
|
*
|
932
|
* @return false
|
933
|
* @see TaxonNameBase#isCodeCompliant()
|
934
|
*/
|
935
|
@Override
|
936
|
@Transient
|
937
|
public boolean isCodeCompliant() {
|
938
|
//FIXME
|
939
|
logger.warn("is CodeCompliant not yet implemented");
|
940
|
return false;
|
941
|
}
|
942
|
|
943
|
|
944
|
/**
|
945
|
* Returns null as {@link NomenclaturalCode nomenclatural code} that governs
|
946
|
* the construction of <i>this</i> non viral taxon name since there is no specific
|
947
|
* nomenclatural code defined. The real implementention takes place in the
|
948
|
* subclasses {@link BacterialName BacterialName},
|
949
|
* {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
|
950
|
* {@link ZoologicalName ZoologicalName}.
|
951
|
* This method overrides the {@link TaxonNameBase#getNomenclaturalCode()} method from {@link TaxonNameBase TaxonNameBase}.
|
952
|
*
|
953
|
* @return null
|
954
|
* @see #isCodeCompliant()
|
955
|
* @see TaxonNameBase#getHasProblem()
|
956
|
*/
|
957
|
@Override
|
958
|
@Transient
|
959
|
public NomenclaturalCode getNomenclaturalCode() {
|
960
|
logger.warn("Non Viral Name has no specific Code defined. Use subclasses");
|
961
|
return null;
|
962
|
}
|
963
|
|
964
|
/**
|
965
|
* Returns the boolean value of the flag intended to protect (true)
|
966
|
* or not (false) the {@link #getAuthorshipCache() authorshipCache} (complete authorship string)
|
967
|
* of <i>this</i> non viral taxon name.
|
968
|
*
|
969
|
* @return the boolean value of the protectedAuthorshipCache flag
|
970
|
* @see #getAuthorshipCache()
|
971
|
*/
|
972
|
public boolean isProtectedAuthorshipCache() {
|
973
|
return protectedAuthorshipCache;
|
974
|
}
|
975
|
|
976
|
/**
|
977
|
* @see #isProtectedAuthorshipCache()
|
978
|
* @see #getAuthorshipCache()
|
979
|
*/
|
980
|
public void setProtectedAuthorshipCache(boolean protectedAuthorshipCache) {
|
981
|
this.protectedAuthorshipCache = protectedAuthorshipCache;
|
982
|
}
|
983
|
|
984
|
|
985
|
/**
|
986
|
* Returns the boolean value of the flag indicating whether the name of <i>this</i>
|
987
|
* botanical taxon name is a hybrid formula (true) or not (false). A hybrid
|
988
|
* named by a hybrid formula (composed with its parent names by placing the
|
989
|
* multiplication sign between them) does not have an own published name
|
990
|
* and therefore has neither an {@link NonViralName#getAuthorshipCache() autorship}
|
991
|
* nor other name components. If this flag is set no other hybrid flags may
|
992
|
* be set.
|
993
|
*
|
994
|
* @return the boolean value of the isHybridFormula flag
|
995
|
* @see #isMonomHybrid()
|
996
|
* @see #isBinomHybrid()
|
997
|
* @see #isTrinomHybrid()
|
998
|
*/
|
999
|
public boolean isHybridFormula(){
|
1000
|
return this.hybridFormula;
|
1001
|
}
|
1002
|
|
1003
|
/**
|
1004
|
* @see #isHybridFormula()
|
1005
|
*/
|
1006
|
public void setHybridFormula(boolean hybridFormula){
|
1007
|
this.hybridFormula = hybridFormula;
|
1008
|
}
|
1009
|
|
1010
|
/**
|
1011
|
* Returns the boolean value of the flag indicating whether <i>this</i> botanical
|
1012
|
* taxon name is the name of an intergeneric hybrid (true) or not (false).
|
1013
|
* In this case the multiplication sign is placed before the scientific
|
1014
|
* name. If this flag is set no other hybrid flags may be set.
|
1015
|
*
|
1016
|
* @return the boolean value of the isMonomHybrid flag
|
1017
|
* @see #isHybridFormula()
|
1018
|
* @see #isBinomHybrid()
|
1019
|
* @see #isTrinomHybrid()
|
1020
|
*/
|
1021
|
public boolean isMonomHybrid(){
|
1022
|
return this.monomHybrid;
|
1023
|
}
|
1024
|
|
1025
|
/**
|
1026
|
* @see #isMonomHybrid()
|
1027
|
* @see #isBinomHybrid()
|
1028
|
* @see #isTrinomHybrid()
|
1029
|
*/
|
1030
|
public void setMonomHybrid(boolean monomHybrid){
|
1031
|
this.monomHybrid = monomHybrid;
|
1032
|
}
|
1033
|
|
1034
|
/**
|
1035
|
* Returns the boolean value of the flag indicating whether <i>this</i> botanical
|
1036
|
* taxon name is the name of an interspecific hybrid (true) or not (false).
|
1037
|
* In this case the multiplication sign is placed before the species
|
1038
|
* epithet. If this flag is set no other hybrid flags may be set.
|
1039
|
*
|
1040
|
* @return the boolean value of the isBinomHybrid flag
|
1041
|
* @see #isHybridFormula()
|
1042
|
* @see #isMonomHybrid()
|
1043
|
* @see #isTrinomHybrid()
|
1044
|
*/
|
1045
|
public boolean isBinomHybrid(){
|
1046
|
return this.binomHybrid;
|
1047
|
}
|
1048
|
|
1049
|
/**
|
1050
|
* @see #isBinomHybrid()
|
1051
|
* @see #isMonomHybrid()
|
1052
|
* @see #isTrinomHybrid()
|
1053
|
*/
|
1054
|
public void setBinomHybrid(boolean binomHybrid){
|
1055
|
this.binomHybrid = binomHybrid;
|
1056
|
}
|
1057
|
|
1058
|
/**
|
1059
|
* Returns the boolean value of the flag indicating whether <i>this</i> botanical
|
1060
|
* taxon name is the name of an infraspecific hybrid (true) or not (false).
|
1061
|
* In this case the term "notho-" (optionally abbreviated "n-") is used as
|
1062
|
* a prefix to the term denoting the infraspecific rank of <i>this</i> botanical
|
1063
|
* taxon name. If this flag is set no other hybrid flags may be set.
|
1064
|
*
|
1065
|
* @return the boolean value of the isTrinomHybrid flag
|
1066
|
* @see #isHybridFormula()
|
1067
|
* @see #isMonomHybrid()
|
1068
|
* @see #isBinomHybrid()
|
1069
|
*/
|
1070
|
public boolean isTrinomHybrid(){
|
1071
|
return this.trinomHybrid;
|
1072
|
}
|
1073
|
|
1074
|
/**
|
1075
|
* @see #isTrinomHybrid()
|
1076
|
* @see #isBinomHybrid()
|
1077
|
* @see #isMonomHybrid()
|
1078
|
*/
|
1079
|
public void setTrinomHybrid(boolean trinomHybrid){
|
1080
|
this.trinomHybrid = trinomHybrid;
|
1081
|
}
|
1082
|
|
1083
|
|
1084
|
/**
|
1085
|
* Returns the set of all {@link HybridRelationship hybrid relationships}
|
1086
|
* in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedFrom() parent}.
|
1087
|
*
|
1088
|
* @see #getHybridRelationships()
|
1089
|
* @see #getChildRelationships()
|
1090
|
* @see HybridRelationshipType
|
1091
|
*/
|
1092
|
public Set<HybridRelationship> getHybridParentRelations() {
|
1093
|
if(hybridParentRelations == null) {
|
1094
|
this.hybridParentRelations = new HashSet<HybridRelationship>();
|
1095
|
}
|
1096
|
return hybridParentRelations;
|
1097
|
}
|
1098
|
|
1099
|
private void setHybridParentRelations(Set<HybridRelationship> hybridParentRelations) {
|
1100
|
this.hybridParentRelations = hybridParentRelations;
|
1101
|
}
|
1102
|
|
1103
|
|
1104
|
/**
|
1105
|
* Returns the set of all {@link HybridRelationship hybrid relationships}
|
1106
|
* in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedTo() child}.
|
1107
|
*
|
1108
|
* @see #getHybridRelationships()
|
1109
|
* @see #getParentRelationships()
|
1110
|
* @see HybridRelationshipType
|
1111
|
*/
|
1112
|
public Set<HybridRelationship> getHybridChildRelations() {
|
1113
|
if(hybridChildRelations == null) {
|
1114
|
this.hybridChildRelations = new HashSet<HybridRelationship>();
|
1115
|
}
|
1116
|
return hybridChildRelations;
|
1117
|
}
|
1118
|
|
1119
|
private void setHybridChildRelations(Set<HybridRelationship> hybridChildRelations) {
|
1120
|
this.hybridChildRelations = hybridChildRelations;
|
1121
|
}
|
1122
|
|
1123
|
/**
|
1124
|
* Returns the hybrid child relationships ordered by relationship type, or if equal
|
1125
|
* by title cache of the related names.
|
1126
|
* @see #getHybridParentRelations()
|
1127
|
*/
|
1128
|
@Transient
|
1129
|
public List<HybridRelationship> getOrderedChildRelationships(){
|
1130
|
List<HybridRelationship> result = new ArrayList<HybridRelationship>();
|
1131
|
result.addAll(this.hybridChildRelations);
|
1132
|
Collections.sort(result);
|
1133
|
Collections.reverse(result);
|
1134
|
return result;
|
1135
|
|
1136
|
}
|
1137
|
|
1138
|
|
1139
|
/**
|
1140
|
* Adds the given {@link HybridRelationship hybrid relationship} to the set
|
1141
|
* of {@link #getHybridRelationships() hybrid relationships} of both non-viral names
|
1142
|
* involved in this hybrid relationship. One of both non-viral names
|
1143
|
* must be <i>this</i> non-viral name otherwise no addition will be carried
|
1144
|
* out. The {@link eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo() child
|
1145
|
* non viral taxon name} must be a hybrid, which means that one of its four hybrid flags must be set.
|
1146
|
*
|
1147
|
* @param relationship the hybrid relationship to be added
|
1148
|
* @see #isHybridFormula()
|
1149
|
* @see #isMonomHybrid()
|
1150
|
* @see #isBinomHybrid()
|
1151
|
* @see #isTrinomHybrid()
|
1152
|
* @see #getHybridRelationships()
|
1153
|
* @see #getParentRelationships()
|
1154
|
* @see #getChildRelationships()
|
1155
|
* @see #addRelationship(RelationshipBase)
|
1156
|
* @throws IllegalArgumentException
|
1157
|
*/
|
1158
|
protected void addHybridRelationship(HybridRelationship rel) {
|
1159
|
if (rel!=null && rel.getHybridName().equals(this)){
|
1160
|
this.hybridChildRelations.add(rel);
|
1161
|
}else if(rel!=null && rel.getParentName().equals(this)){
|
1162
|
this.hybridParentRelations.add(rel);
|
1163
|
}else{
|
1164
|
throw new IllegalArgumentException("Hybrid relationship is either null or the relationship does not reference this name");
|
1165
|
}
|
1166
|
}
|
1167
|
|
1168
|
|
1169
|
|
1170
|
/**
|
1171
|
* Does the same as the addHybridRelationship method if the given
|
1172
|
* {@link common.RelationshipBase relation} is also a {@link HybridRelationship hybrid relationship}.
|
1173
|
* Otherwise this method does the same as the overwritten {@link TaxonNameBase#addRelationship(RelationshipBase) addRelationship}
|
1174
|
* method from TaxonNameBase.
|
1175
|
*
|
1176
|
* @param relation the relationship to be added to some of <i>this</i> taxon name's relationships sets
|
1177
|
* @see #addHybridRelationship(HybridRelationship)
|
1178
|
* @see TaxonNameBase#addRelationship(RelationshipBase)
|
1179
|
* @see TaxonNameBase#addNameRelationship(NameRelationship)
|
1180
|
* @deprecated to be used by RelationshipBase only
|
1181
|
*/
|
1182
|
@Override
|
1183
|
@Deprecated //to be used by RelationshipBase only
|
1184
|
public void addRelationship(RelationshipBase relation) {
|
1185
|
if (relation instanceof HybridRelationship){
|
1186
|
addHybridRelationship((HybridRelationship)relation);
|
1187
|
}else {
|
1188
|
super.addRelationship(relation);
|
1189
|
}
|
1190
|
}
|
1191
|
|
1192
|
/**
|
1193
|
* Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship}
|
1194
|
* to <i>this</i> botanical name. A HybridRelationship may be of type
|
1195
|
* "is first/second parent" or "is male/female parent". By invoking this
|
1196
|
* method <i>this</i> botanical name becomes a hybrid child of the parent
|
1197
|
* botanical name.
|
1198
|
*
|
1199
|
* @param parentName the botanical name of the parent for this new hybrid name relationship
|
1200
|
* @param type the type of this new name relationship
|
1201
|
* @param ruleConsidered the string which specifies the rule on which this name relationship is based
|
1202
|
* @return
|
1203
|
* @see #addHybridChild(BotanicalName, HybridRelationshipType,String )
|
1204
|
* @see #getRelationsToThisName()
|
1205
|
* @see #getNameRelations()
|
1206
|
* @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
|
1207
|
* @see #addNameRelationship(NameRelationship)
|
1208
|
*/
|
1209
|
public HybridRelationship addHybridParent(NonViralName parentName, HybridRelationshipType type, String ruleConsidered){
|
1210
|
return new HybridRelationship(this, parentName, type, ruleConsidered);
|
1211
|
}
|
1212
|
|
1213
|
/**
|
1214
|
* Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship}
|
1215
|
* to <i>this</i> botanical name. A HybridRelationship may be of type
|
1216
|
* "is first/second parent" or "is male/female parent". By invoking this
|
1217
|
* method <i>this</i> botanical name becomes a parent of the hybrid child
|
1218
|
* botanical name.
|
1219
|
*
|
1220
|
* @param childName the botanical name of the child for this new hybrid name relationship
|
1221
|
* @param type the type of this new name relationship
|
1222
|
* @param ruleConsidered the string which specifies the rule on which this name relationship is based
|
1223
|
* @return
|
1224
|
* @see #addHybridParent(BotanicalName, HybridRelationshipType,String )
|
1225
|
* @see #getRelationsToThisName()
|
1226
|
* @see #getNameRelations()
|
1227
|
* @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
|
1228
|
* @see #addNameRelationship(NameRelationship)
|
1229
|
*/
|
1230
|
public HybridRelationship addHybridChild(NonViralName childName, HybridRelationshipType type, String ruleConsidered){
|
1231
|
return new HybridRelationship(childName, this, type, ruleConsidered);
|
1232
|
}
|
1233
|
|
1234
|
|
1235
|
/**
|
1236
|
* Removes one {@link HybridRelationship hybrid relationship} from the set of
|
1237
|
* {@link #getHybridRelationships() hybrid relationships} in which <i>this</i> botanical taxon name
|
1238
|
* is involved. The hybrid relationship will also be removed from the set
|
1239
|
* belonging to the second botanical taxon name involved.
|
1240
|
*
|
1241
|
* @param relationship the hybrid relationship which should be deleted from the corresponding sets
|
1242
|
* @see #getHybridRelationships()
|
1243
|
*/
|
1244
|
public void removeHybridRelationship(HybridRelationship hybridRelation) {
|
1245
|
if (hybridRelation == null) {
|
1246
|
return;
|
1247
|
}
|
1248
|
|
1249
|
NonViralName<?> parent = hybridRelation.getParentName();
|
1250
|
NonViralName<?> child = hybridRelation.getHybridName();
|
1251
|
if (this.equals(parent)){
|
1252
|
this.hybridParentRelations.remove(hybridRelation);
|
1253
|
child.hybridChildRelations.remove(hybridRelation);
|
1254
|
hybridRelation.setHybridName(null);
|
1255
|
hybridRelation.setParentName(null);
|
1256
|
|
1257
|
}
|
1258
|
if (this.equals(child)){
|
1259
|
parent.hybridParentRelations.remove(hybridRelation);
|
1260
|
this.hybridChildRelations.remove(hybridRelation);
|
1261
|
hybridRelation.setHybridName(null);
|
1262
|
hybridRelation.setParentName(null);
|
1263
|
|
1264
|
}
|
1265
|
|
1266
|
|
1267
|
}
|
1268
|
|
1269
|
|
1270
|
public void removeHybridChild(NonViralName child) {
|
1271
|
Set<HybridRelationship> hybridRelationships = new HashSet<HybridRelationship>();
|
1272
|
hybridRelationships.addAll(this.getHybridChildRelations());
|
1273
|
hybridRelationships.addAll(this.getHybridParentRelations());
|
1274
|
for(HybridRelationship hybridRelationship : hybridRelationships) {
|
1275
|
// remove name relationship from this side
|
1276
|
if (hybridRelationship.getParentName().equals(this) && hybridRelationship.getHybridName().equals(child)) {
|
1277
|
this.removeHybridRelationship(hybridRelationship);
|
1278
|
}
|
1279
|
}
|
1280
|
}
|
1281
|
|
1282
|
public void removeHybridParent(NonViralName parent) {
|
1283
|
Set<HybridRelationship> hybridRelationships = new HashSet<HybridRelationship>();
|
1284
|
hybridRelationships.addAll(this.getHybridChildRelations());
|
1285
|
hybridRelationships.addAll(this.getHybridParentRelations());
|
1286
|
for(HybridRelationship hybridRelationship : hybridRelationships) {
|
1287
|
// remove name relationship from this side
|
1288
|
if (hybridRelationship.getParentName().equals(parent) && hybridRelationship.getHybridName().equals(this)) {
|
1289
|
this.removeHybridRelationship(hybridRelationship);
|
1290
|
}
|
1291
|
}
|
1292
|
}
|
1293
|
|
1294
|
/**
|
1295
|
* Needs to be implemented by those classes that handle autonyms (e.g. botanical names).
|
1296
|
**/
|
1297
|
@Transient
|
1298
|
public boolean isAutonym(){
|
1299
|
return false;
|
1300
|
}
|
1301
|
|
1302
|
|
1303
|
// /**
|
1304
|
// * Returns the boolean value indicating whether <i>this</i> names rank is Rank "unranked"
|
1305
|
// * (uuid = 'a965befb-70a9-4747-a18f-624456c65223') but most likely it is an infrageneric rank
|
1306
|
// * due to existing atomized data for the genus epithet and the infrageneric epithet but missing
|
1307
|
// * specific epithet.
|
1308
|
// * Returns false if <i>this</i> names rank is null.
|
1309
|
// *
|
1310
|
// * @see #isSupraGeneric()
|
1311
|
// * @see #isGenus()
|
1312
|
// * @see #isSpeciesAggregate()
|
1313
|
// * @see #isSpecies()
|
1314
|
// * @see #isInfraSpecific()
|
1315
|
// */
|
1316
|
// @Transient
|
1317
|
// public boolean isInfragenericUnranked() {
|
1318
|
// Rank rank = this.getRank();
|
1319
|
// if (rank == null || ! rank.equals(Rank.UNRANKED())){
|
1320
|
// return false;
|
1321
|
// }
|
1322
|
// if (StringUtils.isBlank(this.getSpecificEpithet()) && StringUtils.isBlank(this.getInfraSpecificEpithet()) ){
|
1323
|
// return true;
|
1324
|
// }else{
|
1325
|
// return false;
|
1326
|
// }
|
1327
|
// }
|
1328
|
|
1329
|
|
1330
|
/**
|
1331
|
* Tests if the given name has any authors.
|
1332
|
* @return false if no author ((ex)combination or (ex)basionym) exists, true otherwise
|
1333
|
*/
|
1334
|
public boolean hasAuthors() {
|
1335
|
return (this.getCombinationAuthorship() != null ||
|
1336
|
this.getExCombinationAuthorship() != null ||
|
1337
|
this.getBasionymAuthorship() != null ||
|
1338
|
this.getExBasionymAuthorship() != null);
|
1339
|
}
|
1340
|
|
1341
|
/**
|
1342
|
* Shortcut. Returns the combination authors title cache. Returns null if no combination author exists.
|
1343
|
* @return
|
1344
|
*/
|
1345
|
public String computeCombinationAuthorNomenclaturalTitle() {
|
1346
|
return computeNomenclaturalTitle(this.getCombinationAuthorship());
|
1347
|
}
|
1348
|
|
1349
|
/**
|
1350
|
* Shortcut. Returns the basionym authors title cache. Returns null if no basionym author exists.
|
1351
|
* @return
|
1352
|
*/
|
1353
|
public String computeBasionymAuthorNomenclaturalTitle() {
|
1354
|
return computeNomenclaturalTitle(this.getBasionymAuthorship());
|
1355
|
}
|
1356
|
|
1357
|
|
1358
|
/**
|
1359
|
* Shortcut. Returns the ex-combination authors title cache. Returns null if no ex-combination author exists.
|
1360
|
* @return
|
1361
|
*/
|
1362
|
public String computeExCombinationAuthorNomenclaturalTitle() {
|
1363
|
return computeNomenclaturalTitle(this.getExCombinationAuthorship());
|
1364
|
}
|
1365
|
|
1366
|
/**
|
1367
|
* Shortcut. Returns the ex-basionym authors title cache. Returns null if no exbasionym author exists.
|
1368
|
* @return
|
1369
|
*/
|
1370
|
public String computeExBasionymAuthorNomenclaturalTitle() {
|
1371
|
return computeNomenclaturalTitle(this.getExBasionymAuthorship());
|
1372
|
}
|
1373
|
|
1374
|
private String computeNomenclaturalTitle(INomenclaturalAuthor author){
|
1375
|
if (author == null){
|
1376
|
return null;
|
1377
|
}else{
|
1378
|
return author.getNomenclaturalTitle();
|
1379
|
}
|
1380
|
}
|
1381
|
|
1382
|
|
1383
|
/**
|
1384
|
* Defines the last part of the name.
|
1385
|
* This is for infraspecific taxa, the infraspecific epithet,
|
1386
|
* for species the specific epithet, for infageneric taxa the infrageneric epithet
|
1387
|
* else the genusOrUninomial.
|
1388
|
* However, the result does not depend on the rank (which may be not correctly set
|
1389
|
* in case of dirty data) but returns the first name part which is not blank
|
1390
|
* considering the above order.
|
1391
|
* @return the first not blank name part in reverse order
|
1392
|
*/
|
1393
|
public String getLastNamePart() {
|
1394
|
String result =
|
1395
|
StringUtils.isNotBlank(this.getInfraSpecificEpithet())?
|
1396
|
this.getInfraSpecificEpithet() :
|
1397
|
StringUtils.isNotBlank(this.getSpecificEpithet()) ?
|
1398
|
this.getSpecificEpithet():
|
1399
|
StringUtils.isNotBlank(this.getInfraGenericEpithet()) ?
|
1400
|
this.getInfraGenericEpithet():
|
1401
|
this.getGenusOrUninomial();
|
1402
|
return result;
|
1403
|
}
|
1404
|
|
1405
|
//*********************** CLONE ********************************************************/
|
1406
|
|
1407
|
/**
|
1408
|
* Clones <i>this</i> non-viral name. This is a shortcut that enables to create
|
1409
|
* a new instance that differs only slightly from <i>this</i> non-viral name by
|
1410
|
* modifying only some of the attributes.
|
1411
|
*
|
1412
|
* @see eu.etaxonomy.cdm.model.name.TaxonNameBase#clone()
|
1413
|
* @see java.lang.Object#clone()
|
1414
|
*/
|
1415
|
@Override
|
1416
|
public Object clone() {
|
1417
|
NonViralName<?> result = (NonViralName<?>)super.clone();
|
1418
|
|
1419
|
//HybridChildRelations
|
1420
|
result.hybridChildRelations = new HashSet<HybridRelationship>();
|
1421
|
for (HybridRelationship hybridRelationship : getHybridChildRelations()){
|
1422
|
HybridRelationship newChildRelationship = (HybridRelationship)hybridRelationship.clone();
|
1423
|
newChildRelationship.setRelatedTo(result);
|
1424
|
result.hybridChildRelations.add(newChildRelationship);
|
1425
|
}
|
1426
|
|
1427
|
//HybridParentRelations
|
1428
|
result.hybridParentRelations = new HashSet<HybridRelationship>();
|
1429
|
for (HybridRelationship hybridRelationship : getHybridParentRelations()){
|
1430
|
HybridRelationship newParentRelationship = (HybridRelationship)hybridRelationship.clone();
|
1431
|
newParentRelationship.setRelatedFrom(result);
|
1432
|
result.hybridParentRelations.add(newParentRelationship);
|
1433
|
}
|
1434
|
|
1435
|
//empty caches
|
1436
|
if (! protectedNameCache){
|
1437
|
result.nameCache = null;
|
1438
|
}
|
1439
|
|
1440
|
//empty caches
|
1441
|
if (! protectedAuthorshipCache){
|
1442
|
result.authorshipCache = null;
|
1443
|
}
|
1444
|
|
1445
|
//no changes to: basionamyAuthorship, combinationAuthorship, exBasionymAuthorship, exCombinationAuthorship
|
1446
|
//genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
|
1447
|
//protectedAuthorshipCache, protectedNameCache
|
1448
|
//binomHybrid, monomHybrid, trinomHybrid, hybridFormula,
|
1449
|
return result;
|
1450
|
}
|
1451
|
}
|