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 eu
.etaxonomy
.cdm
.model
.occurrence
.Specimen
;
13 import eu
.etaxonomy
.cdm
.model
.reference
.INomenclaturalReference
;
14 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceBase
;
15 import eu
.etaxonomy
.cdm
.model
.reference
.StrictReferenceBase
;
16 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
17 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
18 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
19 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
20 import eu
.etaxonomy
.cdm
.model
.common
.IParsable
;
21 import eu
.etaxonomy
.cdm
.model
.common
.IRelated
;
22 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
23 import eu
.etaxonomy
.cdm
.model
.common
.IReferencedEntity
;
24 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
27 import org
.apache
.log4j
.Logger
;
28 import org
.hibernate
.annotations
.Cascade
;
29 import org
.hibernate
.annotations
.CascadeType
;
30 import org
.hibernate
.collection
.PersistentSet
;
32 import eu
.etaxonomy
.cdm
.strategy
.cache
.INameCacheStrategy
;
35 import java
.lang
.reflect
.Method
;
38 import javax
.persistence
.*;
41 * The upmost (abstract) class for scientific taxon names regardless of any
42 * particular nomenclatural code. The scientific name including author strings and
43 * maybe year can be stored as a string in the inherited {@link common.IdentifiableEntity#getTitleCache() titleCache} attribute.
44 * The scientific name string without author strings and year can be stored in the {@link #getNameCache() nameCache} attribute.
45 * The scientific taxon name does not depend on the use made of it
46 * in a publication or a treatment ({@link taxon.TaxonBase taxon concept respectively potential taxon})
47 * as an "accepted" respectively "correct" name ({@link taxon.Taxon taxon})
48 * or as a {@link taxon.Synonym synonym}.
52 * @created 08-Nov-2007 13:06:57
55 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
56 public abstract class TaxonNameBase
<T
extends TaxonNameBase
> extends IdentifiableEntity
<TaxonNameBase
> implements IReferencedEntity
, IParsable
, IRelated
{
57 static Logger logger
= Logger
.getLogger(TaxonNameBase
.class);
58 //The scientific name without author strings and year
59 private String nameCache
;
60 //Non-atomised addition to a name not ruled by a nomenclatural code
61 private String appendedPhrase
;
62 //Details of the nomenclatural reference (protologue). These are mostly (implicitly) pages but can also be figures or
63 //tables or any other element of a publication. {only if a nomenclatural reference exists}
64 private String nomenclaturalMicroReference
;
65 //this flag will be set to true if the parseName method was unable to successfully parse the name
66 private boolean hasProblem
= false;
67 protected Set
<NameTypeDesignation
> nameTypeDesignations
= new HashSet
<NameTypeDesignation
>();
68 private HomotypicalGroup homotypicalGroup
= new HomotypicalGroup();
69 private Set
<NameRelationship
> relationsFromThisName
= new HashSet
<NameRelationship
>();
70 private Set
<NameRelationship
> relationsToThisName
= new HashSet
<NameRelationship
>();
71 private Set
<NomenclaturalStatus
> status
= new HashSet
<NomenclaturalStatus
>();
72 private Set
<TaxonBase
> taxonBases
= new HashSet
<TaxonBase
>();
74 //if set, the Reference.isNomenclaturallyRelevant flag should be set to true!
75 private INomenclaturalReference nomenclaturalReference
;
77 //this flag shows if the getNameCache should return generated value(false) or the given String(true)
78 protected boolean protectedNameCache
;
80 protected INameCacheStrategy
<T
> cacheStrategy
;
81 static Method methodTaxonBaseSetName
;
83 // ************* CONSTRUCTORS *************/
85 * Class constructor: creates a new empty taxon name instance.
87 * @see #TaxonNameBase(Rank)
88 * @see #TaxonNameBase(HomotypicalGroup)
89 * @see #TaxonNameBase(Rank, HomotypicalGroup)
91 public TaxonNameBase() {
95 * Class constructor: creates a new taxon name instance
96 * only containing its {@link common.Rank rank}.
98 * @param rank the rank to be assigned to this taxon name
99 * @see #TaxonNameBase()
100 * @see #TaxonNameBase(HomotypicalGroup)
101 * @see #TaxonNameBase(Rank, HomotypicalGroup)
103 public TaxonNameBase(Rank rank
) {
107 * Class constructor: creates a new taxon name instance
108 * only containing its {@link common.HomotypicalGroup homotypical group}.
109 * The new taxon name instance will be also added to the set of taxon names
110 * belonging to this homotypical group. If the homotypical group
111 * does not exist a new instance will be created for it.
113 * @param homotypicalGroup the homotypical group to which this taxon name belongs
114 * @see #TaxonNameBase()
115 * @see #TaxonNameBase(Rank)
116 * @see #TaxonNameBase(Rank, HomotypicalGroup)
118 public TaxonNameBase(HomotypicalGroup homotypicalGroup
) {
119 this(null, homotypicalGroup
);
122 * Class constructor: creates a new instance of a taxon name
123 * only containing its {@link common.Rank rank} and
124 * its {@link common.HomotypicalGroup homotypical group}.
126 * @param rank the rank to be assigned to this taxon name
127 * @param homotypicalGroup the homotypical group to which this taxon name belongs
128 * @see #TaxonNameBase()
129 * @see #TaxonNameBase(Rank)
130 * @see #TaxonNameBase(HomotypicalGroup)
132 public TaxonNameBase(Rank rank
, HomotypicalGroup homotypicalGroup
) {
135 if (homotypicalGroup
== null){
136 homotypicalGroup
= new HomotypicalGroup();
138 homotypicalGroup
.addTypifiedName(this);
141 //********* METHODS **************************************/
146 * Generates the composed name string of this taxon name without authors
147 * or year according to the strategy defined in
148 * {@link eu.etaxonomy.cdm.strategy.cache.INameCacheStrategy INameCacheStrategy}.
149 * The result might be stored in {@link #getNameCache() nameCache} if the
150 * flag {@link #isProtectedNameCache() protectedNameCache} is not set.
152 * @return the string with the composed name of this taxon name without authors or year
154 protected String
generateNameCache(){
155 if (cacheStrategy
== null){
156 logger
.warn("No CacheStrategy defined for taxonName: " + this.toString());
159 return cacheStrategy
.getNameCache((T
)this);
164 * Returns or generates the nameCache (scientific name
165 * without author strings and year) string for this taxon name. If the
166 * {@link #isProtectedNameCache() protectedNameCache} flag is not set (False)
167 * the string will be generated according to a defined strategy,
168 * otherwise the value of the actual nameCache string will be returned.
170 * @return the string which identifies this taxon name (without authors or year)
171 * @see #generateNameCache()
173 public String
getNameCache() {
174 if (protectedNameCache
){
175 return this.nameCache
;
177 // is title dirty, i.e. equal NULL?
178 if (nameCache
== null){
179 this.nameCache
= generateNameCache();
185 * Assigns a nameCache string to this taxon name and protects it from being overwritten.
187 * @param nameCache the string which identifies this taxon name (without authors or year)
188 * @see #getNameCache()
190 public void setNameCache(String nameCache
){
191 this.nameCache
= nameCache
;
192 this.setProtectedTitleCache(false);
193 this.setProtectedNameCache(true);
197 * Returns the boolean value of the flag intended to protect (true)
198 * or not (false) the {@link #getNameCache() nameCache} (scientific name without author strings and year)
199 * string of this taxon name.
201 * @return the boolean value of the protectedNameCache flag
202 * @see #getNameCache()
204 public boolean isProtectedNameCache() {
205 return protectedNameCache
;
209 * @see #isProtectedNameCache()
211 public void setProtectedNameCache(boolean protectedNameCache
) {
212 this.protectedNameCache
= protectedNameCache
;
216 * Returns the boolean value "true" if the components of this taxon name
217 * follow the rules of the corresponding {@link NomenclaturalCode nomenclatural code},
218 * "false" otherwise. The nomenclatural code depends on
219 * the concrete name subclass ({@link BacterialName BacterialName},
220 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName},
221 * {@link ZoologicalName ZoologicalName} or {@link ViralName ViralName})
222 * to which this taxon name belongs.
224 * @return the boolean value expressing the compliance of this taxon name to the nomenclatural code
227 public abstract boolean isCodeCompliant();
231 * Returns the set of all {@link NameRelationship name relationships}
232 * in which this taxon name is involved. A taxon name can be both source
233 * in some name relationships or target in some others.
235 * @see #getRelationsToThisName()
236 * @see #getRelationsFromThisName()
237 * @see #addNameRelationship(NameRelationship)
238 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
239 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
242 public Set
<NameRelationship
> getNameRelations() {
243 Set
<NameRelationship
> rels
= new HashSet
<NameRelationship
>();
244 rels
.addAll(getRelationsFromThisName());
245 rels
.addAll(getRelationsToThisName());
249 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from this taxon name to another taxon name
250 * and adds it both to the set of {@link #getRelationsFromThisName() relations from this taxon name} and
251 * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
253 * @param toName the taxon name of the target for this new name relationship
254 * @param type the type of this new name relationship
255 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
256 * @see #getRelationsToThisName()
257 * @see #getNameRelations()
258 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
259 * @see #addNameRelationship(NameRelationship)
261 public void addRelationshipToName(TaxonNameBase toName
, NameRelationshipType type
, String ruleConsidered
){
262 NameRelationship rel
= new NameRelationship(toName
, this, type
, ruleConsidered
);
265 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to this taxon name
266 * and adds it both to the set of {@link #getRelationsToThisName() relations to this taxon name} and
267 * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
269 * @param fromName the taxon name of the source for this new name relationship
270 * @param type the type of this new name relationship
271 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
272 * @see #getRelationsFromThisName()
273 * @see #getNameRelations()
274 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
275 * @see #addNameRelationship(NameRelationship)
277 public void addRelationshipFromName(TaxonNameBase fromName
, NameRelationshipType type
, String ruleConsidered
){
278 NameRelationship rel
= new NameRelationship(this, fromName
, type
, ruleConsidered
);
281 * Adds an existing {@link NameRelationship name relationship} either to the set of
282 * {@link #getRelationsToThisName() relations to this taxon name} or to the set of
283 * {@link #getRelationsFromThisName() relations from this taxon name}. If neither the
284 * source nor the target of the name relationship match with this taxon
285 * no addition will be carried out.
287 * @param rel the name relationship to be added to one of this taxon name's name relationships sets
288 * @see #getNameRelations()
289 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
290 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
292 protected void addNameRelationship(NameRelationship rel
) {
293 if (rel
!=null && rel
.getToName().equals(this)){
294 this.relationsToThisName
.add(rel
);
295 }else if(rel
!=null && rel
.getFromName().equals(this)){
296 this.relationsFromThisName
.add(rel
);
298 //TODO: raise error???
302 * Removes one {@link NameRelationship name relationship} from one of both sets of
303 * {@link #getNameRelations() name relationships} in which this taxon name is involved.
304 * The name relationship will also be removed from one of both sets belonging
305 * to the second taxon name involved. Furthermore the fromName and toName
306 * attributes of the name relationship object will be nullified.
308 * @param nameRelation the name relationship which should be deleted from one of both sets
309 * @see #getNameRelations()
311 public void removeNameRelationship(NameRelationship nameRelation
) {
312 //TODO to be implemented?
313 logger
.warn("not yet fully implemented?");
314 this.relationsToThisName
.remove(nameRelation
);
315 this.relationsFromThisName
.remove(nameRelation
);
319 public void addRelationship(RelationshipBase relation
) {
320 if (relation
instanceof NameRelationship
){
321 addNameRelationship((NameRelationship
)relation
);
323 //TODO exception handling
329 * Returns the set of all {@link NameRelationship name relationships}
330 * in which this taxon name is involved as a source.
332 * @see #getNameRelations()
333 * @see #getRelationsToThisName()
334 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
336 @OneToMany(mappedBy
="fromName", fetch
= FetchType
.EAGER
)
337 @Cascade({CascadeType
.SAVE_UPDATE
})
338 public Set
<NameRelationship
> getRelationsFromThisName() {
339 return relationsFromThisName
;
341 private void setRelationsFromThisName(Set
<NameRelationship
> relationsFromThisName
) {
342 this.relationsFromThisName
= relationsFromThisName
;
346 * Returns the set of all {@link NameRelationship name relationships}
347 * in which this taxon name is involved as a target.
349 * @see #getNameRelations()
350 * @see #getRelationsFromThisName()
351 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
353 @OneToMany(mappedBy
="toName", fetch
= FetchType
.EAGER
)
354 @Cascade({CascadeType
.SAVE_UPDATE
})
355 public Set
<NameRelationship
> getRelationsToThisName() {
356 return relationsToThisName
;
358 private void setRelationsToThisName(Set
<NameRelationship
> relationsToThisName
) {
359 this.relationsToThisName
= relationsToThisName
;
364 * Returns the set of {@link NomenclaturalStatus nomenclatural status} assigned
365 * to this taxon name according to its corresponding nomenclature code.
366 * This includes the {@link NomenclaturalStatusType type} of the nomenclatural status
367 * and the nomenclatural code rule considered.
369 * @see NomenclaturalStatus
370 * @see NomenclaturalStatusType
372 @OneToMany(fetch
= FetchType
.EAGER
)
373 @Cascade({CascadeType
.SAVE_UPDATE
})
374 public Set
<NomenclaturalStatus
> getStatus() {
380 protected void setStatus(Set
<NomenclaturalStatus
> nomStatus
) {
381 this.status
= nomStatus
;
384 * Adds a new {@link NomenclaturalStatus nomenclatural status}
385 * to this taxon name's set of nomenclatural status.
387 * @param nomStatus the nomenclatural status to be added
390 public void addStatus(NomenclaturalStatus nomStatus
) {
391 this.status
.add(nomStatus
);
394 * Removes one element from the set of nomenclatural status of this taxon name.
395 * Type and ruleConsidered attributes of the nomenclatural status object
398 * @param nomStatus the nomenclatural status of this taxon name which should be deleted
401 public void removeStatus(NomenclaturalStatus nomStatus
) {
402 //TODO to be implemented?
403 logger
.warn("not yet fully implemented?");
404 this.status
.remove(nomStatus
);
409 * Indicates if this taxon name is a {@link NameRelationshipType.BASIONYM() basionym}
410 * or a {@link NameRelationshipType.REPLACED_SYNONYM() replaced synonym}
411 * of any other taxon name. Returns true, if a basionym or a replaced synonym
412 * relationship from this taxon name to another taxon name exists,
413 * false otherwise (also in case this taxon name is the only one in the
414 * homotypical group).
417 public boolean isOriginalCombination(){
418 Set
<NameRelationship
> relationsFromThisName
= this.getRelationsFromThisName();
419 for (NameRelationship relation
: relationsFromThisName
) {
420 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) ||
421 relation
.getType().equals(NameRelationshipType
.REPLACED_SYNONYM())) {
429 * Returns the taxon name which is the {@link NameRelationshipType.BASIONYM() basionym} of this taxon name.
430 * The basionym of a taxon name is its epithet-bringing synonym.
431 * For instance Pinus abies L. was published by Linnaeus and the botanist
432 * Karsten transferred later this taxon to the genus Picea. Therefore,
433 * Pinus abies L. is the basionym of the new combination Picea abies (L.) H. Karst.
436 public T
getBasionym(){
437 //TODO: pick the right name relationships...
441 * Assigns another taxon name as {@link NameRelationshipType.BASIONYM() basionym} of this taxon name.
442 * The basionym relationship will be added to this taxon name
443 * and to the basionym. The basionym cannot have itself a basionym.
445 * @see #getBasionym()
446 * @see #setBasionym(TaxonNameBase, String)
448 public void setBasionym(T basionym
){
449 setBasionym(basionym
, null);
452 * Assigns another taxon name as {@link NameRelationshipType.BASIONYM() basionym} of this taxon name
453 * and keeps the nomenclatural rule considered for it. The basionym
454 * relationship will be added to this taxon name and to the basionym.
455 * The basionym cannot have itself a basionym.
457 * @see #getBasionym()
458 * @see #setBasionym(TaxonNameBase)
460 public void setBasionym(T basionym
, String ruleConsidered
){
461 basionym
.addRelationshipToName(this, NameRelationshipType
.BASIONYM(), ruleConsidered
);
468 public INameCacheStrategy
<T
> getCacheStrategy() {
469 return cacheStrategy
;
471 public void setCacheStrategy(INameCacheStrategy cacheStrategy
) {
472 this.cacheStrategy
= cacheStrategy
;
476 //@Cascade({CascadeType.SAVE_UPDATE})
477 public Rank
getRank(){
480 public void setRank(Rank rank
){
485 @Cascade({CascadeType
.SAVE_UPDATE
})
486 public ReferenceBase
getNomenclaturalReference(){
487 return (ReferenceBase
) this.nomenclaturalReference
;
489 public void setNomenclaturalReference(INomenclaturalReference nomenclaturalReference
){
490 this.nomenclaturalReference
= nomenclaturalReference
;
494 public String
getAppendedPhrase(){
495 return this.appendedPhrase
;
497 public void setAppendedPhrase(String appendedPhrase
){
498 this.appendedPhrase
= appendedPhrase
;
501 public String
getNomenclaturalMicroReference(){
502 return this.nomenclaturalMicroReference
;
504 public void setNomenclaturalMicroReference(String nomenclaturalMicroReference
){
505 this.nomenclaturalMicroReference
= nomenclaturalMicroReference
;
508 public boolean getHasProblem(){
509 return this.hasProblem
;
511 public void setHasProblem(boolean hasProblem
){
512 this.hasProblem
= hasProblem
;
515 * Same as getHasProblem()
518 public boolean hasProblem(){
519 return getHasProblem();
524 //TODO @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE_ORPHAN})
525 @Cascade(CascadeType
.SAVE_UPDATE
)
526 public Set
<NameTypeDesignation
> getNameTypeDesignations() {
527 return nameTypeDesignations
;
529 protected void setNameTypeDesignations(Set
<NameTypeDesignation
> nameTypeDesignations
) {
530 this.nameTypeDesignations
= nameTypeDesignations
;
533 public void addTypeDesignation(TaxonNameBase typeSpecies
, ReferenceBase citation
, String citationMicroReference
, String originalNameString
, boolean isRejectedType
, boolean isConservedType
) {
534 NameTypeDesignation td
= new NameTypeDesignation(this, typeSpecies
, citation
, citationMicroReference
, originalNameString
, isRejectedType
, isConservedType
);
536 public void addTypeDesignation(Specimen typeSpecimen
, TypeDesignationStatus status
, ReferenceBase citation
, String citationMicroReference
, String originalNameString
) {
537 this.homotypicalGroup
.addTypeDesignation(typeSpecimen
, status
, citation
, citationMicroReference
, originalNameString
);
539 public void removeTypeDesignation(NameTypeDesignation typeDesignation
) {
540 this.nameTypeDesignations
.remove(typeDesignation
);
542 public void removeTypeDesignation(SpecimenTypeDesignation typeDesignation
) {
543 this.homotypicalGroup
.removeTypeDesignation(typeDesignation
);
548 @Cascade({CascadeType
.SAVE_UPDATE
})
549 public HomotypicalGroup
getHomotypicalGroup() {
550 return homotypicalGroup
;
552 @Deprecated //only for bidirectional and persistence use
553 protected void setHomotypicalGroup(HomotypicalGroup newHomotypicalGroup
) {
554 this.homotypicalGroup
= newHomotypicalGroup
;
558 public StrictReferenceBase
getCitation(){
559 logger
.warn("getCitation not yet implemented");
564 public String
getCitationString(){
565 logger
.warn("getCitationString not yet implemented");
570 public String
[] getProblems(){
571 logger
.warn("getProblems not yet implemented");
576 * returns year of according nomenclatural reference, null if nomenclatural
577 * reference does not exist
580 public String
getReferenceYear(){
581 if (this.getNomenclaturalReference() != null ){
582 return this.getNomenclaturalReference().getYear();
588 @OneToMany(mappedBy
="name", fetch
= FetchType
.EAGER
)
589 public Set
<TaxonBase
> getTaxonBases() {
590 return this.taxonBases
;
592 protected void setTaxonBases(Set
<TaxonBase
> taxonBases
) {
593 if (taxonBases
== null){
594 taxonBases
= new HashSet
<TaxonBase
>();
596 this.taxonBases
= taxonBases
;
600 public void addTaxonBase(TaxonBase taxonBase
){
601 taxonBases
.add(taxonBase
);
603 invokeSetMethod(methodTaxonBaseSetName
, taxonBase
);
606 private void initMethods(){
607 if (methodTaxonBaseSetName
== null){
609 methodTaxonBaseSetName
= TaxonBase
.class.getDeclaredMethod("setName", TaxonNameBase
.class);
610 methodTaxonBaseSetName
.setAccessible(true);
611 } catch (Exception e
) {
613 //TODO handle exception
620 * Return a set of taxa that use this name.
621 * @return Set<Taxon> The set of taxa this TaxonName belongs to
624 public Set
<Taxon
> getTaxa(){
625 Set
<Taxon
> result
= new HashSet
<Taxon
>();
626 for (TaxonBase taxonBase
: this.taxonBases
){
627 if (taxonBase
instanceof Taxon
){
628 result
.add((Taxon
)taxonBase
);
635 * Return a set of synonyms that use this name
636 * @return The set of synonyms this TaxonName belongs to
639 public Set
<Synonym
> getSynonyms() {
640 Set
<Synonym
> result
= new HashSet
<Synonym
>();
641 for (TaxonBase taxonBase
: this.taxonBases
){
642 if (taxonBase
instanceof Synonym
){
643 result
.add((Synonym
)taxonBase
);
650 public Set
<SpecimenTypeDesignation
> getSpecimenTypeDesignations() {
651 return this.getHomotypicalGroup().getTypeDesignations();
654 // Rank comparison shortcuts
656 public boolean isSupraGeneric() {
657 return getRank().isSupraGeneric();
660 public boolean isGenus() {
661 return getRank().isGenus();
664 public boolean isInfraGeneric() {
665 return getRank().isInfraGeneric();
668 public boolean isSpecies() {
669 return getRank().isSpecies();
672 public boolean isInfraSpecific() {
673 return getRank().isInfraSpecific();
678 abstract public NomenclaturalCode
getNomeclaturalCode();
681 public String
generateTitle() {
682 // TODO Auto-generated method stub