2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.model
.name
;
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
;
22 import javax
.persistence
.Entity
;
23 import javax
.persistence
.FetchType
;
24 import javax
.persistence
.JoinColumn
;
25 import javax
.persistence
.ManyToOne
;
26 import javax
.persistence
.OneToMany
;
27 import javax
.persistence
.Transient
;
28 import javax
.validation
.constraints
.NotNull
;
29 import javax
.validation
.constraints
.Pattern
;
30 import javax
.validation
.constraints
.Size
;
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
;
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
;
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
;
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.
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
93 * @created 08-Nov-2007 13:06:39
95 @XmlAccessorType(XmlAccessType
.FIELD
)
96 @XmlType(name
= "NonViralName", propOrder
= {
99 "infraGenericEpithet",
101 "infraSpecificEpithet",
102 "combinationAuthorTeam",
103 "exCombinationAuthorTeam",
104 "basionymAuthorTeam",
105 "exBasionymAuthorTeam",
107 "protectedAuthorshipCache",
108 "protectedNameCache",
109 "hybridParentRelations",
110 "hybridChildRelations",
116 @XmlRootElement(name
= "NonViralName")
118 @Indexed(index
= "eu.etaxonomy.cdm.model.name.TaxonNameBase")
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);
128 @XmlElement(name
= "NameCache")
130 @Field(name
= "nameCache_tokenized"),
131 @Field(store
= Store
.YES
, index
= Index
.YES
, analyze
= Analyze
.YES
)
133 @Analyzer (impl
= org
.apache
.lucene
.analysis
.KeywordAnalyzer
.class)
134 @Match(value
=MatchMode
.CACHE
, cacheReplaceMode
=ReplaceMode
.DEFINED
,
135 cacheReplacedProperties
={"genusOrUninomial", "infraGenericEpithet", "specificEpithet", "infraSpecificEpithet"} )
136 @NotEmpty(groups
= Level2
.class) // implicitly NotNull
138 private String nameCache
;
140 @XmlElement(name
= "ProtectedNameCache")
141 @CacheUpdate(value
="nameCache")
142 protected boolean protectedNameCache
;
144 @XmlElement(name
= "GenusOrUninomial")
145 @Field(analyze
= Analyze
.YES
,indexNullAs
=Field
.DEFAULT_NULL_TOKEN
)
146 @Match(MatchMode
.EQUAL_REQUIRED
)
147 @CacheUpdate("nameCache")
149 @Pattern(regexp
= "[A-Z][a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups
=Level2
.class, message
="{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForUninomial.message}")
151 @NotNull(groups
= Level2
.class)
152 private String genusOrUninomial
;
154 @XmlElement(name
= "InfraGenericEpithet")
155 @Field(analyze
= Analyze
.YES
,indexNullAs
=Field
.DEFAULT_NULL_TOKEN
)
156 @CacheUpdate("nameCache")
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
;
163 @XmlElement(name
= "SpecificEpithet")
164 @Field(analyze
= Analyze
.YES
,indexNullAs
=Field
.DEFAULT_NULL_TOKEN
)
165 @CacheUpdate("nameCache")
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
;
172 @XmlElement(name
= "InfraSpecificEpithet")
173 @Field(analyze
= Analyze
.YES
,indexNullAs
=Field
.DEFAULT_NULL_TOKEN
)
174 @CacheUpdate("nameCache")
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
;
181 @XmlElement(name
= "CombinationAuthorTeam", type
= TeamOrPersonBase
.class)
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")
190 private TeamOrPersonBase
<?
> combinationAuthorTeam
;
192 @XmlElement(name
= "ExCombinationAuthorTeam", type
= TeamOrPersonBase
.class)
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")
201 private TeamOrPersonBase
<?
> exCombinationAuthorTeam
;
203 @XmlElement(name
= "BasionymAuthorTeam", type
= TeamOrPersonBase
.class)
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")
212 private TeamOrPersonBase
<?
> basionymAuthorTeam
;
214 @XmlElement(name
= "ExBasionymAuthorTeam", type
= TeamOrPersonBase
.class)
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")
223 private TeamOrPersonBase
<?
> exBasionymAuthorTeam
;
225 @XmlElement(name
= "AuthorshipCache")
227 @Field(name
= "authorshipCache_tokenized"),
228 @Field(analyze
= Analyze
.NO
)
230 @Match(value
=MatchMode
.CACHE
, cacheReplaceMode
=ReplaceMode
.DEFINED
,
231 cacheReplacedProperties
={"combinationAuthorTeam", "basionymAuthorTeam", "exCombinationAuthorTeam", "exBasionymAuthorTeam"} )
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
;
238 @XmlElement(name
= "ProtectedAuthorshipCache")
239 @CacheUpdate("authorshipCache")
240 protected boolean protectedAuthorshipCache
;
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
)
248 private Set
<HybridRelationship
> hybridParentRelations
= new HashSet
<HybridRelationship
>();
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
)
256 private Set
<HybridRelationship
> hybridChildRelations
= new HashSet
<HybridRelationship
>();
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;
264 @XmlElement(name
="IsMonomHybrid")
265 @CacheUpdate("nameCache")
266 private boolean monomHybrid
= false;
268 @XmlElement(name
="IsBinomHybrid")
269 @CacheUpdate("nameCache")
270 private boolean binomHybrid
= false;
272 @XmlElement(name
="IsTrinomHybrid")
273 @CacheUpdate("nameCache")
274 private boolean trinomHybrid
= false;
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}.
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
290 public static NonViralName
NewInstance(Rank rank
){
291 return new NonViralName(rank
, null);
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.
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
312 public static NonViralName
NewInstance(Rank rank
, HomotypicalGroup homotypicalGroup
){
313 return new NonViralName(rank
, homotypicalGroup
);
316 // ************************** CONSTRUCTORS *************/
318 //needed by hibernate
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}.
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
329 protected NonViralName(){
331 setNameCacheStrategy();
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.
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
351 protected NonViralName(Rank rank
, HomotypicalGroup homotypicalGroup
) {
352 super(rank
, homotypicalGroup
);
353 setNameCacheStrategy();
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.
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 combinationAuthorTeam 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
387 protected NonViralName(Rank rank
, String genusOrUninomial
, String infraGenericEpithet
, String specificEpithet
, String infraSpecificEpithet
, TeamOrPersonBase combinationAuthorTeam
, 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 setCombinationAuthorTeam(combinationAuthorTeam
);
395 setNomenclaturalReference(nomenclaturalReference
);
396 this.setNomenclaturalMicroReference(nomenclMicroRef
);
401 //**************************** METHODS **************************************/
404 private void setNameCacheStrategy(){
405 if (getClass() == NonViralName
.class){
406 this.cacheStrategy
= NonViralNameDefaultCacheStrategy
.NewInstance();
411 protected void initListener(){
412 PropertyChangeListener listener
= new PropertyChangeListener() {
414 public void propertyChange(PropertyChangeEvent e
) {
415 boolean protectedByLowerCache
= false;
417 if (fieldHasCacheUpdateProperty(e
.getPropertyName(), "authorshipCache")){
418 if (protectedAuthorshipCache
){
419 protectedByLowerCache
= true;
421 authorshipCache
= null;
426 if (fieldHasCacheUpdateProperty(e
.getPropertyName(), "nameCache")){
427 if (protectedNameCache
){
428 protectedByLowerCache
= true;
434 if (! fieldHasNoUpdateProperty(e
.getPropertyName(), "titleCache")){
435 if (isProtectedTitleCache()|| protectedByLowerCache
== true ){
436 protectedByLowerCache
= true;
442 if (! fieldHasNoUpdateProperty(e
.getPropertyName(), "fullTitleCache")){
443 if (isProtectedFullTitleCache()|| protectedByLowerCache
== true ){
444 protectedByLowerCache
= true;
446 fullTitleCache
= null;
451 addPropertyChangeListener(listener
); //didn't use this.addXXX to make lsid.AssemblerTest run in cdmlib-remote
454 private static Map
<String
, java
.lang
.reflect
.Field
> allFields
= null;
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);
464 * @param propertyName
468 private boolean fieldHasCacheUpdateProperty(String propertyName
, String cacheName
) {
469 java
.lang
.reflect
.Field field
;
471 field
= getAllFields().get(propertyName
);
473 CacheUpdate updateAnnotation
= field
.getAnnotation(CacheUpdate
.class);
474 if (updateAnnotation
!= null){
475 for (String value
: updateAnnotation
.value()){
476 if (cacheName
.equals(value
)){
483 } catch (SecurityException e1
) {
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
)){
494 //evaluate annotation
496 field
= getAllFields().get(propertyName
);
498 CacheUpdate updateAnnotation
= field
.getAnnotation(CacheUpdate
.class);
499 if (updateAnnotation
!= null){
500 for (String value
: updateAnnotation
.noUpdate()){
501 if (cacheName
.equals(value
)){
508 } catch (SecurityException e1
) {
515 * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that published <i>this</i> non viral
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()
522 public TeamOrPersonBase
<?
> getCombinationAuthorTeam(){
523 return this.combinationAuthorTeam
;
527 * @see #getCombinationAuthorTeam()
529 public void setCombinationAuthorTeam(TeamOrPersonBase
<?
> combinationAuthorTeam
){
530 this.combinationAuthorTeam
= combinationAuthorTeam
;
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 #getCombinationAuthorTeam() 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.
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.
551 * @return the nomenclatural ex author (team) of <i>this</i> non viral taxon name
552 * @see #getCombinationAuthorTeam()
553 * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
554 * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
556 public TeamOrPersonBase
<?
> getExCombinationAuthorTeam(){
557 return this.exCombinationAuthorTeam
;
561 * @see #getExCombinationAuthorTeam()
563 public void setExCombinationAuthorTeam(TeamOrPersonBase
<?
> exCombinationAuthorTeam
){
564 this.exCombinationAuthorTeam
= exCombinationAuthorTeam
;
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.
573 * @return the nomenclatural basionym author (team) of <i>this</i> non viral taxon name
574 * @see #getCombinationAuthorTeam()
575 * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
576 * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
578 public TeamOrPersonBase
<?
> getBasionymAuthorTeam(){
579 return basionymAuthorTeam
;
583 * @see #getBasionymAuthorTeam()
585 public void setBasionymAuthorTeam(TeamOrPersonBase
<?
> basionymAuthorTeam
) {
586 this.basionymAuthorTeam
= basionymAuthorTeam
;
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 #getBasionymAuthorTeam() 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.
598 * @return the nomenclatural ex basionym author (team) of <i>this</i> non viral taxon name
599 * @see #getBasionymAuthorTeam()
600 * @see #getExCombinationAuthorTeam()
601 * @see #getCombinationAuthorTeam()
602 * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
603 * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
605 public TeamOrPersonBase
<?
> getExBasionymAuthorTeam(){
606 return exBasionymAuthorTeam
;
610 * @see #getExBasionymAuthorTeam()
612 public void setExBasionymAuthorTeam(TeamOrPersonBase
<?
> exBasionymAuthorTeam
) {
613 this.exBasionymAuthorTeam
= exBasionymAuthorTeam
;
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.
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()
624 public String
getGenusOrUninomial() {
625 return genusOrUninomial
;
629 * @see #getGenusOrUninomial()
631 public void setGenusOrUninomial(String genusOrUninomial
) {
632 this.genusOrUninomial
= StringUtils
.isBlank(genusOrUninomial
) ?
null : genusOrUninomial
;
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.
641 * @return the string containing the infrageneric part of <i>this</i> non viral taxon name
642 * @see #getNameCache()
644 public String
getInfraGenericEpithet(){
645 return this.infraGenericEpithet
;
649 * @see #getInfraGenericEpithet()
651 public void setInfraGenericEpithet(String infraGenericEpithet
){
652 this.infraGenericEpithet
= StringUtils
.isBlank(infraGenericEpithet
)?
null : infraGenericEpithet
;
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.
660 * @return the string containing the species epithet of <i>this</i> non viral taxon name
661 * @see #getNameCache()
663 public String
getSpecificEpithet(){
664 return this.specificEpithet
;
668 * @see #getSpecificEpithet()
670 public void setSpecificEpithet(String specificEpithet
){
671 this.specificEpithet
= StringUtils
.isBlank(specificEpithet
) ?
null : specificEpithet
;
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.
680 * @return the string containing the infraspecific part of <i>this</i> non viral taxon name
681 * @see #getNameCache()
683 public String
getInfraSpecificEpithet(){
684 return this.infraSpecificEpithet
;
688 * @see #getInfraSpecificEpithet()
690 public void setInfraSpecificEpithet(String infraSpecificEpithet
){
691 this.infraSpecificEpithet
= StringUtils
.isBlank(infraSpecificEpithet
)?
null : infraSpecificEpithet
;
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.
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()
710 // public String generateTitle(){
711 // if (cacheStrategy == null){
712 // logger.warn("No CacheStrategy defined for nonViralName: " + this.getUuid());
715 // return cacheStrategy.getTitleCache(this);
720 public String
generateFullTitle(){
721 if (cacheStrategy
== null){
722 logger
.warn("No CacheStrategy defined for nonViralName: " + this.getUuid());
725 return cacheStrategy
.getFullTitleCache(this);
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.
736 * @return the string with the composed name of <i>this</i> non viral taxon name without authors or year
737 * @see #getNameCache()
739 protected String
generateNameCache(){
740 if (cacheStrategy
== null){
741 logger
.warn("No CacheStrategy defined for taxonName: " + this.toString());
744 return cacheStrategy
.getNameCache(this);
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.
755 * @return the string which identifies <i>this</i> non viral taxon name (without authors or year)
756 * @see #generateNameCache()
759 public String
getNameCache() {
760 if (protectedNameCache
){
761 return this.nameCache
;
763 // is title dirty, i.e. equal NULL?
764 if (nameCache
== null){
765 this.nameCache
= generateNameCache();
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>.
774 * @param nameCache the string which identifies <i>this</i> non viral taxon name (without authors or year)
775 * @see #getNameCache()
777 public void setNameCache(String nameCache
){
778 setNameCache(nameCache
, true);
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>.
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
788 * @see #getNameCache()
790 public void setNameCache(String nameCache
, boolean protectedNameCache
){
791 this.nameCache
= nameCache
;
792 this.setProtectedNameCache(protectedNameCache
);
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.
800 * @return the boolean value of the protectedNameCache flag
801 * @see #getNameCache()
803 public boolean isProtectedNameCache() {
804 return protectedNameCache
;
808 * @see #isProtectedNameCache()
810 public void setProtectedNameCache(boolean protectedNameCache
) {
811 this.protectedNameCache
= protectedNameCache
;
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}.
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)
824 public String
generateAuthorship(){
825 if (cacheStrategy
== null){
826 logger
.warn("No CacheStrategy defined for nonViralName: " + this.getUuid());
829 return ((INonViralNameCacheStrategy
<T
>)cacheStrategy
).getAuthorshipCache((T
)this);
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.
841 * @return the string with the concatenated and formated authorteams for <i>this</i> non viral taxon name
842 * @see #generateAuthorship()
845 public String
getAuthorshipCache() {
846 if (protectedAuthorshipCache
){
847 return this.authorshipCache
;
849 if (this.authorshipCache
== null ){
850 this.authorshipCache
= generateAuthorship();
852 //TODO get is Dirty of authors, make better if possible
853 this.setAuthorshipCache(generateAuthorship(), protectedAuthorshipCache
); //throw change event to inform higher caches
856 return authorshipCache
;
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
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);
877 * Assigns an authorshipCache string to <i>this</i> non viral taxon name. Sets the isProtectedAuthorshipCache
878 * flag to <code>true</code>.
880 * @param authorshipCache the string which identifies the complete authorship of <i>this</i> non viral taxon name
881 * @see #getAuthorshipCache()
883 public void setAuthorshipCache(String authorshipCache
) {
884 setAuthorshipCache(authorshipCache
, true);
889 public String
getFullTitleCache(){
890 updateAuthorshipCache();
891 return super.getFullTitleCache();
895 public String
getTitleCache(){
896 if(!protectedTitleCache
) {
897 updateAuthorshipCache();
900 return super.getTitleCache();
905 * Assigns an authorshipCache string to <i>this</i> non viral taxon name.
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()
912 public void setAuthorshipCache(String authorshipCache
, boolean protectedAuthorshipCache
) {
913 this.authorshipCache
= authorshipCache
;
914 this.setProtectedAuthorshipCache(protectedAuthorshipCache
);
918 public void setTitleCache(String titleCache
, boolean protectCache
){
919 super.setTitleCache(titleCache
, protectCache
);
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.
933 * @see TaxonNameBase#isCodeCompliant()
937 public boolean isCodeCompliant() {
939 logger
.warn("is CodeCompliant not yet implemented");
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}.
954 * @see #isCodeCompliant()
955 * @see TaxonNameBase#getHasProblem()
959 public NomenclaturalCode
getNomenclaturalCode() {
960 logger
.warn("Non Viral Name has no specific Code defined. Use subclasses");
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.
969 * @return the boolean value of the protectedAuthorshipCache flag
970 * @see #getAuthorshipCache()
972 public boolean isProtectedAuthorshipCache() {
973 return protectedAuthorshipCache
;
977 * @see #isProtectedAuthorshipCache()
978 * @see #getAuthorshipCache()
980 public void setProtectedAuthorshipCache(boolean protectedAuthorshipCache
) {
981 this.protectedAuthorshipCache
= protectedAuthorshipCache
;
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
994 * @return the boolean value of the isHybridFormula flag
995 * @see #isMonomHybrid()
996 * @see #isBinomHybrid()
997 * @see #isTrinomHybrid()
999 public boolean isHybridFormula(){
1000 return this.hybridFormula
;
1004 * @see #isHybridFormula()
1006 public void setHybridFormula(boolean hybridFormula
){
1007 this.hybridFormula
= hybridFormula
;
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.
1016 * @return the boolean value of the isMonomHybrid flag
1017 * @see #isHybridFormula()
1018 * @see #isBinomHybrid()
1019 * @see #isTrinomHybrid()
1021 public boolean isMonomHybrid(){
1022 return this.monomHybrid
;
1026 * @see #isMonomHybrid()
1027 * @see #isBinomHybrid()
1028 * @see #isTrinomHybrid()
1030 public void setMonomHybrid(boolean monomHybrid
){
1031 this.monomHybrid
= monomHybrid
;
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.
1040 * @return the boolean value of the isBinomHybrid flag
1041 * @see #isHybridFormula()
1042 * @see #isMonomHybrid()
1043 * @see #isTrinomHybrid()
1045 public boolean isBinomHybrid(){
1046 return this.binomHybrid
;
1050 * @see #isBinomHybrid()
1051 * @see #isMonomHybrid()
1052 * @see #isTrinomHybrid()
1054 public void setBinomHybrid(boolean binomHybrid
){
1055 this.binomHybrid
= binomHybrid
;
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.
1065 * @return the boolean value of the isTrinomHybrid flag
1066 * @see #isHybridFormula()
1067 * @see #isMonomHybrid()
1068 * @see #isBinomHybrid()
1070 public boolean isTrinomHybrid(){
1071 return this.trinomHybrid
;
1075 * @see #isTrinomHybrid()
1076 * @see #isBinomHybrid()
1077 * @see #isMonomHybrid()
1079 public void setTrinomHybrid(boolean trinomHybrid
){
1080 this.trinomHybrid
= trinomHybrid
;
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}.
1088 * @see #getHybridRelationships()
1089 * @see #getChildRelationships()
1090 * @see HybridRelationshipType
1092 public Set
<HybridRelationship
> getHybridParentRelations() {
1093 if(hybridParentRelations
== null) {
1094 this.hybridParentRelations
= new HashSet
<HybridRelationship
>();
1096 return hybridParentRelations
;
1099 private void setHybridParentRelations(Set
<HybridRelationship
> hybridParentRelations
) {
1100 this.hybridParentRelations
= hybridParentRelations
;
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}.
1108 * @see #getHybridRelationships()
1109 * @see #getParentRelationships()
1110 * @see HybridRelationshipType
1112 public Set
<HybridRelationship
> getHybridChildRelations() {
1113 if(hybridChildRelations
== null) {
1114 this.hybridChildRelations
= new HashSet
<HybridRelationship
>();
1116 return hybridChildRelations
;
1119 private void setHybridChildRelations(Set
<HybridRelationship
> hybridChildRelations
) {
1120 this.hybridChildRelations
= hybridChildRelations
;
1124 * Returns the hybrid child relationships ordered by relationship type, or if equal
1125 * by title cache of the related names.
1126 * @see #getHybridParentRelations()
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
);
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.
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
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
);
1164 throw new IllegalArgumentException("Hybrid relationship is either null or the relationship does not reference this name");
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.
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
1183 @Deprecated //to be used by RelationshipBase only
1184 public void addRelationship(RelationshipBase relation
) {
1185 if (relation
instanceof HybridRelationship
){
1186 addHybridRelationship((HybridRelationship
)relation
);
1188 super.addRelationship(relation
);
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
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
1203 * @see #addHybridChild(BotanicalName, HybridRelationshipType,String )
1204 * @see #getRelationsToThisName()
1205 * @see #getNameRelations()
1206 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
1207 * @see #addNameRelationship(NameRelationship)
1209 public HybridRelationship
addHybridParent(NonViralName parentName
, HybridRelationshipType type
, String ruleConsidered
){
1210 return new HybridRelationship(this, parentName
, type
, ruleConsidered
);
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
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
1224 * @see #addHybridParent(BotanicalName, HybridRelationshipType,String )
1225 * @see #getRelationsToThisName()
1226 * @see #getNameRelations()
1227 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
1228 * @see #addNameRelationship(NameRelationship)
1230 public HybridRelationship
addHybridChild(NonViralName childName
, HybridRelationshipType type
, String ruleConsidered
){
1231 return new HybridRelationship(childName
, this, type
, ruleConsidered
);
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.
1241 * @param relationship the hybrid relationship which should be deleted from the corresponding sets
1242 * @see #getHybridRelationships()
1244 public void removeHybridRelationship(HybridRelationship hybridRelation
) {
1245 if (hybridRelation
== null) {
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);
1258 if (this.equals(child
)){
1259 parent
.hybridParentRelations
.remove(hybridRelation
);
1260 this.hybridChildRelations
.remove(hybridRelation
);
1261 hybridRelation
.setHybridName(null);
1262 hybridRelation
.setParentName(null);
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
);
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
);
1295 * Needs to be implemented by those classes that handle autonyms (e.g. botanical names).
1298 public boolean isAutonym(){
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.
1310 // * @see #isSupraGeneric()
1311 // * @see #isGenus()
1312 // * @see #isSpeciesAggregate()
1313 // * @see #isSpecies()
1314 // * @see #isInfraSpecific()
1317 // public boolean isInfragenericUnranked() {
1318 // Rank rank = this.getRank();
1319 // if (rank == null || ! rank.equals(Rank.UNRANKED())){
1322 // if (StringUtils.isBlank(this.getSpecificEpithet()) && StringUtils.isBlank(this.getInfraSpecificEpithet()) ){
1331 * Tests if the given name has any authors.
1332 * @return false if no author ((ex)combination or (ex)basionym) exists, true otherwise
1334 public boolean hasAuthors() {
1335 return (this.getCombinationAuthorTeam() != null ||
1336 this.getExCombinationAuthorTeam() != null ||
1337 this.getBasionymAuthorTeam() != null ||
1338 this.getExBasionymAuthorTeam() != null);
1342 * Shortcut. Returns the combination authors title cache. Returns null if no combination author exists.
1345 public String
computeCombinationAuthorNomenclaturalTitle() {
1346 return computeNomenclaturalTitle(this.getCombinationAuthorTeam());
1350 * Shortcut. Returns the basionym authors title cache. Returns null if no basionym author exists.
1353 public String
computeBasionymAuthorNomenclaturalTitle() {
1354 return computeNomenclaturalTitle(this.getBasionymAuthorTeam());
1359 * Shortcut. Returns the ex-combination authors title cache. Returns null if no ex-combination author exists.
1362 public String
computeExCombinationAuthorNomenclaturalTitle() {
1363 return computeNomenclaturalTitle(this.getExCombinationAuthorTeam());
1367 * Shortcut. Returns the ex-basionym authors title cache. Returns null if no exbasionym author exists.
1370 public String
computeExBasionymAuthorNomenclaturalTitle() {
1371 return computeNomenclaturalTitle(this.getExBasionymAuthorTeam());
1374 private String
computeNomenclaturalTitle(INomenclaturalAuthor author
){
1375 if (author
== null){
1378 return author
.getNomenclaturalTitle();
1382 //*********************** CLONE ********************************************************/
1385 * Clones <i>this</i> non-viral name. This is a shortcut that enables to create
1386 * a new instance that differs only slightly from <i>this</i> non-viral name by
1387 * modifying only some of the attributes.
1389 * @see eu.etaxonomy.cdm.model.name.TaxonNameBase#clone()
1390 * @see java.lang.Object#clone()
1393 public Object
clone() {
1394 NonViralName
<?
> result
= (NonViralName
<?
>)super.clone();
1396 //HybridChildRelations
1397 result
.hybridChildRelations
= new HashSet
<HybridRelationship
>();
1398 for (HybridRelationship hybridRelationship
: getHybridChildRelations()){
1399 HybridRelationship newChildRelationship
= (HybridRelationship
)hybridRelationship
.clone();
1400 newChildRelationship
.setRelatedTo(result
);
1401 result
.hybridChildRelations
.add(newChildRelationship
);
1404 //HybridParentRelations
1405 result
.hybridParentRelations
= new HashSet
<HybridRelationship
>();
1406 for (HybridRelationship hybridRelationship
: getHybridParentRelations()){
1407 HybridRelationship newParentRelationship
= (HybridRelationship
)hybridRelationship
.clone();
1408 newParentRelationship
.setRelatedFrom(result
);
1409 result
.hybridParentRelations
.add(newParentRelationship
);
1413 if (! protectedNameCache
){
1414 result
.nameCache
= null;
1418 if (! protectedAuthorshipCache
){
1419 result
.authorshipCache
= null;
1422 //no changes to: basionamyAuthorTeam, combinationAuthorTeam, exBasionymAuthorTeam, exCombinationAuthorTeam
1423 //genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
1424 //protectedAuthorshipCache, protectedNameCache
1425 //binomHybrid, monomHybrid, trinomHybrid, hybridFormula,