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
;
12 import java
.beans
.PropertyChangeEvent
;
13 import java
.beans
.PropertyChangeListener
;
14 import java
.lang
.reflect
.Method
;
15 import java
.util
.ArrayList
;
16 import java
.util
.Collections
;
17 import java
.util
.HashSet
;
18 import java
.util
.List
;
22 import javax
.persistence
.Column
;
23 import javax
.persistence
.Entity
;
24 import javax
.persistence
.FetchType
;
25 import javax
.persistence
.Inheritance
;
26 import javax
.persistence
.InheritanceType
;
27 import javax
.persistence
.JoinTable
;
28 import javax
.persistence
.ManyToMany
;
29 import javax
.persistence
.ManyToOne
;
30 import javax
.persistence
.OneToMany
;
31 import javax
.persistence
.Table
;
32 import javax
.persistence
.Transient
;
33 import javax
.validation
.Valid
;
34 import javax
.validation
.constraints
.Min
;
35 import javax
.validation
.constraints
.NotNull
;
36 import javax
.validation
.constraints
.Pattern
;
37 import javax
.xml
.bind
.annotation
.XmlAccessType
;
38 import javax
.xml
.bind
.annotation
.XmlAccessorType
;
39 import javax
.xml
.bind
.annotation
.XmlAttribute
;
40 import javax
.xml
.bind
.annotation
.XmlElement
;
41 import javax
.xml
.bind
.annotation
.XmlElementWrapper
;
42 import javax
.xml
.bind
.annotation
.XmlIDREF
;
43 import javax
.xml
.bind
.annotation
.XmlRootElement
;
44 import javax
.xml
.bind
.annotation
.XmlSchemaType
;
45 import javax
.xml
.bind
.annotation
.XmlType
;
47 import org
.apache
.commons
.lang
.StringUtils
;
48 import org
.apache
.log4j
.Logger
;
49 import org
.hibernate
.annotations
.Cascade
;
50 import org
.hibernate
.annotations
.CascadeType
;
51 import org
.hibernate
.annotations
.Type
;
52 import org
.hibernate
.envers
.Audited
;
53 import org
.hibernate
.search
.annotations
.Analyze
;
54 import org
.hibernate
.search
.annotations
.Analyzer
;
55 import org
.hibernate
.search
.annotations
.Field
;
56 import org
.hibernate
.search
.annotations
.Fields
;
57 import org
.hibernate
.search
.annotations
.Index
;
58 import org
.hibernate
.search
.annotations
.Indexed
;
59 import org
.hibernate
.search
.annotations
.IndexedEmbedded
;
60 import org
.hibernate
.search
.annotations
.Store
;
61 import org
.hibernate
.validator
.constraints
.NotEmpty
;
62 import org
.springframework
.util
.ReflectionUtils
;
64 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
65 import eu
.etaxonomy
.cdm
.common
.UTF8
;
66 import eu
.etaxonomy
.cdm
.model
.EntityCollectionSetterAdapter
;
67 import eu
.etaxonomy
.cdm
.model
.EntityCollectionSetterAdapter
.SetterAdapterException
;
68 import eu
.etaxonomy
.cdm
.model
.agent
.INomenclaturalAuthor
;
69 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
70 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
71 import eu
.etaxonomy
.cdm
.model
.common
.IIntextReferenceTarget
;
72 import eu
.etaxonomy
.cdm
.model
.common
.IParsable
;
73 import eu
.etaxonomy
.cdm
.model
.common
.IRelated
;
74 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
75 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
76 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
77 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
78 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementSource
;
79 import eu
.etaxonomy
.cdm
.model
.description
.IDescribable
;
80 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
81 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
82 import eu
.etaxonomy
.cdm
.model
.reference
.INomenclaturalReference
;
83 import eu
.etaxonomy
.cdm
.model
.reference
.OriginalSourceType
;
84 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
85 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
86 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
87 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
88 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
89 import eu
.etaxonomy
.cdm
.strategy
.cache
.name
.CacheUpdate
;
90 import eu
.etaxonomy
.cdm
.strategy
.cache
.name
.INameCacheStrategy
;
91 import eu
.etaxonomy
.cdm
.strategy
.cache
.name
.TaxonNameDefaultCacheStrategy
;
92 import eu
.etaxonomy
.cdm
.strategy
.match
.IMatchable
;
93 import eu
.etaxonomy
.cdm
.strategy
.match
.Match
;
94 import eu
.etaxonomy
.cdm
.strategy
.match
.Match
.ReplaceMode
;
95 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchMode
;
96 import eu
.etaxonomy
.cdm
.strategy
.merge
.Merge
;
97 import eu
.etaxonomy
.cdm
.strategy
.merge
.MergeMode
;
98 import eu
.etaxonomy
.cdm
.strategy
.parser
.ParserProblem
;
99 import eu
.etaxonomy
.cdm
.validation
.Level2
;
100 import eu
.etaxonomy
.cdm
.validation
.Level3
;
101 import eu
.etaxonomy
.cdm
.validation
.annotation
.CorrectEpithetsForRank
;
102 import eu
.etaxonomy
.cdm
.validation
.annotation
.NameMustFollowCode
;
103 import eu
.etaxonomy
.cdm
.validation
.annotation
.NameMustHaveAuthority
;
104 import eu
.etaxonomy
.cdm
.validation
.annotation
.NoDuplicateNames
;
105 import eu
.etaxonomy
.cdm
.validation
.annotation
.NullOrNotEmpty
;
106 import eu
.etaxonomy
.cdm
.validation
.annotation
.ValidTaxonomicYear
;
109 * The upmost (abstract) class for scientific taxon names regardless of any
110 * particular {@link NomenclaturalCode nomenclature code}. The scientific taxon name does not depend
111 * on the use made of it in a publication or a treatment
112 * ({@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon concept respectively potential taxon})
113 * as an {@link eu.etaxonomy.cdm.model.taxon.Taxon "accepted" respectively "correct" (taxon) name}
114 * or as a {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
116 * This class corresponds partially to: <ul>
117 * <li> TaxonName according to the TDWG ontology
118 * <li> ScientificName and CanonicalName according to the TCS
119 * <li> ScientificName according to the ABCD schema
123 * @since 08-Nov-2007 13:06:57
125 @XmlAccessorType(XmlAccessType
.FIELD
)
126 @XmlType(name
= "TaxonName", propOrder
= {
129 "nomenclaturalMicroReference",
130 "nomenclaturalReference",
131 "nomenclaturalSource",
134 "protectedFullTitleCache",
137 "relationsFromThisName",
138 "relationsToThisName",
146 "infraGenericEpithet",
148 "infraSpecificEpithet",
149 "combinationAuthorship",
150 "exCombinationAuthorship",
151 "basionymAuthorship",
152 "exBasionymAuthorship",
154 "protectedAuthorshipCache",
155 "protectedNameCache",
156 "hybridParentRelations",
157 "hybridChildRelations",
165 "subGenusAuthorship",
170 "originalPublicationYear",
171 "inCombinationAuthorship",
172 "inBasionymAuthorship",
178 @XmlRootElement(name
= "TaxonName")
181 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
182 @Table(name
="TaxonName", indexes
= {
183 @javax.persistence
.Index(name
= "taxonNameBaseTitleCacheIndex", columnList
= "titleCache"),
184 @javax.persistence
.Index(name
= "taxonNameBaseNameCacheIndex", columnList
= "nameCache") })
186 @CorrectEpithetsForRank(groups
= Level2
.class)
187 @NameMustHaveAuthority(groups
= Level2
.class)
188 @NoDuplicateNames(groups
= Level3
.class)
189 @Indexed(index
= "eu.etaxonomy.cdm.model.name.TaxonName")
190 public class TaxonName
191 extends IdentifiableEntity
<INameCacheStrategy
>
192 implements ITaxonNameBase
, INonViralName
, IViralName
, IBacterialName
, IZoologicalName
,
193 IBotanicalName
, ICultivarPlantName
, IFungusName
,
194 IParsable
, IRelated
, IMatchable
, IIntextReferenceTarget
, Cloneable
,
195 IDescribable
<TaxonNameDescription
>{
197 private static final long serialVersionUID
= -791164269603409712L;
198 private static final Logger logger
= Logger
.getLogger(TaxonName
.class);
202 * The {@link NomenclaturalCode nomenclatural code} this taxon name is ruled by.
204 @XmlAttribute(name
="NameType")
205 @Column(name
="nameType", length
=15)
207 @Type(type
= "eu.etaxonomy.cdm.hibernate.EnumUserType",
208 parameters
= {@org.hibernate
.annotations
.Parameter(name
= "enumClass", value
= "eu.etaxonomy.cdm.model.name.NomenclaturalCode")}
211 private NomenclaturalCode nameType
;
213 @XmlElement(name
= "FullTitleCache")
214 @Column(length
=800, name
="fullTitleCache") //see #1592
215 @Match(value
=MatchMode
.CACHE
, cacheReplaceMode
=ReplaceMode
.ALL
)
216 @CacheUpdate(noUpdate
="titleCache")
217 @NotEmpty(groups
= Level2
.class)
218 protected String fullTitleCache
;
220 //if true titleCache will not be automatically generated/updated
221 @XmlElement(name
= "ProtectedFullTitleCache")
222 @CacheUpdate(value
="fullTitleCache", noUpdate
="titleCache")
223 private boolean protectedFullTitleCache
;
225 @XmlElementWrapper(name
= "Descriptions")
226 @XmlElement(name
= "Description")
227 @OneToMany(mappedBy
="taxonName", fetch
= FetchType
.LAZY
, orphanRemoval
=true)
228 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
230 private Set
<TaxonNameDescription
> descriptions
= new HashSet
<>();
232 @XmlElement(name
= "AppendedPhrase")
234 @CacheUpdate(value
="nameCache")
238 private String appendedPhrase
;
241 @XmlElement(name
= "NomenclaturalMicroReference")
243 @CacheUpdate(noUpdate
="titleCache")
247 private String nomenclaturalMicroReference
;
250 @CacheUpdate(noUpdate
={"titleCache","fullTitleCache"})
251 private int parsingProblem
= 0;
254 @CacheUpdate(noUpdate
={"titleCache","fullTitleCache"})
255 private int problemStarts
= -1;
258 @CacheUpdate(noUpdate
={"titleCache","fullTitleCache"})
259 private int problemEnds
= -1;
261 @XmlElementWrapper(name
= "TypeDesignations")
262 @XmlElement(name
= "TypeDesignation")
264 @XmlSchemaType(name
= "IDREF")
265 @ManyToMany(fetch
= FetchType
.LAZY
)
267 name
="TaxonName_TypeDesignationBase",
268 joinColumns
=@javax.persistence
.JoinColumn(name
="TaxonName_id"),
269 inverseJoinColumns
=@javax.persistence
.JoinColumn(name
="typedesignations_id")
271 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
273 private Set
<TypeDesignationBase
> typeDesignations
= new HashSet
<>();
275 @XmlElement(name
= "HomotypicalGroup")
277 @XmlSchemaType(name
= "IDREF")
278 @ManyToOne(fetch
= FetchType
.LAZY
)
279 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
280 @Match(MatchMode
.IGNORE
)
281 @CacheUpdate(noUpdate
="titleCache")
284 private HomotypicalGroup homotypicalGroup
;
286 @XmlElementWrapper(name
= "RelationsFromThisName")
287 @XmlElement(name
= "RelationFromThisName")
288 @OneToMany(mappedBy
="relatedFrom", fetch
= FetchType
.LAZY
, orphanRemoval
=true)
289 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
290 @Merge(MergeMode
.RELATION
)
293 private Set
<NameRelationship
> relationsFromThisName
= new HashSet
<>();
295 @XmlElementWrapper(name
= "RelationsToThisName")
296 @XmlElement(name
= "RelationToThisName")
298 @XmlSchemaType(name
= "IDREF")
299 @OneToMany(mappedBy
="relatedTo", fetch
= FetchType
.LAZY
, orphanRemoval
=true)
300 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
301 @Merge(MergeMode
.RELATION
)
304 private Set
<NameRelationship
> relationsToThisName
= new HashSet
<>();
306 @XmlElementWrapper(name
= "NomenclaturalStatuses")
307 @XmlElement(name
= "NomenclaturalStatus")
308 @OneToMany(fetch
= FetchType
.LAZY
, orphanRemoval
=true)
309 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
,CascadeType
.DELETE
})
311 @IndexedEmbedded(depth
=1)
312 private Set
<NomenclaturalStatus
> status
= new HashSet
<>();
314 @XmlElementWrapper(name
= "TaxonBases")
315 @XmlElement(name
= "TaxonBase")
317 @XmlSchemaType(name
= "IDREF")
318 @OneToMany(mappedBy
="name", fetch
= FetchType
.LAZY
)
320 @IndexedEmbedded(depth
=1)
321 private Set
<TaxonBase
> taxonBases
= new HashSet
<>();
323 @XmlElement(name
= "Rank")
325 @XmlSchemaType(name
= "IDREF")
326 @ManyToOne(fetch
= FetchType
.EAGER
)
327 @CacheUpdate(value
="nameCache")
328 //TODO Val #3379, handle maybe as groups = Level2.class ??
330 @IndexedEmbedded(depth
=1)
334 @XmlElement(name
= "NomenclaturalReference")
336 @XmlSchemaType(name
= "IDREF")
337 @ManyToOne(fetch
= FetchType
.LAZY
)
338 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
339 @CacheUpdate(noUpdate
="titleCache")
341 private Reference nomenclaturalReference
;
344 @XmlElement(name
= "NomenclaturalSource")
346 @XmlSchemaType(name
= "IDREF")
347 @ManyToOne(fetch
= FetchType
.LAZY
)
348 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
349 @CacheUpdate(noUpdate
="titleCache")
351 private DescriptionElementSource nomenclaturalSource
;
353 @XmlElementWrapper(name
= "Registrations")
354 @XmlElement(name
= "Registration")
356 @XmlSchemaType(name
= "IDREF")
357 @OneToMany(mappedBy
="name", fetch
= FetchType
.LAZY
)
358 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
360 @IndexedEmbedded(depth
=1)
361 private Set
<Registration
> registrations
= new HashSet
<>();
363 //****** Non-ViralName attributes ***************************************/
365 @XmlElement(name
= "NameCache")
367 @Field(name
= "nameCache_tokenized"),
368 @Field(store
= Store
.YES
, index
= Index
.YES
, analyze
= Analyze
.YES
)
370 @Analyzer(impl
= org
.apache
.lucene
.analysis
.core
.KeywordAnalyzer
.class)
371 @Match(value
=MatchMode
.CACHE
, cacheReplaceMode
=ReplaceMode
.DEFINED
,
372 cacheReplacedProperties
={"genusOrUninomial", "infraGenericEpithet", "specificEpithet", "infraSpecificEpithet"} )
373 @NotEmpty(groups
= Level2
.class) // implicitly NotNull
375 private String nameCache
;
377 @XmlElement(name
= "ProtectedNameCache")
378 @CacheUpdate(value
="nameCache")
379 protected boolean protectedNameCache
;
381 @XmlElement(name
= "GenusOrUninomial")
382 @Field(analyze
= Analyze
.YES
, indexNullAs
=Field
.DEFAULT_NULL_TOKEN
)
383 @Match(MatchMode
.EQUAL_REQUIRED
)
384 @CacheUpdate("nameCache")
386 @Pattern(regexp
= "[A-Z][a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups
=Level2
.class, message
="{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForUninomial.message}")
388 @NotNull(groups
= Level2
.class)
389 private String genusOrUninomial
;
391 @XmlElement(name
= "InfraGenericEpithet")
392 @Field(analyze
= Analyze
.YES
,indexNullAs
=Field
.DEFAULT_NULL_TOKEN
)
393 @CacheUpdate("nameCache")
397 @Pattern(regexp
= "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups
=Level2
.class,message
="{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}")
398 private String infraGenericEpithet
;
400 @XmlElement(name
= "SpecificEpithet")
401 @Field(analyze
= Analyze
.YES
,indexNullAs
=Field
.DEFAULT_NULL_TOKEN
)
402 @CacheUpdate("nameCache")
406 @Pattern(regexp
= "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups
=Level2
.class, message
= "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}")
407 private String specificEpithet
;
409 @XmlElement(name
= "InfraSpecificEpithet")
410 @Field(analyze
= Analyze
.YES
,indexNullAs
=Field
.DEFAULT_NULL_TOKEN
)
411 @CacheUpdate("nameCache")
415 @Pattern(regexp
= "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups
=Level2
.class, message
= "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}")
416 private String infraSpecificEpithet
;
418 @XmlElement(name
= "CombinationAuthorship")
420 @XmlSchemaType(name
= "IDREF")
421 @ManyToOne(fetch
= FetchType
.LAZY
)
422 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
423 @CacheUpdate("authorshipCache")
425 private TeamOrPersonBase
<?
> combinationAuthorship
;
427 @XmlElement(name
= "ExCombinationAuthorship")
429 @XmlSchemaType(name
= "IDREF")
430 @ManyToOne(fetch
= FetchType
.LAZY
)
431 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
432 @CacheUpdate("authorshipCache")
434 private TeamOrPersonBase
<?
> exCombinationAuthorship
;
437 @XmlElement(name
= "InCombinationAuthorship")
439 @XmlSchemaType(name
= "IDREF")
440 @ManyToOne(fetch
= FetchType
.LAZY
)
441 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
442 @CacheUpdate("authorshipCache")
444 private TeamOrPersonBase
<?
> inCombinationAuthorship
;
446 @XmlElement(name
= "BasionymAuthorship")
448 @XmlSchemaType(name
= "IDREF")
449 @ManyToOne(fetch
= FetchType
.LAZY
)
450 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
451 @CacheUpdate("authorshipCache")
453 private TeamOrPersonBase
<?
> basionymAuthorship
;
455 @XmlElement(name
= "ExBasionymAuthorship")
457 @XmlSchemaType(name
= "IDREF")
458 @ManyToOne(fetch
= FetchType
.LAZY
)
459 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
460 @CacheUpdate("authorshipCache")
462 private TeamOrPersonBase
<?
> exBasionymAuthorship
;
465 @XmlElement(name
= "InBasionymAuthorship")
467 @XmlSchemaType(name
= "IDREF")
468 @ManyToOne(fetch
= FetchType
.LAZY
)
469 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
470 @CacheUpdate("authorshipCache")
472 private TeamOrPersonBase
<?
> inBasionymAuthorship
;
474 @XmlElement(name
= "AuthorshipCache")
476 @Field(name
= "authorshipCache_tokenized"),
477 @Field(analyze
= Analyze
.NO
)
479 @Match(value
=MatchMode
.CACHE
, cacheReplaceMode
=ReplaceMode
.DEFINED
,
480 cacheReplacedProperties
={"combinationAuthorship", "basionymAuthorship", "exCombinationAuthorship", "exBasionymAuthorship"} )
484 @Pattern(regexp
= "^[A-Za-z0-9 \\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-\\&\\,\\(\\)\\.]+$", groups
=Level2
.class, message
= "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForAuthority.message}")
485 private String authorshipCache
;
487 @XmlElement(name
= "ProtectedAuthorshipCache")
488 @CacheUpdate("authorshipCache")
489 protected boolean protectedAuthorshipCache
;
491 @XmlElementWrapper(name
= "HybridRelationsFromThisName")
492 @XmlElement(name
= "HybridRelationsFromThisName")
493 @OneToMany(mappedBy
="relatedFrom", fetch
= FetchType
.LAZY
)
494 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
495 @Merge(MergeMode
.RELATION
)
497 private Set
<HybridRelationship
> hybridParentRelations
= new HashSet
<>();
499 @XmlElementWrapper(name
= "HybridRelationsToThisName")
500 @XmlElement(name
= "HybridRelationsToThisName")
501 @OneToMany(mappedBy
="relatedTo", fetch
= FetchType
.LAZY
, orphanRemoval
=true) //a hybrid relation can be deleted automatically if the child is deleted.
502 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
503 @Merge(MergeMode
.RELATION
)
505 private Set
<HybridRelationship
> hybridChildRelations
= new HashSet
<>();
508 //if set: this name is a hybrid formula (a hybrid that does not have an own name) and no
509 //other hybrid flags may be set. A
510 //hybrid name may not have either an authorteam nor other name components.
511 @XmlElement(name
="IsHybridFormula")
512 @CacheUpdate("nameCache")
513 private boolean hybridFormula
= false;
515 @XmlElement(name
="IsMonomHybrid")
516 @CacheUpdate("nameCache")
517 private boolean monomHybrid
= false;
519 @XmlElement(name
="IsBinomHybrid")
520 @CacheUpdate("nameCache")
521 private boolean binomHybrid
= false;
523 @XmlElement(name
="IsTrinomHybrid")
524 @CacheUpdate("nameCache")
525 private boolean trinomHybrid
= false;
527 // ViralName attributes ************************* /
529 @XmlElement(name
= "Acronym")
534 private String acronym
;
536 // BacterialName attributes ***********************/
538 //Author team and year of the subgenus name
539 @XmlElement(name
= "SubGenusAuthorship")
541 private String subGenusAuthorship
;
543 //Approbation of name according to approved list, validation list, or validly published, paper in IJSB after 1980
544 @XmlElement(name
= "NameApprobation")
546 private String nameApprobation
;
550 //Name of the breed of an animal
551 @XmlElement(name
= "Breed")
555 private String breed
;
557 @XmlElement(name
= "PublicationYear")
558 @Field(analyze
= Analyze
.NO
)
559 @CacheUpdate(value
="authorshipCache")
561 private Integer publicationYear
;
563 @XmlElement(name
= "OriginalPublicationYear")
564 @Field(analyze
= Analyze
.NO
)
565 @CacheUpdate(value
="authorshipCache")
567 private Integer originalPublicationYear
;
569 //Cultivar attribute(s)
571 //the characteristical name of the cultivar
572 @XmlElement(name
= "CultivarName")
576 private String cultivarName
;
578 // ************** FUNGUS name attributes
579 //to indicate that the type of the name is asexual or not
580 @XmlElement(name
="IsAnamorphic")
581 private boolean anamorphic
= false;
583 // *************** FACTORY METHODS ********************************/
585 //see TaxonNameFactory
589 * @param homotypicalGroup
592 protected static TaxonName
NewInstance(NomenclaturalCode code
, Rank rank
,
593 HomotypicalGroup homotypicalGroup
) {
594 TaxonName result
= new TaxonName(code
, rank
, homotypicalGroup
);
602 * @param genusOrUninomial2
603 * @param infraGenericEpithet2
604 * @param specificEpithet2
605 * @param infraSpecificEpithet2
606 * @param combinationAuthorship2
607 * @param nomenclaturalReference2
608 * @param nomenclMicroRef
609 * @param homotypicalGroup2
612 public static TaxonName
NewInstance(NomenclaturalCode code
, Rank rank
, String genusOrUninomial
,
613 String infraGenericEpithet
, String specificEpithet
, String infraSpecificEpithet
,
614 TeamOrPersonBase combinationAuthorship
, Reference nomenclaturalReference
,
615 String nomenclMicroRef
, HomotypicalGroup homotypicalGroup
) {
616 TaxonName result
= new TaxonName(code
, rank
, genusOrUninomial
, infraGenericEpithet
, specificEpithet
, infraSpecificEpithet
, combinationAuthorship
, nomenclaturalReference
, nomenclMicroRef
, homotypicalGroup
);
621 // ************* CONSTRUCTORS *************/
623 * Class constructor: creates a new empty taxon name.
626 * @see #TaxonName(Rank)
627 * @see #TaxonName(HomotypicalGroup)
628 * @see #TaxonName(Rank, HomotypicalGroup)
630 protected TaxonName() {
632 rectifyNameCacheStrategy();
637 * Class constructor: creates a new taxon name instance
638 * only containing its {@link Rank rank} and
639 * its {@link HomotypicalGroup homotypical group} and
640 * the {@link eu.etaxonomy.cdm.strategy.cache.name.TaxonNameDefaultCacheStrategy default cache strategy}.
641 * The new taxon name will be also added to the set of taxon names
642 * belonging to this homotypical group.
644 * @param rank the rank to be assigned to <i>this</i> taxon name
645 * @param homotypicalGroup the homotypical group to which <i>this</i> taxon name belongs
647 * @see #TaxonName(Rank)
648 * @see #TaxonName(HomotypicalGroup)
650 protected TaxonName(NomenclaturalCode type
, Rank rank
, HomotypicalGroup homotypicalGroup
) {
654 if (homotypicalGroup
== null){
655 homotypicalGroup
= HomotypicalGroup
.NewInstance();
657 homotypicalGroup
.addTypifiedName(this);
658 this.homotypicalGroup
= homotypicalGroup
;
663 * Class constructor: creates a new non viral taxon name instance
664 * containing its {@link Rank rank},
665 * its {@link HomotypicalGroup homotypical group},
666 * its scientific name components, its {@link eu.etaxonomy.cdm.model.agent.TeamOrPersonBase author(team)},
667 * its {@link eu.etaxonomy.cdm.model.reference.Reference nomenclatural reference} and
668 * the {@link eu.etaxonomy.cdm.strategy.cache.name.TaxonNameDefaultCacheStrategy default cache strategy}.
669 * The new non viral taxon name instance will be also added to the set of
670 * non viral taxon names belonging to this homotypical group.
672 * @param rank the rank to be assigned to <i>this</i> non viral taxon name
673 * @param genusOrUninomial the string for <i>this</i> non viral taxon name
674 * if its rank is genus or higher or for the genus part
675 * if its rank is lower than genus
676 * @param infraGenericEpithet the string for the first epithet of
677 * <i>this</i> non viral taxon name if its rank is lower than genus
678 * and higher than species aggregate
679 * @param specificEpithet the string for the first epithet of
680 * <i>this</i> non viral taxon name if its rank is species aggregate or lower
681 * @param infraSpecificEpithet the string for the second epithet of
682 * <i>this</i> non viral taxon name if its rank is lower than species
683 * @param combinationAuthorship the author or the team who published <i>this</i> non viral taxon name
684 * @param nomenclaturalReference the nomenclatural reference where <i>this</i> non viral taxon name was published
685 * @param nomenclMicroRef the string with the details for precise location within the nomenclatural reference
686 * @param homotypicalGroup the homotypical group to which <i>this</i> non viral taxon name belongs
687 * @see #NewInstance(NomenclaturalCode, Rank, HomotypicalGroup)
688 * @see #NewInstance(NomenclaturalCode, Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup)
689 * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy
690 * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy
691 * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
693 protected TaxonName(NomenclaturalCode type
, Rank rank
, String genusOrUninomial
,
694 String infraGenericEpithet
, String specificEpithet
, String infraSpecificEpithet
,
695 TeamOrPersonBase combinationAuthorship
, Reference nomenclaturalReference
,
696 String nomenclMicroRef
, HomotypicalGroup homotypicalGroup
) {
697 this(type
, rank
, homotypicalGroup
);
698 setGenusOrUninomial(genusOrUninomial
);
699 setInfraGenericEpithet (infraGenericEpithet
);
700 setSpecificEpithet(specificEpithet
);
701 setInfraSpecificEpithet(infraSpecificEpithet
);
702 setCombinationAuthorship(combinationAuthorship
);
703 setNomenclaturalReference(nomenclaturalReference
);
704 this.setNomenclaturalMicroReference(nomenclMicroRef
);
709 * This method was originally needed to distinguish cache strategies
710 * depending on the name type. Now we have a unified cache strategy
711 * which does not require this anymore. Maybe we could even further remove this method.
713 private void rectifyNameCacheStrategy(){
714 if (this.cacheStrategy
== null){
715 this.cacheStrategy
= TaxonNameDefaultCacheStrategy
.NewInstance();
721 public void initListener(){
722 PropertyChangeListener listener
= new PropertyChangeListener() {
724 public void propertyChange(PropertyChangeEvent e
) {
725 boolean protectedByLowerCache
= false;
727 if (fieldHasCacheUpdateProperty(e
.getPropertyName(), "authorshipCache")){
728 if (protectedAuthorshipCache
){
729 protectedByLowerCache
= true;
731 authorshipCache
= null;
736 if (fieldHasCacheUpdateProperty(e
.getPropertyName(), "nameCache")){
737 if (protectedNameCache
){
738 protectedByLowerCache
= true;
744 if (! fieldHasNoUpdateProperty(e
.getPropertyName(), "titleCache")){
745 if (isProtectedTitleCache()|| protectedByLowerCache
== true ){
746 protectedByLowerCache
= true;
752 if (! fieldHasNoUpdateProperty(e
.getPropertyName(), "fullTitleCache")){
753 if (isProtectedFullTitleCache()|| protectedByLowerCache
== true ){
754 protectedByLowerCache
= true;
756 fullTitleCache
= null;
761 addPropertyChangeListener(listener
); //didn't use this.addXXX to make lsid.AssemblerTest run in cdmlib-remote
764 private static Map
<String
, java
.lang
.reflect
.Field
> allFields
= null;
765 protected Map
<String
, java
.lang
.reflect
.Field
> getAllFields(){
766 if (allFields
== null){
767 allFields
= CdmUtils
.getAllFields(this.getClass(), CdmBase
.class, false, false, false, true);
773 * @param propertyName
777 private boolean fieldHasCacheUpdateProperty(String propertyName
, String cacheName
) {
778 java
.lang
.reflect
.Field field
;
780 field
= getAllFields().get(propertyName
);
782 CacheUpdate updateAnnotation
= field
.getAnnotation(CacheUpdate
.class);
783 if (updateAnnotation
!= null){
784 for (String value
: updateAnnotation
.value()){
785 if (cacheName
.equals(value
)){
792 } catch (SecurityException e1
) {
797 private boolean fieldHasNoUpdateProperty(String propertyName
, String cacheName
) {
798 java
.lang
.reflect
.Field field
;
799 //do not update fields with the same name
800 if (cacheName
.equals(propertyName
)){
803 //evaluate annotation
805 field
= getAllFields().get(propertyName
);
807 CacheUpdate updateAnnotation
= field
.getAnnotation(CacheUpdate
.class);
808 if (updateAnnotation
!= null){
809 for (String value
: updateAnnotation
.noUpdate()){
810 if (cacheName
.equals(value
)){
817 } catch (SecurityException e1
) {
822 // ****************** GETTER / SETTER ****************************/
825 public NomenclaturalCode
getNameType() {
830 public void setNameType(NomenclaturalCode nameType
) {
831 this.nameType
= nameType
;
835 * Returns the boolean value of the flag intended to protect (true)
836 * or not (false) the {@link #getNameCache() nameCache} (scientific name without author strings and year)
837 * string of <i>this</i> non viral taxon name.
839 * @return the boolean value of the protectedNameCache flag
840 * @see #getNameCache()
843 public boolean isProtectedNameCache() {
844 return protectedNameCache
;
848 * @see #isProtectedNameCache()
851 public void setProtectedNameCache(boolean protectedNameCache
) {
852 this.protectedNameCache
= protectedNameCache
;
856 * Returns either the scientific name string (without authorship) for <i>this</i>
857 * non viral taxon name if its rank is genus or higher (monomial) or the string for
858 * the genus part of it if its {@link Rank rank} is lower than genus (bi- or trinomial).
859 * Genus or uninomial strings begin with an upper case letter.
861 * @return the string containing the suprageneric name, the genus name or the genus part of <i>this</i> non viral taxon name
862 * @see #getNameCache()
865 public String
getGenusOrUninomial() {
866 return genusOrUninomial
;
870 * @see #getGenusOrUninomial()
873 public void setGenusOrUninomial(String genusOrUninomial
) {
874 this.genusOrUninomial
= StringUtils
.isBlank(genusOrUninomial
) ?
null : genusOrUninomial
;
878 * Returns the genus subdivision epithet string (infrageneric part) for
879 * <i>this</i> non viral taxon name if its {@link Rank rank} is infrageneric (lower than genus and
880 * higher than species aggregate: binomial). Genus subdivision epithet
881 * strings begin with an upper case letter.
883 * @return the string containing the infrageneric part of <i>this</i> non viral taxon name
884 * @see #getNameCache()
887 public String
getInfraGenericEpithet(){
888 return this.infraGenericEpithet
;
892 * @see #getInfraGenericEpithet()
895 public void setInfraGenericEpithet(String infraGenericEpithet
){
896 this.infraGenericEpithet
= StringUtils
.isBlank(infraGenericEpithet
)?
null : infraGenericEpithet
;
900 * Returns the species epithet string for <i>this</i> non viral taxon name if its {@link Rank rank} is
901 * species aggregate or lower (bi- or trinomial). Species epithet strings
902 * begin with a lower case letter.
904 * @return the string containing the species epithet of <i>this</i> non viral taxon name
905 * @see #getNameCache()
908 public String
getSpecificEpithet(){
909 return this.specificEpithet
;
913 * @see #getSpecificEpithet()
916 public void setSpecificEpithet(String specificEpithet
){
917 this.specificEpithet
= StringUtils
.isBlank(specificEpithet
) ?
null : specificEpithet
;
921 * Returns the species subdivision epithet string (infraspecific part) for
922 * <i>this</i> non viral taxon name if its {@link Rank rank} is infraspecific
923 * (lower than species: trinomial). Species subdivision epithet strings
924 * begin with a lower case letter.
926 * @return the string containing the infraspecific part of <i>this</i> non viral taxon name
927 * @see #getNameCache()
930 public String
getInfraSpecificEpithet(){
931 return this.infraSpecificEpithet
;
935 * @see #getInfraSpecificEpithet()
938 public void setInfraSpecificEpithet(String infraSpecificEpithet
){
939 this.infraSpecificEpithet
= StringUtils
.isBlank(infraSpecificEpithet
)?
null : infraSpecificEpithet
;
943 * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that published <i>this</i> non viral
946 * @return the nomenclatural author (team) of <i>this</i> non viral taxon name
947 * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
948 * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
951 public TeamOrPersonBase
<?
> getCombinationAuthorship(){
952 return this.combinationAuthorship
;
956 * @see #getCombinationAuthorship()
959 public void setCombinationAuthorship(TeamOrPersonBase
<?
> combinationAuthorship
){
960 this.combinationAuthorship
= combinationAuthorship
;
964 * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that contributed to
965 * the publication of <i>this</i> non viral taxon name as generally stated by
966 * the {@link #getCombinationAuthorship() combination author (team)} itself.<BR>
967 * An ex-author(-team) is an author(-team) to whom a taxon name was ascribed
968 * although it is not the author(-team) of a valid publication (for instance
969 * without the validating description or diagnosis in case of a name for a
970 * new taxon). The name of this ascribed authorship, followed by "ex", may
971 * be inserted before the name(s) of the publishing author(s) of the validly
972 * published name:<BR>
973 * <i>Lilium tianschanicum</i> was described by Grubov (1977) as a new species and
974 * its name was ascribed to Ivanova; since there is no indication that
975 * Ivanova provided the validating description, the name may be cited as
976 * <i>Lilium tianschanicum</i> N. A. Ivanova ex Grubov or <i>Lilium tianschanicum</i> Grubov.
978 * The presence of an author (team) of <i>this</i> non viral taxon name is a
979 * condition for the existence of an ex author (team) for <i>this</i> same name.
981 * @return the nomenclatural ex author (team) of <i>this</i> non viral taxon name
982 * @see #getCombinationAuthorship()
983 * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
984 * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
987 public TeamOrPersonBase
<?
> getExCombinationAuthorship(){
988 return this.exCombinationAuthorship
;
991 * @see #getExCombinationAuthorship()
994 public void setExCombinationAuthorship(TeamOrPersonBase
<?
> exCombinationAuthorship
){
995 this.exCombinationAuthorship
= exCombinationAuthorship
;
999 public TeamOrPersonBase
<?
> getInCombinationAuthorship(){
1000 return this.inCombinationAuthorship
;
1003 public void setInCombinationAuthorship(TeamOrPersonBase
<?
> inCombinationAuthorship
) {
1004 this.inCombinationAuthorship
= inCombinationAuthorship
;
1008 * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that published the original combination
1009 * on which <i>this</i> non viral taxon name is nomenclaturally based. Such an
1010 * author (team) can only exist if <i>this</i> non viral taxon name is a new
1011 * combination due to a taxonomical revision.
1013 * @return the nomenclatural basionym author (team) of <i>this</i> non viral taxon name
1014 * @see #getCombinationAuthorship()
1015 * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
1016 * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
1019 public TeamOrPersonBase
<?
> getBasionymAuthorship(){
1020 return basionymAuthorship
;
1024 * @see #getBasionymAuthorship()
1027 public void setBasionymAuthorship(TeamOrPersonBase
<?
> basionymAuthorship
) {
1028 this.basionymAuthorship
= basionymAuthorship
;
1032 * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that contributed to
1033 * the publication of the original combination <i>this</i> non viral taxon name is
1034 * based on. This should have been generally stated by
1035 * the {@link #getBasionymAuthorship() basionym author (team)} itself.
1036 * The presence of a basionym author (team) of <i>this</i> non viral taxon name is a
1037 * condition for the existence of an ex basionym author (team)
1038 * for <i>this</i> same name.
1040 * @return the nomenclatural ex basionym author (team) of <i>this</i> non viral taxon name
1041 * @see #getBasionymAuthorship()
1042 * @see #getExCombinationAuthorship()
1043 * @see #getCombinationAuthorship()
1044 * @see eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
1045 * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
1048 public TeamOrPersonBase
<?
> getExBasionymAuthorship(){
1049 return exBasionymAuthorship
;
1053 * @see #getExBasionymAuthorship()
1056 public void setExBasionymAuthorship(TeamOrPersonBase
<?
> exBasionymAuthorship
) {
1057 this.exBasionymAuthorship
= exBasionymAuthorship
;
1061 public TeamOrPersonBase
<?
> getInBasionymAuthorship(){
1062 return this.inBasionymAuthorship
;
1065 public void setInBasionymAuthorship(TeamOrPersonBase
<?
> inBasionymAuthorship
) {
1066 this.inBasionymAuthorship
= inBasionymAuthorship
;
1070 * Returns the boolean value of the flag intended to protect (true)
1071 * or not (false) the {@link #getAuthorshipCache() authorshipCache} (complete authorship string)
1072 * of <i>this</i> non viral taxon name.
1074 * @return the boolean value of the protectedAuthorshipCache flag
1075 * @see #getAuthorshipCache()
1078 public boolean isProtectedAuthorshipCache() {
1079 return protectedAuthorshipCache
;
1083 * @see #isProtectedAuthorshipCache()
1084 * @see #getAuthorshipCache()
1087 public void setProtectedAuthorshipCache(boolean protectedAuthorshipCache
) {
1088 this.protectedAuthorshipCache
= protectedAuthorshipCache
;
1092 * Returns the set of all {@link HybridRelationship hybrid relationships}
1093 * in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedFrom() parent}.
1095 * @see #getHybridRelationships()
1096 * @see #getChildRelationships()
1097 * @see HybridRelationshipType
1100 public Set
<HybridRelationship
> getHybridParentRelations() {
1101 if(hybridParentRelations
== null) {
1102 this.hybridParentRelations
= new HashSet
<>();
1104 return hybridParentRelations
;
1107 private void setHybridParentRelations(Set
<HybridRelationship
> hybridParentRelations
) {
1108 this.hybridParentRelations
= hybridParentRelations
;
1113 * Returns the set of all {@link HybridRelationship hybrid relationships}
1114 * in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedTo() child}.
1116 * @see #getHybridRelationships()
1117 * @see #getParentRelationships()
1118 * @see HybridRelationshipType
1121 public Set
<HybridRelationship
> getHybridChildRelations() {
1122 if(hybridChildRelations
== null) {
1123 this.hybridChildRelations
= new HashSet
<>();
1125 return hybridChildRelations
;
1128 private void setHybridChildRelations(Set
<HybridRelationship
> hybridChildRelations
) {
1129 this.hybridChildRelations
= hybridChildRelations
;
1133 public boolean isProtectedFullTitleCache() {
1134 return protectedFullTitleCache
;
1138 public void setProtectedFullTitleCache(boolean protectedFullTitleCache
) {
1139 this.protectedFullTitleCache
= protectedFullTitleCache
;
1143 * Returns the boolean value of the flag indicating whether the name of <i>this</i>
1144 * botanical taxon name is a hybrid formula (true) or not (false). A hybrid
1145 * named by a hybrid formula (composed with its parent names by placing the
1146 * multiplication sign between them) does not have an own published name
1147 * and therefore has neither an {@link NonViralName#getAuthorshipCache() autorship}
1148 * nor other name components. If this flag is set no other hybrid flags may
1151 * @return the boolean value of the isHybridFormula flag
1152 * @see #isMonomHybrid()
1153 * @see #isBinomHybrid()
1154 * @see #isTrinomHybrid()
1158 @java.beans
.Transient
1159 public boolean isHybridFormula(){
1160 return this.hybridFormula
;
1164 * @see #isHybridFormula()
1167 public void setHybridFormula(boolean hybridFormula
){
1168 this.hybridFormula
= hybridFormula
;
1172 * Returns the boolean value of the flag indicating whether <i>this</i> botanical
1173 * taxon name is the name of an intergeneric hybrid (true) or not (false).
1174 * In this case the multiplication sign is placed before the scientific
1175 * name. If this flag is set no other hybrid flags may be set.
1177 * @return the boolean value of the isMonomHybrid flag
1178 * @see #isHybridFormula()
1179 * @see #isBinomHybrid()
1180 * @see #isTrinomHybrid()
1183 public boolean isMonomHybrid(){
1184 return this.monomHybrid
;
1188 * @see #isMonomHybrid()
1189 * @see #isBinomHybrid()
1190 * @see #isTrinomHybrid()
1193 public void setMonomHybrid(boolean monomHybrid
){
1194 this.monomHybrid
= monomHybrid
;
1198 * Returns the boolean value of the flag indicating whether <i>this</i> botanical
1199 * taxon name is the name of an interspecific hybrid (true) or not (false).
1200 * In this case the multiplication sign is placed before the species
1201 * epithet. If this flag is set no other hybrid flags may be set.
1203 * @return the boolean value of the isBinomHybrid flag
1204 * @see #isHybridFormula()
1205 * @see #isMonomHybrid()
1206 * @see #isTrinomHybrid()
1209 public boolean isBinomHybrid(){
1210 return this.binomHybrid
;
1214 * @see #isBinomHybrid()
1215 * @see #isMonomHybrid()
1216 * @see #isTrinomHybrid()
1219 public void setBinomHybrid(boolean binomHybrid
){
1220 this.binomHybrid
= binomHybrid
;
1224 public boolean isTrinomHybrid(){
1225 return this.trinomHybrid
;
1229 * @see #isTrinomHybrid()
1230 * @see #isBinomHybrid()
1231 * @see #isMonomHybrid()
1234 public void setTrinomHybrid(boolean trinomHybrid
){
1235 this.trinomHybrid
= trinomHybrid
;
1238 // ****************** VIRAL NAME ******************/
1241 public String
getAcronym(){
1242 return this.acronym
;
1246 * @see #getAcronym()
1249 public void setAcronym(String acronym
){
1250 this.acronym
= StringUtils
.isBlank(acronym
)?
null : acronym
;
1253 // ****************** BACTERIAL NAME ******************/
1256 public String
getSubGenusAuthorship(){
1257 return this.subGenusAuthorship
;
1261 public void setSubGenusAuthorship(String subGenusAuthorship
){
1262 this.subGenusAuthorship
= subGenusAuthorship
;
1267 public String
getNameApprobation(){
1268 return this.nameApprobation
;
1272 * @see #getNameApprobation()
1275 public void setNameApprobation(String nameApprobation
){
1276 this.nameApprobation
= nameApprobation
;
1279 //************ Zoological Name
1283 public String
getBreed(){
1290 public void setBreed(String breed
){
1291 this.breed
= StringUtils
.isBlank(breed
) ?
null : breed
;
1296 public Integer
getPublicationYear() {
1297 return publicationYear
;
1300 * @see #getPublicationYear()
1303 public void setPublicationYear(Integer publicationYear
) {
1304 this.publicationYear
= publicationYear
;
1309 public Integer
getOriginalPublicationYear() {
1310 return originalPublicationYear
;
1313 * @see #getOriginalPublicationYear()
1316 public void setOriginalPublicationYear(Integer originalPublicationYear
) {
1317 this.originalPublicationYear
= originalPublicationYear
;
1320 // **** Cultivar Name ************
1324 public String
getCultivarName(){
1325 return this.cultivarName
;
1329 * @see #getCultivarName()
1332 public void setCultivarName(String cultivarName
){
1333 this.cultivarName
= StringUtils
.isBlank(cultivarName
) ?
null : cultivarName
;
1336 // **************** Fungus Name
1338 public boolean isAnamorphic(){
1339 return this.anamorphic
;
1343 * @see #isAnamorphic()
1346 public void setAnamorphic(boolean anamorphic
){
1347 this.anamorphic
= anamorphic
;
1351 // **************** ADDER / REMOVE *************************/
1354 * Adds the given {@link HybridRelationship hybrid relationship} to the set
1355 * of {@link #getHybridRelationships() hybrid relationships} of both non-viral names
1356 * involved in this hybrid relationship. One of both non-viral names
1357 * must be <i>this</i> non-viral name otherwise no addition will be carried
1358 * out. The {@link eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo() child
1359 * non viral taxon name} must be a hybrid, which means that one of its four hybrid flags must be set.
1361 * @param relationship the hybrid relationship to be added
1362 * @see #isHybridFormula()
1363 * @see #isMonomHybrid()
1364 * @see #isBinomHybrid()
1365 * @see #isTrinomHybrid()
1366 * @see #getHybridRelationships()
1367 * @see #getParentRelationships()
1368 * @see #getChildRelationships()
1369 * @see #addRelationship(RelationshipBase)
1370 * @throws IllegalArgumentException
1372 protected void addHybridRelationship(HybridRelationship rel
) {
1373 if (rel
!=null && rel
.getHybridName().equals(this)){
1374 this.hybridChildRelations
.add(rel
);
1375 }else if(rel
!=null && rel
.getParentName().equals(this)){
1376 this.hybridParentRelations
.add(rel
);
1378 throw new IllegalArgumentException("Hybrid relationship is either null or the relationship does not reference this name");
1384 * Removes one {@link HybridRelationship hybrid relationship} from the set of
1385 * {@link #getHybridRelationships() hybrid relationships} in which <i>this</i> botanical taxon name
1386 * is involved. The hybrid relationship will also be removed from the set
1387 * belonging to the second botanical taxon name involved.
1389 * @param relationship the hybrid relationship which should be deleted from the corresponding sets
1390 * @see #getHybridRelationships()
1393 public void removeHybridRelationship(HybridRelationship hybridRelation
) {
1394 if (hybridRelation
== null) {
1398 TaxonName parent
= hybridRelation
.getParentName();
1399 TaxonName child
= hybridRelation
.getHybridName();
1400 if (this.equals(parent
)){
1401 this.hybridParentRelations
.remove(hybridRelation
);
1402 child
.hybridChildRelations
.remove(hybridRelation
);
1403 hybridRelation
.setHybridName(null);
1404 hybridRelation
.setParentName(null);
1406 if (this.equals(child
)){
1407 parent
.hybridParentRelations
.remove(hybridRelation
);
1408 this.hybridChildRelations
.remove(hybridRelation
);
1409 hybridRelation
.setHybridName(null);
1410 hybridRelation
.setParentName(null);
1414 //********* METHODS **************************************/
1417 public INameCacheStrategy
getCacheStrategy() {
1418 rectifyNameCacheStrategy();
1419 return this.cacheStrategy
;
1423 public String
generateFullTitle(){
1424 if (getCacheStrategy() == null){
1425 logger
.warn("No CacheStrategy defined for taxon name: " + this.getUuid());
1428 return cacheStrategy
.getFullTitleCache(this);
1434 public void setFullTitleCache(String fullTitleCache
){
1435 setFullTitleCache(fullTitleCache
, PROTECTED
);
1439 public void setFullTitleCache(String fullTitleCache
, boolean protectCache
){
1440 fullTitleCache
= getTruncatedCache(fullTitleCache
);
1441 this.fullTitleCache
= fullTitleCache
;
1442 this.setProtectedFullTitleCache(protectCache
);
1445 /** Checks if this name is an autonym.<BR>
1446 * An autonym is a taxon name that has equal specific and infra specific epithets.<BR>
1447 * {@link http://ibot.sav.sk/icbn/frameset/0010Ch2Sec1a006.htm#6.8. Vienna Code §6.8}
1448 * or a taxon name that has equal generic and infrageneric epithets (A22.2).<BR>
1449 * Only relevant for botanical names.
1450 * @return true, if name has Rank, Rank is below species and species epithet equals infraSpeciesEpithtet, else false
1454 public boolean isAutonym(){
1456 if (this.getRank() != null && this.getSpecificEpithet() != null && this.getInfraSpecificEpithet() != null &&
1457 this.isInfraSpecific() && this.getSpecificEpithet().trim().equals(this.getInfraSpecificEpithet().trim())){
1459 }else if (this.getRank() != null && this.getGenusOrUninomial() != null && this.getInfraGenericEpithet() != null &&
1460 this.isInfraGeneric() && this.getGenusOrUninomial().trim().equals(this.getInfraGenericEpithet().trim())){
1473 public List
<TaggedText
> getTaggedName(){
1474 INameCacheStrategy strat
= getCacheStrategy();
1475 return strat
.getTaggedTitle(this);
1480 public List
<TaggedText
> getTaggedFullTitle() {
1481 INameCacheStrategy strat
= getCacheStrategy();
1482 return strat
.getTaggedFullTitle(this);
1487 public String
getFullTitleCache(){
1488 if (protectedFullTitleCache
){
1489 return this.fullTitleCache
;
1491 updateAuthorshipCache();
1492 if (fullTitleCache
== null ){
1493 this.fullTitleCache
= getTruncatedCache(generateFullTitle());
1495 return fullTitleCache
;
1500 public String
getTitleCache(){
1501 if(!protectedTitleCache
) {
1502 updateAuthorshipCache();
1504 return super.getTitleCache();
1508 public void setTitleCache(String titleCache
, boolean protectCache
){
1509 super.setTitleCache(titleCache
, protectCache
);
1513 * Returns the concatenated and formated authorteams string including
1514 * basionym and combination authors of <i>this</i> non viral taxon name.
1515 * If the protectedAuthorshipCache flag is set this method returns the
1516 * string stored in the the authorshipCache attribute, otherwise it
1517 * generates the complete authorship string, returns it and stores it in
1518 * the authorshipCache attribute.
1520 * @return the string with the concatenated and formated authorteams for <i>this</i> non viral taxon name
1521 * @see #generateAuthorship()
1525 public String
getAuthorshipCache() {
1526 if (protectedAuthorshipCache
){
1527 return this.authorshipCache
;
1529 if (this.authorshipCache
== null ){
1530 this.authorshipCache
= generateAuthorship();
1532 //TODO get isDirty of authors, make better if possible
1533 this.setAuthorshipCache(generateAuthorship(), protectedAuthorshipCache
); //throw change event to inform higher caches
1536 return authorshipCache
;
1545 * Assigns an authorshipCache string to <i>this</i> non viral taxon name. Sets the isProtectedAuthorshipCache
1546 * flag to <code>true</code>.
1548 * @param authorshipCache the string which identifies the complete authorship of <i>this</i> non viral taxon name
1549 * @see #getAuthorshipCache()
1552 public void setAuthorshipCache(String authorshipCache
) {
1553 setAuthorshipCache(authorshipCache
, true);
1558 * Assigns an authorshipCache string to <i>this</i> non viral taxon name.
1560 * @param authorshipCache the string which identifies the complete authorship of <i>this</i> non viral taxon name
1561 * @param protectedAuthorshipCache if true the isProtectedAuthorshipCache flag is set to <code>true</code>, otherwise
1562 * the flag is set to <code>false</code>.
1563 * @see #getAuthorshipCache()
1566 public void setAuthorshipCache(String authorshipCache
, boolean protectedAuthorshipCache
) {
1567 this.authorshipCache
= authorshipCache
;
1568 this.setProtectedAuthorshipCache(protectedAuthorshipCache
);
1572 * Generates and returns a concatenated and formated author team string
1573 * including basionym and combination authors of <i>this</i> taxon name
1574 * according to the strategy defined in
1575 * {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(TaxonName) INonViralNameCacheStrategy}.
1577 * @return the string with the concatenated and formatted author teams for <i>this</i> taxon name
1578 * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(TaxonName)
1581 public String
generateAuthorship(){
1582 if (getCacheStrategy() == null){
1583 logger
.warn("No CacheStrategy defined for taxon name: " + this.getUuid());
1586 return cacheStrategy
.getAuthorshipCache(this);
1593 * Tests if the given name has any authors.
1594 * @return false if no author ((ex)combination or (ex)basionym) exists, true otherwise
1597 public boolean hasAuthors() {
1598 return (this.getCombinationAuthorship() != null ||
1599 this.getExCombinationAuthorship() != null ||
1600 this.getBasionymAuthorship() != null ||
1601 this.getExBasionymAuthorship() != null);
1605 * Shortcut. Returns the combination authors title cache. Returns null if no combination author exists.
1609 public String
computeCombinationAuthorNomenclaturalTitle() {
1610 return computeNomenclaturalTitle(this.getCombinationAuthorship());
1614 * Shortcut. Returns the basionym authors title cache. Returns null if no basionym author exists.
1618 public String
computeBasionymAuthorNomenclaturalTitle() {
1619 return computeNomenclaturalTitle(this.getBasionymAuthorship());
1624 * Shortcut. Returns the ex-combination authors title cache. Returns null if no ex-combination author exists.
1628 public String
computeExCombinationAuthorNomenclaturalTitle() {
1629 return computeNomenclaturalTitle(this.getExCombinationAuthorship());
1633 * Shortcut. Returns the ex-basionym authors title cache. Returns null if no exbasionym author exists.
1637 public String
computeExBasionymAuthorNomenclaturalTitle() {
1638 return computeNomenclaturalTitle(this.getExBasionymAuthorship());
1641 private String
computeNomenclaturalTitle(INomenclaturalAuthor author
){
1642 if (author
== null){
1645 return author
.getNomenclaturalTitle();
1650 * Returns the set of all {@link NameRelationship name relationships}
1651 * in which <i>this</i> taxon name is involved. A taxon name can be both source
1652 * in some name relationships or target in some others.
1654 * @see #getRelationsToThisName()
1655 * @see #getRelationsFromThisName()
1656 * @see #addNameRelationship(NameRelationship)
1657 * @see #addRelationshipToName(TaxonName, NameRelationshipType, String)
1658 * @see #addRelationshipFromName(TaxonName, NameRelationshipType, String)
1662 public Set
<NameRelationship
> getNameRelations() {
1663 Set
<NameRelationship
> rels
= new HashSet
<NameRelationship
>();
1664 rels
.addAll(getRelationsFromThisName());
1665 rels
.addAll(getRelationsToThisName());
1670 public NameRelationship
addRelationshipToName(TaxonName toName
, NameRelationshipType type
, String ruleConsidered
, NomenclaturalCodeEdition codeEdition
){
1671 return addRelationshipToName(toName
, type
, null, null, ruleConsidered
, codeEdition
);
1675 public NameRelationship
addRelationshipToName(TaxonName toName
, NameRelationshipType type
){
1676 return addRelationshipToName(toName
, type
, null, null, null, null);
1680 public NameRelationship
addRelationshipToName(TaxonName toName
, NameRelationshipType type
, Reference citation
, String microCitation
, String ruleConsidered
, NomenclaturalCodeEdition codeEdition
){
1681 if (toName
== null){
1682 throw new NullPointerException("Null is not allowed as name for a name relationship");
1684 NameRelationship rel
= new NameRelationship(toName
, this, type
, citation
, microCitation
, ruleConsidered
, codeEdition
);
1689 public NameRelationship
addRelationshipFromName(TaxonName fromName
, NameRelationshipType type
, String ruleConsidered
, NomenclaturalCodeEdition codeEdition
){
1690 //fromName.addRelationshipToName(this, type, null, null, ruleConsidered);
1691 return this.addRelationshipFromName(fromName
, type
, null, null, ruleConsidered
, codeEdition
);
1695 public NameRelationship
addRelationshipFromName(TaxonName fromName
, NameRelationshipType type
, Reference citation
, String microCitation
, String ruleConsidered
, NomenclaturalCodeEdition codeEdition
){
1696 return fromName
.addRelationshipToName(this, type
, citation
, microCitation
, ruleConsidered
, codeEdition
);
1700 * Adds an existing {@link NameRelationship name relationship} either to the set of
1701 * {@link #getRelationsToThisName() relations to <i>this</i> taxon name} or to the set of
1702 * {@link #getRelationsFromThisName() relations from <i>this</i> taxon name}. If neither the
1703 * source nor the target of the name relationship match with <i>this</i> taxon name
1704 * no addition will be carried out.
1706 * @param rel the name relationship to be added to one of <i>this</i> taxon name's name relationships sets
1707 * @see #getNameRelations()
1708 * @see #addRelationshipToName(TaxonName, NameRelationshipType, String)
1709 * @see #addRelationshipFromName(TaxonName, NameRelationshipType, String)
1711 protected void addNameRelationship(NameRelationship rel
) {
1713 if (rel
.getToName().equals(this)){
1714 this.relationsToThisName
.add(rel
);
1715 }else if(rel
.getFromName().equals(this)){
1716 this.relationsFromThisName
.add(rel
);
1718 NameRelationshipType type
= rel
.getType();
1719 if (type
!= null && ( type
.isBasionymRelation() || type
.isReplacedSynonymRelation() ) ){
1720 rel
.getFromName().mergeHomotypicGroups(rel
.getToName());
1723 throw new RuntimeException("NameRelationship is either null or the relationship does not reference this name");
1727 * Removes one {@link NameRelationship name relationship} from one of both sets of
1728 * {@link #getNameRelations() name relationships} in which <i>this</i> taxon name is involved.
1729 * The name relationship will also be removed from one of both sets belonging
1730 * to the second taxon name involved. Furthermore the fromName and toName
1731 * attributes of the name relationship object will be nullified.
1733 * @param nameRelation the name relationship which should be deleted from one of both sets
1734 * @see #getNameRelations()
1737 public void removeNameRelationship(NameRelationship nameRelation
) {
1739 TaxonName fromName
= nameRelation
.getFromName();
1740 TaxonName toName
= nameRelation
.getToName();
1742 if (nameRelation
!= null) {
1743 nameRelation
.setToName(null);
1744 nameRelation
.setFromName(null);
1747 if (fromName
!= null) {
1748 fromName
.removeNameRelationship(nameRelation
);
1751 if (toName
!= null) {
1752 toName
.removeNameRelationship(nameRelation
);
1755 this.relationsToThisName
.remove(nameRelation
);
1756 this.relationsFromThisName
.remove(nameRelation
);
1760 public void removeRelationToTaxonName(TaxonName toTaxonName
) {
1761 Set
<NameRelationship
> nameRelationships
= new HashSet
<NameRelationship
>();
1762 // nameRelationships.addAll(this.getNameRelations());
1763 nameRelationships
.addAll(this.getRelationsFromThisName());
1764 nameRelationships
.addAll(this.getRelationsToThisName());
1765 for(NameRelationship nameRelationship
: nameRelationships
) {
1766 // remove name relationship from this side
1767 if (nameRelationship
.getFromName().equals(this) && nameRelationship
.getToName().equals(toTaxonName
)) {
1768 this.removeNameRelationship(nameRelationship
);
1773 public void removeRelationWithTaxonName(TaxonName otherTaxonName
, Direction direction
, NameRelationshipType type
) {
1775 Set
<NameRelationship
> tmpRels
= new HashSet
<>(relationsWithThisName(direction
));
1776 for(NameRelationship nameRelationship
: tmpRels
) {
1777 if (direction
.equals(Direction
.relatedFrom
) && nameRelationship
.getToName().equals(otherTaxonName
) ||
1778 direction
.equals(Direction
.relatedTo
) && nameRelationship
.getFromName().equals(otherTaxonName
)) {
1779 if (type
== null || type
.equals(nameRelationship
.getType())){
1780 this.removeNameRelationship(nameRelationship
);
1788 * If relation is of type NameRelationship, addNameRelationship is called;
1789 * if relation is of type HybridRelationship addHybridRelationship is called,
1790 * otherwise an IllegalArgumentException is thrown.
1792 * @param relation the relationship to be added to one of <i>this</i> taxon name's name relationships sets
1793 * @see #addNameRelationship(NameRelationship)
1794 * @see #getNameRelations()
1795 * @see NameRelationship
1796 * @see RelationshipBase
1797 * @see #addHybridRelationship(HybridRelationship)
1799 * @deprecated to be used by RelationshipBase only
1803 public void addRelationship(RelationshipBase relation
) {
1804 if (relation
instanceof NameRelationship
){
1805 addNameRelationship((NameRelationship
)relation
);
1807 }else if (relation
instanceof HybridRelationship
){
1808 addHybridRelationship((HybridRelationship
)relation
);
1810 logger
.warn("Relationship not of type NameRelationship!");
1811 throw new IllegalArgumentException("Relationship not of type NameRelationship or HybridRelationship");
1816 * Returns the set of all {@link NameRelationship name relationships}
1817 * in which <i>this</i> taxon name is involved as a source ("from"-side).
1819 * @see #getNameRelations()
1820 * @see #getRelationsToThisName()
1821 * @see #addRelationshipFromName(TaxonName, NameRelationshipType, String)
1824 public Set
<NameRelationship
> getRelationsFromThisName() {
1825 if(relationsFromThisName
== null) {
1826 this.relationsFromThisName
= new HashSet
<>();
1828 return relationsFromThisName
;
1832 * Returns the set of all {@link NameRelationship name relationships}
1833 * in which <i>this</i> taxon name is involved as a target ("to"-side).
1835 * @see #getNameRelations()
1836 * @see #getRelationsFromThisName()
1837 * @see #addRelationshipToName(TaxonName, NameRelationshipType, String)
1840 public Set
<NameRelationship
> getRelationsToThisName() {
1841 if(relationsToThisName
== null) {
1842 this.relationsToThisName
= new HashSet
<>();
1844 return relationsToThisName
;
1848 * Returns the set of {@link NomenclaturalStatus nomenclatural status} assigned
1849 * to <i>this</i> taxon name according to its corresponding nomenclature code.
1850 * This includes the {@link NomenclaturalStatusType type} of the nomenclatural status
1851 * and the nomenclatural code rule considered.
1853 * @see NomenclaturalStatus
1854 * @see NomenclaturalStatusType
1857 public Set
<NomenclaturalStatus
> getStatus() {
1858 if(status
== null) {
1859 this.status
= new HashSet
<>();
1865 * Adds a new {@link NomenclaturalStatus nomenclatural status}
1866 * to <i>this</i> taxon name's set of nomenclatural status.
1868 * @param nomStatus the nomenclatural status to be added
1872 public void addStatus(NomenclaturalStatus nomStatus
) {
1873 this.status
.add(nomStatus
);
1876 public NomenclaturalStatus
addStatus(NomenclaturalStatusType statusType
, Reference citation
, String microCitation
) {
1877 NomenclaturalStatus newStatus
= NomenclaturalStatus
.NewInstance(statusType
, citation
, microCitation
);
1878 this.status
.add(newStatus
);
1883 * Removes one element from the set of nomenclatural status of <i>this</i> taxon name.
1884 * Type and ruleConsidered attributes of the nomenclatural status object
1885 * will be nullified.
1887 * @param nomStatus the nomenclatural status of <i>this</i> taxon name which should be deleted
1891 public void removeStatus(NomenclaturalStatus nomStatus
) {
1892 //TODO to be implemented?
1893 logger
.warn("not yet fully implemented?");
1894 this.status
.remove(nomStatus
);
1897 public void setStatus(Set
<NomenclaturalStatus
> nomStatus
) throws SetterAdapterException
{
1898 new EntityCollectionSetterAdapter
<TaxonName
, NomenclaturalStatus
>(TaxonName
.class, NomenclaturalStatus
.class, "status", "addStatus", "removeStatus").setCollection(this, status
);
1903 * Generates the composed name string of <i>this</i> non viral taxon name without author
1904 * strings or year according to the strategy defined in
1905 * {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy INonViralNameCacheStrategy}.
1906 * The result might be stored in {@link #getNameCache() nameCache} if the
1907 * flag {@link #isProtectedNameCache() protectedNameCache} is not set.
1909 * @return the string with the composed name of <i>this</i> non viral taxon name without authors or year
1910 * @see #getNameCache()
1912 protected String
generateNameCache(){
1913 if (getCacheStrategy() == null){
1914 logger
.warn("No CacheStrategy defined for taxon name: " + this.toString());
1917 return cacheStrategy
.getNameCache(this);
1922 * Returns or generates the nameCache (scientific name
1923 * without author strings and year) string for <i>this</i> non viral taxon name. If the
1924 * {@link #isProtectedNameCache() protectedNameCache} flag is not set (False)
1925 * the string will be generated according to a defined strategy,
1926 * otherwise the value of the actual nameCache string will be returned.
1928 * @return the string which identifies <i>this</i> non viral taxon name (without authors or year)
1929 * @see #generateNameCache()
1933 public String
getNameCache() {
1934 if (protectedNameCache
){
1935 return this.nameCache
;
1937 // is title dirty, i.e. equal NULL?
1938 if (nameCache
== null){
1939 this.nameCache
= generateNameCache();
1945 * Assigns a nameCache string to <i>this</i> non viral taxon name and protects it from being overwritten.
1946 * Sets the protectedNameCache flag to <code>true</code>.
1948 * @param nameCache the string which identifies <i>this</i> non viral taxon name (without authors or year)
1949 * @see #getNameCache()
1952 public void setNameCache(String nameCache
){
1953 setNameCache(nameCache
, true);
1957 * Assigns a nameCache string to <i>this</i> non viral taxon name and protects it from being overwritten.
1958 * Sets the protectedNameCache flag to <code>true</code>.
1960 * @param nameCache the string which identifies <i>this</i> non viral taxon name (without authors or year)
1961 * @param protectedNameCache if true teh protectedNameCache is set to <code>true</code> or otherwise set to
1962 * <code>false</code>
1963 * @see #getNameCache()
1966 public void setNameCache(String nameCache
, boolean protectedNameCache
){
1967 this.nameCache
= nameCache
;
1968 this.setProtectedNameCache(protectedNameCache
);
1973 * Indicates whether <i>this</i> taxon name is a {@link NameRelationshipType#BASIONYM() basionym}
1974 * or a {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym}
1975 * of any other taxon name. Returns "true", if a basionym or a replaced
1976 * synonym {@link NameRelationship relationship} from <i>this</i> taxon name to another taxon name exists,
1977 * false otherwise (also in case <i>this</i> taxon name is the only one in the
1978 * homotypical group).
1982 public boolean isOriginalCombination(){
1983 Set
<NameRelationship
> relationsFromThisName
= this.getRelationsFromThisName();
1984 for (NameRelationship relation
: relationsFromThisName
) {
1985 if (relation
.getType().isBasionymRelation() ||
1986 relation
.getType().isReplacedSynonymRelation()) {
1994 * Indicates <i>this</i> taxon name is a {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym}
1995 * of any other taxon name. Returns "true", if a replaced
1996 * synonym {@link NameRelationship relationship} from <i>this</i> taxon name to another taxon name exists,
1997 * false otherwise (also in case <i>this</i> taxon name is the only one in the
1998 * homotypical group).
2002 public boolean isReplacedSynonym(){
2003 Set
<NameRelationship
> relationsFromThisName
= this.getRelationsFromThisName();
2004 for (NameRelationship relation
: relationsFromThisName
) {
2005 if (relation
.getType().isReplacedSynonymRelation()) {
2013 * Returns the taxon name which is the {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
2014 * The basionym of a taxon name is its epithet-bringing synonym.
2015 * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
2016 * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
2017 * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
2019 * If more than one basionym exists one is choosen at radom.
2021 * If no basionym exists null is returned.
2025 public TaxonName
getBasionym(){
2026 Set
<TaxonName
> basionyms
= getBasionyms();
2027 if (basionyms
.size() == 0){
2030 return basionyms
.iterator().next();
2035 * Returns the set of taxon names which are the {@link NameRelationshipType#BASIONYM() basionyms} of <i>this</i> taxon name.
2036 * The basionym of a taxon name is its epithet-bringing synonym.
2037 * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
2038 * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
2039 * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
2043 public Set
<TaxonName
> getBasionyms(){
2044 return getRelatedNames(Direction
.relatedTo
, NameRelationshipType
.BASIONYM());
2053 public Set
<TaxonName
> getRelatedNames(Direction direction
, NameRelationshipType type
) {
2054 return getRelatedNames(relationsWithThisName(direction
), type
);
2062 private Set
<TaxonName
> getRelatedNames(Set
<NameRelationship
> rels
, NameRelationshipType type
) {
2063 Set
<TaxonName
> result
= new HashSet
<>();
2064 for (NameRelationship rel
: rels
){
2065 if (rel
.getType()!= null && rel
.getType().isRelationshipType(type
)){
2066 TaxonName basionym
= rel
.getFromName();
2067 result
.add(basionym
);
2075 public NameRelationship
addOriginalSpelling(TaxonName originalSpelling
, Reference citation
, String microcitation
){
2076 if (originalSpelling
!= null){
2077 return originalSpelling
.addRelationshipToName(this, NameRelationshipType
.ORIGINAL_SPELLING(), citation
, microcitation
, null, null);
2084 * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
2085 * The basionym {@link NameRelationship relationship} will be added to <i>this</i> taxon name
2086 * and to the basionym. The basionym cannot have itself as a basionym.
2087 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
2088 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
2090 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
2091 * @see #getBasionym()
2092 * @see #addBasionym(TaxonName, String)
2095 public NameRelationship
addBasionym(TaxonName basionym
){
2096 return addBasionym(basionym
, null, null, null, null);
2099 * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name
2100 * and keeps the nomenclatural rule considered for it. The basionym
2101 * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the basionym.
2102 * The basionym cannot have itself as a basionym.
2103 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
2104 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
2106 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
2107 * @param ruleConsidered the string identifying the nomenclatural rule
2108 * @param codeEdition the edition of the nomenclatural code where the <code>ruleConsidered</code> has been published.
2110 * @see #getBasionym()
2111 * @see #addBasionym(TaxonName)
2114 public NameRelationship
addBasionym(TaxonName basionym
, Reference citation
, String microcitation
, String ruleConsidered
, NomenclaturalCodeEdition codeEdition
){
2115 if (basionym
!= null){
2116 return basionym
.addRelationshipToName(this, NameRelationshipType
.BASIONYM(), citation
, microcitation
, ruleConsidered
, codeEdition
);
2123 * Returns the set of taxon names which are the {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonyms} of <i>this</i> taxon name.
2127 public Set
<TaxonName
> getReplacedSynonyms(){
2128 return getRelatedNames(Direction
.relatedTo
, NameRelationshipType
.REPLACED_SYNONYM());
2132 * Assigns a taxon name as {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym} of <i>this</i> taxon name
2133 * and keeps the nomenclatural rule considered for it. The replaced synonym
2134 * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the replaced synonym.
2135 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the replaced synonym
2136 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
2138 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
2139 * @param ruleConsidered the string identifying the nomenclatural rule
2140 * @param codeEdition the edition of the nomenclatural code where the <code>ruleConsidered</code> has been published.
2141 * @see #getBasionym()
2142 * @see #addBasionym(TaxonName)
2144 //TODO: Check if true: The replaced synonym cannot have itself a replaced synonym (?).
2146 public void addReplacedSynonym(TaxonName replacedSynonym
, Reference citation
, String microcitation
, String ruleConsidered
, NomenclaturalCodeEdition codeEdition
){
2147 if (replacedSynonym
!= null){
2148 replacedSynonym
.addRelationshipToName(this, NameRelationshipType
.REPLACED_SYNONYM(), citation
, microcitation
, ruleConsidered
, codeEdition
);
2153 * Removes the {@link NameRelationshipType#BASIONYM() basionym} {@link NameRelationship relationship} from the set of
2154 * {@link #getRelationsToThisName() name relationships to} <i>this</i> taxon name. The same relationhip will be
2155 * removed from the set of {@link #getRelationsFromThisName() name relationships from} the taxon name
2156 * previously used as basionym.
2158 * @see #getBasionym()
2159 * @see #addBasionym(TaxonName)
2162 public void removeBasionyms(){
2163 removeNameRelations(Direction
.relatedTo
, NameRelationshipType
.BASIONYM());
2168 * Removes all {@link NameRelationship relationships} of the given <code>type</code> from the set of
2169 * relations in the specified <code>direction</code> direction wich are related from or to this
2170 * <i>this</i> taxon name. The same relationship will be removed from the set of
2171 * reverse relations of the other taxon name.
2176 public void removeNameRelations(Direction direction
, NameRelationshipType type
) {
2177 Set
<NameRelationship
> relationsWithThisName
= relationsWithThisName(direction
);
2178 Set
<NameRelationship
> removeRelations
= new HashSet
<>();
2179 for (NameRelationship nameRelation
: relationsWithThisName
){
2180 if (nameRelation
.getType().isRelationshipType(type
)){
2181 removeRelations
.add(nameRelation
);
2184 // Removing relations from a set through which we are iterating causes a
2185 // ConcurrentModificationException. Therefore, we delete the targeted
2186 // relations in a second step.
2187 for (NameRelationship relation
: removeRelations
){
2188 this.removeNameRelationship(relation
);
2197 protected Set
<NameRelationship
> relationsWithThisName(Direction direction
) {
2201 return this.getRelationsToThisName();
2203 return this.getRelationsFromThisName();
2204 default: throw new RuntimeException("invalid Direction:" + direction
);
2209 * Returns the taxonomic {@link Rank rank} of <i>this</i> taxon name.
2214 public Rank
getRank(){
2222 public void setRank(Rank rank
){
2226 //*************** nom ref/source *******************/
2229 public Reference
getNomenclaturalReference(){
2231 return this.nomenclaturalReference
;
2232 // if (this.nomenclaturalSource == null){
2235 // return this.nomenclaturalSource.getCitation();
2239 public DescriptionElementSource
getNomenclaturalSource(){
2240 return this.nomenclaturalSource
;
2243 protected DescriptionElementSource
getNomenclaturalSource(boolean createIfNotExist
){
2244 if (this.nomenclaturalSource
== null){
2245 if (!createIfNotExist
){
2248 this.nomenclaturalSource
= DescriptionElementSource
.NewInstance(OriginalSourceType
.NomenclaturalReference
);
2250 return this.nomenclaturalSource
;
2254 * Assigns a {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} to <i>this</i> taxon name.
2255 * The corresponding {@link eu.etaxonomy.cdm.model.reference.Reference.isNomenclaturallyRelevant nomenclaturally relevant flag} will be set to true
2256 * as it is obviously used for nomenclatural purposes.
2258 * Shortcut to set the nomenclatural reference.
2260 * @throws IllegalArgumentException if parameter <code>nomenclaturalReference</code> is not assignable from {@link INomenclaturalReference}
2261 * @see #getNomenclaturalReference()
2265 public void setNomenclaturalReference(Reference nomenclaturalReference
){
2267 this.nomenclaturalReference
= nomenclaturalReference
;
2268 // getNomenclaturalSource(true).setCitation(nomenclaturalReference);
2269 // checkNullSource();
2273 public void setNomenclaturalReference(INomenclaturalReference nomenclaturalReference
){
2274 setNomenclaturalReference(CdmBase
.deproxy(nomenclaturalReference
, Reference
.class));
2278 * Returns the details string of the {@link #getNomenclaturalReference() nomenclatural reference} assigned
2279 * to <i>this</i> taxon name. The details describe the exact localisation within
2280 * the publication used as nomenclature reference. These are mostly
2281 * (implicitly) pages but can also be figures or tables or any other
2282 * element of a publication. A nomenclatural micro reference (details)
2283 * requires the existence of a nomenclatural reference.
2285 //Details of the nomenclatural reference (protologue).
2287 public String
getNomenclaturalMicroReference(){
2289 return this.nomenclaturalMicroReference
;
2290 // if (this.nomenclaturalSource == null){
2293 // return this.nomenclaturalSource.getCitationMicroReference();
2296 * @see #getNomenclaturalMicroReference()
2299 public void setNomenclaturalMicroReference(String nomenclaturalMicroReference
){
2301 this.nomenclaturalMicroReference
= nomenclaturalMicroReference
;
2302 // this.getNomenclaturalSource(true).setCitationMicroReference(StringUtils.isBlank(nomenclaturalMicroReference)? null : nomenclaturalMicroReference);
2303 // checkNullSource();
2307 private void checkNullSource() {
2308 if (this.nomenclaturalSource
!= null && this.nomenclaturalSource
.checkEmpty()){
2309 this.nomenclaturalSource
= null;
2315 public void setNomenclaturalSource(DescriptionElementSource nomenclaturalSource
) throws IllegalArgumentException
{
2316 if (nomenclaturalSource
!= null && !OriginalSourceType
.NomenclaturalReference
.equals(nomenclaturalSource
.getType()) ){
2317 throw new IllegalArgumentException("Nomenclatural source must be of type " + OriginalSourceType
.NomenclaturalReference
.getMessage());
2319 this.nomenclaturalSource
= nomenclaturalSource
;
2323 * Returns the appended phrase string assigned to <i>this</i> taxon name.
2324 * The appended phrase is a non-atomised addition to a name. It is
2325 * not ruled by a nomenclatural code.
2328 public String
getAppendedPhrase(){
2329 return this.appendedPhrase
;
2333 * @see #getAppendedPhrase()
2336 public void setAppendedPhrase(String appendedPhrase
){
2337 this.appendedPhrase
= StringUtils
.isBlank(appendedPhrase
)?
null : appendedPhrase
;
2341 public int getParsingProblem(){
2342 return this.parsingProblem
;
2346 public void setParsingProblem(int parsingProblem
){
2347 this.parsingProblem
= parsingProblem
;
2351 public void addParsingProblem(ParserProblem problem
){
2352 parsingProblem
= ParserProblem
.addProblem(parsingProblem
, problem
);
2356 public void removeParsingProblem(ParserProblem problem
) {
2357 parsingProblem
= ParserProblem
.removeProblem(parsingProblem
, problem
);
2364 public void addParsingProblems(int problems
){
2365 parsingProblem
= ParserProblem
.addProblems(parsingProblem
, problems
);
2369 public boolean hasProblem(){
2370 return parsingProblem
!= 0;
2374 public boolean hasProblem(ParserProblem problem
) {
2375 return getParsingProblems().contains(problem
);
2379 public int getProblemStarts(){
2380 return this.problemStarts
;
2384 public void setProblemStarts(int start
) {
2385 this.problemStarts
= start
;
2389 public int getProblemEnds(){
2390 return this.problemEnds
;
2394 public void setProblemEnds(int end
) {
2395 this.problemEnds
= end
;
2398 //*********************** TYPE DESIGNATION *********************************************//
2401 * Returns the set of {@link TypeDesignationBase type designations} assigned
2402 * to <i>this</i> taxon name.
2403 * @see NameTypeDesignation
2404 * @see SpecimenTypeDesignation
2407 public Set
<TypeDesignationBase
> getTypeDesignations() {
2408 if(typeDesignations
== null) {
2409 this.typeDesignations
= new HashSet
<>();
2411 return typeDesignations
;
2415 * Removes one element from the set of {@link TypeDesignationBase type designations} assigned to
2416 * <i>this</i> taxon name. The type designation itself will be nullified.
2418 * @param typeDesignation the type designation which should be deleted
2421 @SuppressWarnings("deprecation")
2422 public void removeTypeDesignation(TypeDesignationBase
<?
> typeDesignation
) {
2423 this.typeDesignations
.remove(typeDesignation
);
2424 typeDesignation
.removeTypifiedName(this);
2428 * Returns the set of {@link SpecimenTypeDesignation specimen type designations} assigned
2429 * to <i>this</i> taxon name. The {@link Rank rank} of <i>this</i> taxon name is generally
2430 * "species" or below. The specimen type designations include all the
2431 * specimens on which the typification of this name is based (which are
2432 * exclusively used to typify taxon names belonging to the same
2433 * {@link HomotypicalGroup homotypical group} to which <i>this</i> taxon name
2434 * belongs) and eventually the status of these designations.
2436 * @see SpecimenTypeDesignation
2437 * @see NameTypeDesignation
2438 * @see HomotypicalGroup
2442 public Set
<SpecimenTypeDesignation
> getSpecimenTypeDesignationsOfHomotypicalGroup() {
2443 return this.getHomotypicalGroup().getSpecimenTypeDesignations();
2446 //*********************** NAME TYPE DESIGNATION *********************************************//
2449 * Returns the set of {@link NameTypeDesignation name type designations} assigned
2450 * to <i>this</i> taxon name the rank of which must be above "species".
2451 * The name type designations include all the taxon names used to typify
2452 * <i>this</i> taxon name and eventually the rejected or conserved status
2453 * of these designations.
2455 * @see NameTypeDesignation
2456 * @see SpecimenTypeDesignation
2460 public Set
<NameTypeDesignation
> getNameTypeDesignations() {
2461 Set
<NameTypeDesignation
> result
= new HashSet
<>();
2462 for (TypeDesignationBase
<?
> typeDesignation
: this.typeDesignations
){
2463 if (typeDesignation
.isInstanceOf(NameTypeDesignation
.class)){
2464 result
.add(CdmBase
.deproxy(typeDesignation
, NameTypeDesignation
.class));
2471 * Creates and adds a new {@link NameTypeDesignation name type designation}
2472 * to <i>this</i> taxon name's set of type designations.
2474 * @param typeSpecies the taxon name to be used as type of <i>this</i> taxon name
2475 * @param citation the reference for this new designation
2476 * @param citationMicroReference the string with the details (generally pages) within the reference
2477 * @param originalNameString the taxon name string used in the reference to assert this designation
2478 * @param isRejectedType the boolean status for a rejected name type designation
2479 * @param isConservedType the boolean status for a conserved name type designation
2480 * @param isLectoType the boolean status for a lectotype name type designation
2481 * @param isNotDesignated the boolean status for a name type designation without name type
2482 * @param addToAllHomotypicNames the boolean indicating whether the name type designation should be
2483 * added to all taxon names of the homotypical group this taxon name belongs to
2485 * @see #getNameTypeDesignations()
2486 * @see NameTypeDesignation
2487 * @see TypeDesignationBase#isNotDesignated()
2490 public NameTypeDesignation
addNameTypeDesignation(TaxonName typeSpecies
,
2492 String citationMicroReference
,
2493 String originalNameString
,
2494 NameTypeDesignationStatus status
,
2495 boolean isRejectedType
,
2496 boolean isConservedType
,
2497 /*boolean isLectoType, */
2498 boolean isNotDesignated
,
2499 boolean addToAllHomotypicNames
) {
2500 NameTypeDesignation nameTypeDesignation
= new NameTypeDesignation(typeSpecies
, citation
, citationMicroReference
, originalNameString
, status
, isRejectedType
, isConservedType
, isNotDesignated
);
2501 //nameTypeDesignation.setLectoType(isLectoType);
2502 addTypeDesignation(nameTypeDesignation
, addToAllHomotypicNames
);
2503 return nameTypeDesignation
;
2507 * Creates and adds a new {@link NameTypeDesignation name type designation}
2508 * to <i>this</i> taxon name's set of type designations.
2510 * @param typeSpecies the taxon name to be used as type of <i>this</i> taxon name
2511 * @param citation the reference for this new designation
2512 * @param citationMicroReference the string with the details (generally pages) within the reference
2513 * @param originalNameString the taxon name string used in the reference to assert this designation
2514 * @param status the name type designation status
2515 * @param addToAllHomotypicNames the boolean indicating whether the name type designation should be
2516 * added to all taxon names of the homotypical group this taxon name belongs to
2518 * @see #getNameTypeDesignations()
2519 * @see NameTypeDesignation
2520 * @see TypeDesignationBase#isNotDesignated()
2523 public NameTypeDesignation
addNameTypeDesignation(TaxonName typeSpecies
,
2525 String citationMicroReference
,
2526 String originalNameString
,
2527 NameTypeDesignationStatus status
,
2528 boolean addToAllHomotypicNames
) {
2529 NameTypeDesignation nameTypeDesignation
= new NameTypeDesignation(typeSpecies
, status
, citation
, citationMicroReference
, originalNameString
);
2530 addTypeDesignation(nameTypeDesignation
, addToAllHomotypicNames
);
2531 return nameTypeDesignation
;
2534 //*********************** SPECIMEN TYPE DESIGNATION *********************************************//
2537 * Returns the set of {@link SpecimenTypeDesignation specimen type designations}
2538 * that typify <i>this</i> taxon name.
2542 public Set
<SpecimenTypeDesignation
> getSpecimenTypeDesignations() {
2543 Set
<SpecimenTypeDesignation
> result
= new HashSet
<>();
2544 for (TypeDesignationBase
<?
> typeDesignation
: this.typeDesignations
){
2545 if (typeDesignation
.isInstanceOf(SpecimenTypeDesignation
.class)){
2546 result
.add(CdmBase
.deproxy(typeDesignation
, SpecimenTypeDesignation
.class));
2554 * Creates and adds a new {@link SpecimenTypeDesignation specimen type designation}
2555 * to <i>this</i> taxon name's set of type designations.
2557 * @param typeSpecimen the specimen to be used as a type for <i>this</i> taxon name
2558 * @param status the specimen type designation status
2559 * @param citation the reference for this new specimen type designation
2560 * @param citationMicroReference the string with the details (generally pages) within the reference
2561 * @param originalNameString the taxon name used in the reference to assert this designation
2562 * @param isNotDesignated the boolean status for a specimen type designation without specimen type
2563 * @param addToAllHomotypicNames the boolean indicating whether the specimen type designation should be
2564 * added to all taxon names of the homotypical group the typified
2565 * taxon name belongs to
2567 * @see #getSpecimenTypeDesignations()
2568 * @see SpecimenTypeDesignationStatus
2569 * @see SpecimenTypeDesignation
2570 * @see TypeDesignationBase#isNotDesignated()
2573 public SpecimenTypeDesignation
addSpecimenTypeDesignation(DerivedUnit typeSpecimen
,
2574 SpecimenTypeDesignationStatus status
,
2576 String citationMicroReference
,
2577 String originalNameString
,
2578 boolean isNotDesignated
,
2579 boolean addToAllHomotypicNames
) {
2580 SpecimenTypeDesignation specimenTypeDesignation
= new SpecimenTypeDesignation(typeSpecimen
, status
, citation
, citationMicroReference
, originalNameString
, isNotDesignated
);
2581 addTypeDesignation(specimenTypeDesignation
, addToAllHomotypicNames
);
2582 return specimenTypeDesignation
;
2586 public TextualTypeDesignation
addTextualTypeDesignation(
2591 String citationMicroReference
,
2592 String originalNameString
,
2593 boolean addToAllHomotypicNames
) {
2594 TextualTypeDesignation textualTypeDesignation
= TextualTypeDesignation
.NewInstance(text
, language
, isVerbatim
, citation
, citationMicroReference
, originalNameString
);
2595 addTypeDesignation(textualTypeDesignation
, addToAllHomotypicNames
);
2596 return textualTypeDesignation
;
2599 //used by merge strategy
2600 private boolean addTypeDesignation(TypeDesignationBase typeDesignation
){
2601 return addTypeDesignation(typeDesignation
, true);
2605 * Adds a {@link TypeDesignationBase type designation} to <code>this</code> taxon name's set of type designations
2607 * @param typeDesignation the typeDesignation to be added to <code>this</code> taxon name
2608 * @param addToAllNames the boolean indicating whether the type designation should be
2609 * added to all taxon names of the homotypical group the typified
2610 * taxon name belongs to
2611 * @return true if the operation was successful
2613 * @throws IllegalArgumentException if the type designation already has typified names, an {@link IllegalArgumentException exception}
2614 * is thrown. We do this to prevent a type designation to be used for multiple taxon names.
2618 public boolean addTypeDesignation(TypeDesignationBase
<?
> typeDesignation
, boolean addToAllNames
){
2619 //currently typeDesignations are not persisted with the homotypical group
2620 //so explicit adding to the homotypical group is not necessary.
2621 if (typeDesignation
!= null){
2622 checkHomotypicalGroup(typeDesignation
);
2623 this.typeDesignations
.add(typeDesignation
);
2624 typeDesignation
.addTypifiedName(this);
2627 for (TaxonName taxonName
: this.getHomotypicalGroup().getTypifiedNames()){
2628 if (taxonName
!= this){
2629 taxonName
.addTypeDesignation(typeDesignation
, false);
2638 * Throws an Exception this type designation already has typified names from another homotypical group.
2639 * @param typeDesignation
2641 private void checkHomotypicalGroup(TypeDesignationBase
<?
> typeDesignation
) {
2642 if(typeDesignation
.getTypifiedNames().size() > 0){
2643 Set
<HomotypicalGroup
> groups
= new HashSet
<>();
2644 Set
<TaxonName
> names
= typeDesignation
.getTypifiedNames();
2645 for (TaxonName taxonName
: names
){
2646 groups
.add(taxonName
.getHomotypicalGroup());
2648 if (groups
.size() > 1){
2649 throw new IllegalArgumentException("TypeDesignation already has typified names from another homotypical group.");
2656 //*********************** HOMOTYPICAL GROUP *********************************************//
2660 * Returns the {@link HomotypicalGroup homotypical group} to which
2661 * <i>this</i> taxon name belongs. A homotypical group represents all taxon names
2662 * that share the same types.
2664 * @see HomotypicalGroup
2668 public HomotypicalGroup
getHomotypicalGroup() {
2669 if (homotypicalGroup
== null){
2670 homotypicalGroup
= new HomotypicalGroup();
2671 homotypicalGroup
.typifiedNames
.add(this);
2673 return homotypicalGroup
;
2677 * @see #getHomotypicalGroup()
2680 public void setHomotypicalGroup(HomotypicalGroup homotypicalGroup
) {
2681 if (homotypicalGroup
== null){
2682 throw new IllegalArgumentException("HomotypicalGroup of name should never be null but was set to 'null'");
2684 /*if (this.homotypicalGroup != null){
2685 this.homotypicalGroup.removeTypifiedName(this, false);
2687 this.homotypicalGroup
= homotypicalGroup
;
2688 if (!this.homotypicalGroup
.typifiedNames
.contains(this)){
2689 this.homotypicalGroup
.addTypifiedName(this);
2695 // *************************************************************************//
2698 * Returns the complete string containing the
2699 * {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation() nomenclatural reference citation}
2700 * and the {@link #getNomenclaturalMicroReference() details} assigned to <i>this</i> taxon name.
2702 * @return the string containing the nomenclatural reference of <i>this</i> taxon name
2703 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation()
2704 * @see #getNomenclaturalReference()
2705 * @see #getNomenclaturalMicroReference()
2709 public String
getCitationString(){
2710 return getNomenclaturalReference().getNomenclaturalCitation(getNomenclaturalMicroReference());
2714 * Returns the parsing problems
2718 public List
<ParserProblem
> getParsingProblems(){
2719 return ParserProblem
.warningList(this.parsingProblem
);
2723 * Returns the string containing the publication date (generally only year)
2724 * of the {@link #getNomenclaturalReference() nomenclatural reference} for <i>this</i> taxon name, null if there is
2725 * no nomenclatural reference.
2727 * @return the string containing the publication date of <i>this</i> taxon name
2728 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getYear()
2732 @ValidTaxonomicYear(groups
=Level3
.class)
2733 public String
getReferenceYear(){
2734 if (this.getNomenclaturalReference() != null ){
2735 return this.getNomenclaturalReference().getYear();
2742 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
2743 * In this context a taxon base means the use of a taxon name by a reference
2744 * either as a {@link eu.etaxonomy.cdm.model.taxon.Taxon taxon} ("accepted/correct" name) or
2745 * as a (junior) {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
2746 * A taxon name can be used by several distinct {@link eu.etaxonomy.cdm.model.reference.Reference references} but only once
2747 * within a taxonomic treatment (identified by one reference).
2750 * @see #getSynonyms()
2753 public Set
<TaxonBase
> getTaxonBases() {
2754 if(taxonBases
== null) {
2755 this.taxonBases
= new HashSet
<>();
2757 return this.taxonBases
;
2761 * Adds a new {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon base}
2762 * to the set of taxon bases using <i>this</i> taxon name.
2764 * @param taxonBase the taxon base to be added
2765 * @see #getTaxonBases()
2766 * @see #removeTaxonBase(TaxonBase)
2770 public void addTaxonBase(TaxonBase taxonBase
){
2771 Method method
= ReflectionUtils
.findMethod(TaxonBase
.class, "setName", new Class
[] {TaxonName
.class});
2772 ReflectionUtils
.makeAccessible(method
);
2773 ReflectionUtils
.invokeMethod(method
, taxonBase
, new Object
[] {this});
2774 taxonBases
.add(taxonBase
);
2777 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
2779 * @param taxonBase the taxon base which should be removed from the corresponding set
2780 * @see #getTaxonBases()
2781 * @see #addTaxonBase(TaxonBase)
2784 public void removeTaxonBase(TaxonBase taxonBase
){
2785 Method method
= ReflectionUtils
.findMethod(TaxonBase
.class, "setName", new Class
[] {TaxonName
.class});
2786 ReflectionUtils
.makeAccessible(method
);
2787 ReflectionUtils
.invokeMethod(method
, taxonBase
, new Object
[] {null});
2793 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Taxon taxa} ("accepted/correct" names according to any
2794 * reference) that are based on <i>this</i> taxon name. This set is a subset of
2795 * the set returned by getTaxonBases().
2797 * @see eu.etaxonomy.cdm.model.taxon.Taxon
2798 * @see #getTaxonBases()
2799 * @see #getSynonyms()
2803 public Set
<Taxon
> getTaxa(){
2804 Set
<Taxon
> result
= new HashSet
<>();
2805 for (TaxonBase
<?
> taxonBase
: this.taxonBases
){
2806 if (taxonBase
instanceof Taxon
){
2807 result
.add((Taxon
)taxonBase
);
2814 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Synonym (junior) synonyms} (according to any
2815 * reference) that are based on <i>this</i> taxon name. This set is a subset of
2816 * the set returned by getTaxonBases().
2818 * @see eu.etaxonomy.cdm.model.taxon.Synonym
2819 * @see #getTaxonBases()
2824 public Set
<Synonym
> getSynonyms() {
2825 Set
<Synonym
> result
= new HashSet
<>();
2826 for (TaxonBase
<?
> taxonBase
: this.taxonBases
){
2827 if (taxonBase
instanceof Synonym
){
2828 result
.add((Synonym
)taxonBase
);
2834 //***************** REGISTRATION *****************/
2837 public Set
<Registration
> getRegistrations() {
2838 return this.registrations
;
2842 // ************* RELATIONSHIPS *****************************/
2845 * Returns the hybrid child relationships ordered by relationship type, or if equal
2846 * by title cache of the related names.
2847 * @see #getHybridParentRelations()
2851 public List
<HybridRelationship
> getOrderedChildRelationships(){
2852 List
<HybridRelationship
> result
= new ArrayList
<HybridRelationship
>();
2853 result
.addAll(this.hybridChildRelations
);
2854 Collections
.sort(result
);
2855 Collections
.reverse(result
);
2860 public HybridRelationship
addHybridParent(INonViralName parentName
, HybridRelationshipType type
, String ruleConsidered
){
2861 return addHybridParent(parentName
, type
, null, null, ruleConsidered
, null);
2865 public HybridRelationship
addHybridParent(INonViralName parentName
, HybridRelationshipType type
, Reference reference
,
2866 String microReference
, String ruleConsidered
, NomenclaturalCodeEdition codeEdition
){
2867 return new HybridRelationship(this, parentName
, type
, reference
, microReference
, ruleConsidered
, codeEdition
);
2871 * Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship}
2872 * to <i>this</i> botanical name. A HybridRelationship may be of type
2873 * "is first/second parent" or "is male/female parent". By invoking this
2874 * method <i>this</i> botanical name becomes a parent of the hybrid child
2877 * @param childName the botanical name of the child for this new hybrid name relationship
2878 * @param type the type of this new name relationship
2879 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
2881 * @see #addHybridParent(BotanicalName, HybridRelationshipType,String )
2882 * @see #getRelationsToThisName()
2883 * @see #getNameRelations()
2884 * @see #addRelationshipFromName(TaxonName, NameRelationshipType, String)
2885 * @see #addNameRelationship(NameRelationship)
2888 public HybridRelationship
addHybridChild(INonViralName childName
, HybridRelationshipType type
, String ruleConsidered
){
2889 return new HybridRelationship(childName
, this, type
, ruleConsidered
);
2893 public void removeHybridChild(INonViralName child
) {
2894 Set
<HybridRelationship
> hybridRelationships
= new HashSet
<HybridRelationship
>();
2895 hybridRelationships
.addAll(this.getHybridChildRelations());
2896 hybridRelationships
.addAll(this.getHybridParentRelations());
2897 for(HybridRelationship hybridRelationship
: hybridRelationships
) {
2898 // remove name relationship from this side
2899 if (hybridRelationship
.getParentName().equals(this) && hybridRelationship
.getHybridName().equals(child
)) {
2900 this.removeHybridRelationship(hybridRelationship
);
2906 public void removeHybridParent(INonViralName parent
) {
2907 Set
<HybridRelationship
> hybridRelationships
= new HashSet
<HybridRelationship
>();
2908 hybridRelationships
.addAll(this.getHybridChildRelations());
2909 hybridRelationships
.addAll(this.getHybridParentRelations());
2910 for(HybridRelationship hybridRelationship
: hybridRelationships
) {
2911 // remove name relationship from this side
2912 if (hybridRelationship
.getParentName().equals(parent
) && hybridRelationship
.getHybridName().equals(this)) {
2913 this.removeHybridRelationship(hybridRelationship
);
2920 // *********** DESCRIPTIONS *************************************
2923 * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
2924 * to <i>this</i> taxon name. A taxon name description is a piece of information
2925 * concerning the taxon name like for instance the content of its first
2926 * publication (protolog) or a picture of this publication.
2928 * @see #addDescription(TaxonNameDescription)
2929 * @see #removeDescription(TaxonNameDescription)
2930 * @see eu.etaxonomy.cdm.model.description.TaxonNameDescription
2933 public Set
<TaxonNameDescription
> getDescriptions() {
2934 return descriptions
;
2938 * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name description}
2939 * to the set of taxon name descriptions assigned to <i>this</i> taxon name. The
2940 * content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute} of the
2941 * taxon name description itself will be replaced with <i>this</i> taxon name.
2943 * @param description the taxon name description to be added
2944 * @see #getDescriptions()
2945 * @see #removeDescription(TaxonNameDescription)
2948 public void addDescription(TaxonNameDescription description
) {
2949 java
.lang
.reflect
.Field field
= ReflectionUtils
.findField(TaxonNameDescription
.class, "taxonName", TaxonName
.class);
2950 ReflectionUtils
.makeAccessible(field
);
2951 ReflectionUtils
.setField(field
, description
, this);
2952 descriptions
.add(description
);
2955 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
2956 * to <i>this</i> taxon name. The content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute}
2957 * of the description itself will be set to "null".
2959 * @param description the taxon name description which should be removed
2960 * @see #getDescriptions()
2961 * @see #addDescription(TaxonNameDescription)
2962 * @see eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName()
2965 public void removeDescription(TaxonNameDescription description
) {
2966 java
.lang
.reflect
.Field field
= ReflectionUtils
.findField(TaxonNameDescription
.class, "taxonName", TaxonName
.class);
2967 ReflectionUtils
.makeAccessible(field
);
2968 ReflectionUtils
.setField(field
, description
, null);
2969 descriptions
.remove(description
);
2972 // *********** HOMOTYPIC GROUP METHODS **************************************************
2976 public void mergeHomotypicGroups(TaxonName name
){
2977 this.getHomotypicalGroup().merge(name
.getHomotypicalGroup());
2978 //HomotypicalGroup thatGroup = name.homotypicalGroup;
2979 name
.setHomotypicalGroup(this.homotypicalGroup
);
2983 * Returns the boolean value indicating whether a given taxon name belongs
2984 * to the same {@link HomotypicalGroup homotypical group} as <i>this</i> taxon name (true)
2985 * or not (false). Returns "true" only if the homotypical groups of both
2986 * taxon names exist and if they are identical.
2988 * @param homoTypicName the taxon name the homotypical group of which is to be checked
2989 * @return the boolean value of the check
2990 * @see HomotypicalGroup
2994 public boolean isHomotypic(TaxonName homoTypicName
) {
2995 if (homoTypicName
== null) {
2998 HomotypicalGroup homotypicGroup
= homoTypicName
.getHomotypicalGroup();
2999 if (homotypicGroup
== null || this.getHomotypicalGroup() == null) {
3002 if (homotypicGroup
.equals(this.getHomotypicalGroup())) {
3010 * Checks whether name is a basionym for ALL names
3011 * in its homotypical group.
3012 * Returns <code>false</code> if there are no other names in the group
3018 public boolean isGroupsBasionym() {
3019 if (homotypicalGroup
== null){
3020 homotypicalGroup
= HomotypicalGroup
.NewInstance();
3021 homotypicalGroup
.addTypifiedName(this);
3023 Set
<TaxonName
> typifiedNames
= homotypicalGroup
.getTypifiedNames();
3025 // Check whether there are any other names in the group
3026 if (typifiedNames
.size() == 1) {
3030 for (TaxonName taxonName
: typifiedNames
) {
3031 if (!taxonName
.equals(this)) {
3032 if (! isBasionymFor(taxonName
)) {
3041 * Checks whether a basionym relationship exists between fromName and toName.
3049 public boolean isBasionymFor(TaxonName newCombinationName
) {
3050 Set
<NameRelationship
> relations
= newCombinationName
.getRelationsToThisName();
3051 for (NameRelationship relation
: relations
) {
3052 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) &&
3053 relation
.getFromName().equals(this)) {
3061 * Creates a basionym relationship to all other names in this names homotypical
3064 * @see HomotypicalGroup.setGroupBasionym(TaxonName basionymName)
3068 public void makeGroupsBasionym() {
3069 this.homotypicalGroup
.setGroupBasionym(this);
3073 //********* Rank comparison shortcuts ********************//
3075 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
3076 * taxon name is higher than the genus rank (true) or not (false).
3077 * Suprageneric non viral names are monomials.
3078 * Returns false if rank is null.
3081 * @see #isInfraGeneric()
3083 * @see #isInfraSpecific()
3087 public boolean isSupraGeneric() {
3091 return getRank().isSupraGeneric();
3094 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
3095 * taxon name is the genus rank (true) or not (false). Non viral names with
3096 * genus rank are monomials. Returns false if rank is null.
3098 * @see #isSupraGeneric()
3099 * @see #isInfraGeneric()
3101 * @see #isInfraSpecific()
3105 public boolean isGenus() {
3109 return getRank().isGenus();
3114 public boolean isGenusOrSupraGeneric() {
3115 return isGenus()|| isSupraGeneric();
3118 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
3119 * taxon name is higher than the species rank and lower than the
3120 * genus rank (true) or not (false). Infrageneric non viral names are
3121 * binomials. Returns false if rank is null.
3123 * @see #isSupraGeneric()
3126 * @see #isInfraSpecific()
3130 public boolean isInfraGeneric() {
3134 return getRank().isInfraGeneric();
3138 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
3139 * taxon name is higher than the species rank (true) or not (false).
3140 * Returns false if rank is null.
3143 * @see #isInfraGeneric()
3145 * @see #isInfraSpecific()
3149 public boolean isSupraSpecific(){
3153 return getRank().isHigher(Rank
.SPECIES());
3157 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
3158 * taxon name is the species rank (true) or not (false). Non viral names
3159 * with species rank are binomials.
3160 * Returns false if rank is null.
3162 * @see #isSupraGeneric()
3164 * @see #isInfraGeneric()
3165 * @see #isInfraSpecific()
3169 public boolean isSpecies() {
3173 return getRank().isSpecies();
3176 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
3177 * taxon name is lower than the species rank (true) or not (false).
3178 * Infraspecific non viral names are trinomials.
3179 * Returns false if rank is null.
3181 * @see #isSupraGeneric()
3183 * @see #isInfraGeneric()
3188 public boolean isInfraSpecific() {
3192 return getRank().isInfraSpecific();
3196 * Returns true if this name's rank indicates a rank that aggregates species like species
3197 * aggregates or species groups, false otherwise. This methods currently returns false
3198 * for all user defined ranks.
3200 *@see Rank#isSpeciesAggregate()
3206 public boolean isSpeciesAggregate() {
3210 return getRank().isSpeciesAggregate();
3215 * Returns null as the {@link NomenclaturalCode nomenclatural code} that governs
3216 * the construction of <i>this</i> taxon name since there is no specific
3217 * nomenclatural code defined. The real implementention takes place in the
3218 * subclasses {@link IBacterialName BacterialName},
3219 * {@link IBotanicalName BotanicalName}, {@link ICultivarPlantName CultivarPlantName} and
3220 * {@link IZoologicalName ZoologicalName}. Each taxon name is governed by one
3221 * and only one nomenclatural code.
3224 * @see #isCodeCompliant()
3225 * @see #getHasProblem()
3226 * @deprecated use {@link #getNameType()} instead
3230 @java.beans
.Transient
3231 public NomenclaturalCode
getNomenclaturalCode() {
3236 * Generates and returns the string with the scientific name of <i>this</i>
3237 * taxon name (only non viral taxon names can be generated from their
3238 * components). This string may be stored in the inherited
3239 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
3240 * This method overrides the generic and inherited
3241 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() method} from
3242 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
3244 * @return the string with the composed name of this non viral taxon name with authorship (and maybe year)
3245 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
3246 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
3249 // public abstract String generateTitle();
3252 * Creates a basionym relationship between this name and
3253 * each name in its homotypic group.
3255 * @param basionymName
3259 public void setAsGroupsBasionym() {
3261 HomotypicalGroup homotypicalGroup
= this.getHomotypicalGroup();
3262 if (homotypicalGroup
== null) {
3266 Set
<NameRelationship
> relations
= new HashSet
<NameRelationship
>();
3267 Set
<NameRelationship
> removeRelations
= new HashSet
<NameRelationship
>();
3269 for(TaxonName typifiedName
: homotypicalGroup
.getTypifiedNames()){
3271 Set
<NameRelationship
> nameRelations
= typifiedName
.getRelationsFromThisName();
3273 for(NameRelationship nameRelation
: nameRelations
){
3274 relations
.add(nameRelation
);
3278 for (NameRelationship relation
: relations
) {
3280 // If this is a basionym relation, and toName is in the homotypical group,
3281 // remove the relationship.
3282 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) &&
3283 relation
.getToName().getHomotypicalGroup().equals(homotypicalGroup
)) {
3284 removeRelations
.add(relation
);
3288 // Removing relations from a set through which we are iterating causes a
3289 // ConcurrentModificationException. Therefore, we delete the targeted
3290 // relations in a second step.
3291 for (NameRelationship relation
: removeRelations
) {
3292 this.removeNameRelationship(relation
);
3295 for (TaxonName name
: homotypicalGroup
.getTypifiedNames()) {
3296 if (!name
.equals(this)) {
3298 // First check whether the relationship already exists
3299 if (!this.isBasionymFor(name
)) {
3302 name
.addRelationshipFromName(this,
3303 NameRelationshipType
.BASIONYM(), null, null);
3310 * Removes basionym relationship between this name and
3311 * each name in its homotypic group.
3313 * @param basionymName
3317 public void removeAsGroupsBasionym() {
3319 HomotypicalGroup homotypicalGroup
= this.getHomotypicalGroup();
3321 if (homotypicalGroup
== null) {
3325 Set
<NameRelationship
> relations
= new HashSet
<NameRelationship
>();
3326 Set
<NameRelationship
> removeRelations
= new HashSet
<NameRelationship
>();
3328 for(TaxonName typifiedName
: homotypicalGroup
.getTypifiedNames()){
3330 Set
<NameRelationship
> nameRelations
= typifiedName
.getRelationsFromThisName();
3332 for(NameRelationship nameRelation
: nameRelations
){
3333 relations
.add(nameRelation
);
3337 for (NameRelationship relation
: relations
) {
3339 // If this is a basionym relation, and toName is in the homotypical group,
3340 // and fromName is basionymName, remove the relationship.
3341 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) &&
3342 relation
.getFromName().equals(this) &&
3343 relation
.getToName().getHomotypicalGroup().equals(homotypicalGroup
)) {
3344 removeRelations
.add(relation
);
3348 // Removing relations from a set through which we are iterating causes a
3349 // ConcurrentModificationException. Therefore, we delete the targeted
3350 // relations in a second step.
3351 for (NameRelationship relation
: removeRelations
) {
3352 this.removeNameRelationship(relation
);
3358 * Defines the last part of the name.
3359 * This is for infraspecific taxa, the infraspecific epithet,
3360 * for species the specific epithet, for infageneric taxa the infrageneric epithet
3361 * else the genusOrUninomial.
3362 * However, the result does not depend on the rank (which may be not correctly set
3363 * in case of dirty data) but returns the first name part which is not blank
3364 * considering the above order.
3365 * @return the first not blank name part in reverse order
3368 public String
getLastNamePart() {
3370 StringUtils
.isNotBlank(this.getInfraSpecificEpithet())?
3371 this.getInfraSpecificEpithet() :
3372 StringUtils
.isNotBlank(this.getSpecificEpithet()) ?
3373 this.getSpecificEpithet():
3374 StringUtils
.isNotBlank(this.getInfraGenericEpithet()) ?
3375 this.getInfraGenericEpithet():
3376 this.getGenusOrUninomial();
3384 public boolean isHybridName() {
3385 return this.isMonomHybrid() || this.isBinomHybrid() || this.isTrinomHybrid();
3392 public boolean isHybrid() {
3393 return this.isHybridName() || this.isHybridFormula();
3396 // ***************** COMPARE ********************************/
3399 public int compareToName(TaxonName otherName
){
3403 if (otherName
== null) {
3404 throw new NullPointerException("Cannot compare to null.");
3408 otherName
= deproxy(otherName
);
3409 String otherNameCache
= otherName
.getNameCache();
3410 String otherTitleCache
= otherName
.getTitleCache();
3411 //TODO is this really necessary, is it not the normal way how name cache is filled for autonyms?
3412 if (otherName
.isAutonym()){
3413 boolean isProtected
= otherName
.isProtectedNameCache();
3414 String oldNameCache
= otherName
.getNameCache();
3415 otherName
.setProtectedNameCache(false);
3416 otherName
.setNameCache(null, false);
3417 otherNameCache
= otherName
.getNameCache();
3418 otherName
.setNameCache(oldNameCache
, isProtected
);
3422 String thisNameCache
= this.getNameCache();
3423 String thisTitleCache
= this.getTitleCache();
3425 if (this.isAutonym()){
3426 boolean isProtected
= this.isProtectedNameCache();
3427 String oldNameCache
= this.getNameCache();
3428 this.setProtectedNameCache(false);
3429 this.setNameCache(null, false);
3430 thisNameCache
= this.getNameCache();
3431 this.setNameCache(oldNameCache
, isProtected
);
3435 // Compare name cache of taxon names
3436 if (CdmUtils
.isNotBlank(otherNameCache
) && CdmUtils
.isNotBlank(thisNameCache
)) {
3437 thisNameCache
= normalizeName(thisNameCache
);
3438 otherNameCache
= normalizeName(otherNameCache
);
3439 result
= thisNameCache
.compareTo(otherNameCache
);
3442 // Compare title cache of taxon names
3444 if ( (CdmUtils
.isNotBlank(otherTitleCache
) || CdmUtils
.isNotBlank(thisTitleCache
))) {
3445 thisTitleCache
= normalizeName(thisTitleCache
);
3446 otherTitleCache
= normalizeName(otherTitleCache
);
3447 result
= CdmUtils
.nullSafeCompareTo(thisTitleCache
, otherTitleCache
);
3454 static final String HYBRID_SIGN
= UTF8
.HYBRID
.toString();
3455 static final String QUOT_SIGN
= "[\\u02BA\\u0022\\u0022]";
3458 * @param thisNameCache
3459 * @param HYBRID_SIGN
3463 private String
normalizeName(String thisNameCache
) {
3464 thisNameCache
= thisNameCache
.replaceAll(HYBRID_SIGN
, "");
3465 thisNameCache
= thisNameCache
.replaceAll(QUOT_SIGN
, "");
3466 return thisNameCache
;
3469 // ********************** INTERFACES ********************************************/
3472 * Method to cast a interfaced name to a concrete name.
3473 * The method includes a deproxy to guarantee that no
3474 * class cast exception is thrown.
3476 * @see #castAndDeproxy(Set)
3477 * @param interfacedName
3480 public static TaxonName
castAndDeproxy(ITaxonNameBase interfacedName
){
3481 return deproxy(interfacedName
, TaxonName
.class);
3485 * Method to cast a set of interfaced names to concrete namex.
3486 * The method includes a deproxy to guarantee that no
3487 * class cast exception is thrown.
3489 * @see #castAndDeproxy(ITaxonNameBase)
3490 * @param naminterfacedNames
3493 public static Set
<TaxonName
> castAndDeproxy(Set
<ITaxonNameBase
> naminterfacedNames
) {
3494 Set
<TaxonName
> result
= new HashSet
<>();
3495 for (ITaxonNameBase naminterfacedName
: naminterfacedNames
){
3496 result
.add(castAndDeproxy(naminterfacedName
));
3501 //************************ isType ***********************************************/
3507 public boolean isNonViral() {
3508 return nameType
.isNonViral();
3512 public boolean isZoological(){
3513 return nameType
.isZoological();
3516 public boolean isBotanical() {
3517 return nameType
.isBotanical();
3520 public boolean isCultivar() {
3521 return nameType
.isCultivar();
3524 public boolean isBacterial() {
3525 return nameType
.isBacterial();
3528 public boolean isViral() {
3529 return nameType
.isViral();
3532 // *********************** CACHES ***************************************************/
3536 public boolean updateCaches() {
3537 boolean result
= updateAuthorshipCache();
3538 result
|= updateNameCache();
3539 result
|= super.updateCaches();
3540 result
|= updateFullTitleCache();
3545 * Updates the authorship cache if any changes appeared in the authors nomenclatural caches.
3546 * Deletes the titleCache and the fullTitleCache if not protected and if any change has happened.
3547 * @return <code>true</code> if something changed
3549 private boolean updateAuthorshipCache() {
3550 //updates the authorship cache if necessary and via the listener updates all higher caches
3551 if (protectedAuthorshipCache
== false){
3552 String oldCache
= this.authorshipCache
;
3553 String newCache
= cacheStrategy
.getAuthorshipCache(this);
3554 if (!CdmUtils
.nullSafeEqual(oldCache
, newCache
)){
3555 this.setAuthorshipCache(null, false);
3556 this.getAuthorshipCache();
3566 private boolean updateNameCache() {
3567 //updates the name cache if necessary and via the listener updates all higher caches
3568 if (protectedNameCache
== false){
3569 String oldCache
= this.nameCache
;
3570 String newCache
= cacheStrategy
.getNameCache(this);
3571 if (!CdmUtils
.nullSafeEqual(oldCache
, newCache
)){
3572 this.setNameCache(null, false);
3573 this.getNameCache();
3584 private boolean updateFullTitleCache() {
3585 if (protectedFullTitleCache
== false){
3586 String oldCache
= this.fullTitleCache
;
3587 String newCache
= getTruncatedCache(cacheStrategy
.getFullTitleCache(this));
3588 if (!CdmUtils
.nullSafeEqual(oldCache
, newCache
)){
3589 this.setFullTitleCache(null, false);
3590 this.getFullTitleCache();
3598 //*********************** CLONE ********************************************************/
3601 * Clones <i>this</i> taxon name. This is a shortcut that enables to create
3602 * a new instance that differs only slightly from <i>this</i> taxon name by
3603 * modifying only some of the attributes.<BR><BR>
3604 * Usages of this name in a taxon concept are <b>not</b> cloned.<BR>
3605 * <b>The name gets a newly created homotypical group</b><BR>
3606 * (CAUTION: this behavior needs to be discussed and may change in future).<BR><BR>
3607 * {@link TaxonNameDescription Name descriptions} are cloned and not reused.<BR>
3608 * {@link TypeDesignationBase Type designations} are cloned and not reused.<BR>
3610 * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
3611 * @see java.lang.Object#clone()
3614 public Object
clone() {
3617 result
= (TaxonName
)super.clone();
3619 //taxonBases -> empty
3620 result
.taxonBases
= new HashSet
<>();
3623 if (! protectedFullTitleCache
){
3624 result
.fullTitleCache
= null;
3628 result
.descriptions
= new HashSet
<>();
3629 for (TaxonNameDescription taxonNameDescription
: getDescriptions()){
3630 TaxonNameDescription newDescription
= (TaxonNameDescription
)taxonNameDescription
.clone();
3631 result
.descriptions
.add(newDescription
);
3635 result
.status
= new HashSet
<>();
3636 for (NomenclaturalStatus nomenclaturalStatus
: getStatus()){
3637 NomenclaturalStatus newStatus
= (NomenclaturalStatus
)nomenclaturalStatus
.clone();
3638 result
.status
.add(newStatus
);
3643 result
.relationsToThisName
= new HashSet
<>();
3644 for (NameRelationship toRelationship
: getRelationsToThisName()){
3645 NameRelationship newRelationship
= (NameRelationship
)toRelationship
.clone();
3646 newRelationship
.setRelatedTo(result
);
3647 result
.relationsToThisName
.add(newRelationship
);
3651 result
.relationsFromThisName
= new HashSet
<>();
3652 for (NameRelationship fromRelationship
: getRelationsFromThisName()){
3653 NameRelationship newRelationship
= (NameRelationship
)fromRelationship
.clone();
3654 newRelationship
.setRelatedFrom(result
);
3655 result
.relationsFromThisName
.add(newRelationship
);
3659 result
.typeDesignations
= new HashSet
<>();
3660 for (TypeDesignationBase
<?
> typeDesignation
: getTypeDesignations()){
3661 TypeDesignationBase
<?
> newDesignation
= (TypeDesignationBase
<?
>)typeDesignation
.clone();
3662 this.removeTypeDesignation(newDesignation
);
3663 result
.addTypeDesignation(newDesignation
, false);
3667 //TODO still needs to be discussed
3668 result
.homotypicalGroup
= HomotypicalGroup
.NewInstance();
3669 result
.homotypicalGroup
.addTypifiedName(this);
3672 //HybridChildRelations
3673 result
.hybridChildRelations
= new HashSet
<>();
3674 for (HybridRelationship hybridRelationship
: getHybridChildRelations()){
3675 HybridRelationship newChildRelationship
= (HybridRelationship
)hybridRelationship
.clone();
3676 newChildRelationship
.setRelatedTo(result
);
3677 result
.hybridChildRelations
.add(newChildRelationship
);
3680 //HybridParentRelations
3681 result
.hybridParentRelations
= new HashSet
<>();
3682 for (HybridRelationship hybridRelationship
: getHybridParentRelations()){
3683 HybridRelationship newParentRelationship
= (HybridRelationship
)hybridRelationship
.clone();
3684 newParentRelationship
.setRelatedFrom(result
);
3685 result
.hybridParentRelations
.add(newParentRelationship
);
3689 if (! protectedNameCache
){
3690 result
.nameCache
= null;
3694 if (! protectedAuthorshipCache
){
3695 result
.authorshipCache
= null;
3698 //no changes to: appendedPharse, nomenclaturalReference,
3699 //nomenclaturalMicroReference, parsingProblem, problemEnds, problemStarts
3700 //protectedFullTitleCache, rank
3701 //basionamyAuthorship, combinationAuthorship, exBasionymAuthorship, exCombinationAuthorship
3702 //genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
3703 //protectedAuthorshipCache, protectedNameCache,
3704 //binomHybrid, monomHybrid, trinomHybrid, hybridFormula,
3706 //subGenusAuthorship, nameApprobation
3710 } catch (CloneNotSupportedException e
) {
3711 logger
.warn("Object does not implement cloneable");
3712 e
.printStackTrace();