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
.agent
.Institution
;
20 import eu
.etaxonomy
.cdm
.model
.agent
.InstitutionalMembership
;
21 import eu
.etaxonomy
.cdm
.model
.agent
.Person
;
22 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
23 import eu
.etaxonomy
.cdm
.model
.common
.IParsable
;
24 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
25 import eu
.etaxonomy
.cdm
.model
.common
.IReferencedEntity
;
26 import eu
.etaxonomy
.cdm
.model
.common
.TimePeriod
;
28 import org
.apache
.log4j
.Logger
;
29 import org
.hibernate
.annotations
.Cascade
;
30 import org
.hibernate
.annotations
.CascadeType
;
31 import org
.hibernate
.collection
.PersistentSet
;
33 import eu
.etaxonomy
.cdm
.strategy
.cache
.INameCacheStrategy
;
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
{
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
>();
75 //if set, the Reference.isNomenclaturallyRelevant flag should be set to true!
76 private INomenclaturalReference nomenclaturalReference
;
78 //this flag shows if the getNameCache should return generated value(false) or the given String(true)
79 protected boolean protectedNameCache
;
81 protected INameCacheStrategy
<T
> cacheStrategy
;
84 // * Returns a TaxonNameBase instance
87 // abstract public static TaxonNameBase PARSED_NAME(String fullName);
89 // ************* CONSTRUCTORS *************/
91 * Class constructor: creates a new empty taxon name instance.
93 * @see #TaxonNameBase(Rank)
94 * @see #TaxonNameBase(HomotypicalGroup)
95 * @see #TaxonNameBase(Rank, HomotypicalGroup)
97 public TaxonNameBase() {
101 * Class constructor: creates a new taxon name instance
102 * only containing its {@link common.Rank rank}.
104 * @param rank the rank to be assigned to this taxon name
105 * @see #TaxonNameBase()
106 * @see #TaxonNameBase(HomotypicalGroup)
107 * @see #TaxonNameBase(Rank, HomotypicalGroup)
109 public TaxonNameBase(Rank rank
) {
113 * Class constructor: creates a new taxon name instance
114 * only containing its {@link common.HomotypicalGroup homotypical group}.
115 * The new taxon name instance will be also added to the set of taxon names
116 * belonging to this homotypical group. If the homotypical group
117 * does not exist a new instance will be created for it.
119 * @param homotypicalGroup the homotypical group to which this taxon name belongs
120 * @see #TaxonNameBase()
121 * @see #TaxonNameBase(Rank)
122 * @see #TaxonNameBase(Rank, HomotypicalGroup)
124 public TaxonNameBase(HomotypicalGroup homotypicalGroup
) {
125 this(null, homotypicalGroup
);
128 * Class constructor: creates a new instance of a taxon name
129 * only containing its {@link common.Rank rank} and
130 * its {@link common.HomotypicalGroup homotypical group}.
132 * @param rank the rank to be assigned to this taxon name
133 * @param homotypicalGroup the homotypical group to which this taxon name belongs
134 * @see #TaxonNameBase()
135 * @see #TaxonNameBase(Rank)
136 * @see #TaxonNameBase(HomotypicalGroup)
138 public TaxonNameBase(Rank rank
, HomotypicalGroup homotypicalGroup
) {
141 if (homotypicalGroup
== null){
142 homotypicalGroup
= new HomotypicalGroup();
144 homotypicalGroup
.addTypifiedName(this);
147 //********* METHODS **************************************/
152 * Generates the composed name string of this taxon name without authors
153 * or year according to the strategy defined in
154 * {@link eu.etaxonomy.cdm.strategy.cache.INameCacheStrategy INameCacheStrategy}.
155 * The result might be stored in {@link #getNameCache() nameCache} if the
156 * flag {@link #isProtectedNameCache() protectedNameCache} is not set.
158 * @return the string with the composed name of this taxon name without authors or year
160 protected String
generateNameCache(){
161 if (cacheStrategy
== null){
162 logger
.warn("No CacheStrategy defined for taxonName: " + this.toString());
165 return cacheStrategy
.getNameCache((T
)this);
170 * Returns or generates the nameCache (scientific name
171 * without author strings and year) string for this taxon name. If the
172 * {@link #isProtectedNameCache() protectedNameCache} flag is not set (False)
173 * the string will be generated according to a defined strategy,
174 * otherwise the value of the actual nameCache string will be returned.
176 * @return the string which identifies this taxon name (without authors or year)
177 * @see #generateNameCache()
179 public String
getNameCache() {
180 if (protectedNameCache
){
181 return this.nameCache
;
183 // is title dirty, i.e. equal NULL?
184 if (nameCache
== null){
185 this.nameCache
= generateNameCache();
191 * Assigns a nameCache string to this taxon name and protects it from being overwritten.
193 * @param nameCache the string which identifies this taxon name (without authors or year)
194 * @see #getNameCache()
196 public void setNameCache(String nameCache
){
197 this.nameCache
= nameCache
;
198 this.setProtectedTitleCache(false);
199 this.setProtectedNameCache(true);
203 * Returns the boolean value of the flag intended to protect (true)
204 * or not (false) the {@link #getNameCache() nameCache} (scientific name without author strings and year)
205 * string of this taxon name.
207 * @return the boolean value of the protectedNameCache flag
208 * @see #getNameCache()
210 public boolean isProtectedNameCache() {
211 return protectedNameCache
;
215 * @see #isProtectedNameCache()
217 public void setProtectedNameCache(boolean protectedNameCache
) {
218 this.protectedNameCache
= protectedNameCache
;
222 * Returns the boolean value "true" if the components of this taxon name
223 * follow the rules of the corresponding {@link NomenclaturalCode nomenclatural code},
224 * "false" otherwise. The nomenclatural code depends on
225 * the concrete name subclass ({@link BacterialName BacterialName},
226 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName},
227 * {@link ZoologicalName ZoologicalName} or {@link ViralName ViralName})
228 * to which this taxon name belongs.
230 * @return the boolean value expressing the compliance of this taxon name to the nomenclatural code
233 public abstract boolean isCodeCompliant();
237 * Returns the set of all {@link NameRelationship name relationships}
238 * in which this taxon name is involved. A taxon name can be both source
239 * in some name relationships or target in some others.
241 * @see #getRelationsToThisName()
242 * @see #getRelationsFromThisName()
243 * @see #addNameRelationship(NameRelationship)
244 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
245 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
248 public Set
<NameRelationship
> getNameRelations() {
249 Set
<NameRelationship
> rels
= new HashSet
<NameRelationship
>();
250 rels
.addAll(getRelationsFromThisName());
251 rels
.addAll(getRelationsToThisName());
255 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from this taxon name to another taxon name
256 * and adds it both to the set of {@link #getRelationsFromThisName() relations from this taxon name} and
257 * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
259 * @param toName the taxon name of the target for this new name relationship
260 * @param type the type of this new name relationship
261 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
262 * @see #getRelationsToThisName()
263 * @see #getNameRelations()
264 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
265 * @see #addNameRelationship(NameRelationship)
267 public void addRelationshipToName(TaxonNameBase toName
, NameRelationshipType type
, String ruleConsidered
){
268 NameRelationship rel
= new NameRelationship(toName
, this, type
, ruleConsidered
);
271 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to this taxon name
272 * and adds it both to the set of {@link #getRelationsToThisName() relations to this taxon name} and
273 * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
275 * @param fromName the taxon name of the source for this new name relationship
276 * @param type the type of this new name relationship
277 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
278 * @see #getRelationsFromThisName()
279 * @see #getNameRelations()
280 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
281 * @see #addNameRelationship(NameRelationship)
283 public void addRelationshipFromName(TaxonNameBase fromName
, NameRelationshipType type
, String ruleConsidered
){
284 NameRelationship rel
= new NameRelationship(this, fromName
, type
, ruleConsidered
);
287 * Adds an existing {@link NameRelationship name relationship} either to the set of
288 * {@link #getRelationsToThisName() relations to this taxon name} or to the set of
289 * {@link #getRelationsFromThisName() relations from this taxon name}. If neither the
290 * source nor the target of the name relationship match with this taxon
291 * no addition will be carried out.
293 * @param rel the name relationship to be added to one of this taxon name's name relationships sets
294 * @see #getNameRelations()
295 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
296 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
298 protected void addNameRelationship(NameRelationship rel
) {
299 if (rel
!=null && rel
.getToName().equals(this)){
300 this.relationsToThisName
.add(rel
);
301 }else if(rel
!=null && rel
.getFromName().equals(this)){
302 this.relationsFromThisName
.add(rel
);
304 //TODO: raise error???
308 * Removes one {@link NameRelationship name relationship} from one of both sets of
309 * {@link #getNameRelations() name relationships} in which this taxon name is involved.
310 * The name relationship will also be removed from one of both sets belonging
311 * to the second taxon name involved. Furthermore the fromName and toName
312 * attributes of the name relationship object will be nullified.
314 * @param nameRelation the name relationship which should be deleted from one of both sets
315 * @see #getNameRelations()
317 public void removeNameRelationship(NameRelationship nameRelation
) {
318 //TODO to be implemented?
319 logger
.warn("not yet fully implemented?");
320 this.relationsToThisName
.remove(nameRelation
);
321 this.relationsFromThisName
.remove(nameRelation
);
326 * Returns the set of all {@link NameRelationship name relationships}
327 * in which this taxon name is involved as a source.
329 * @see #getNameRelations()
330 * @see #getRelationsToThisName()
331 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
333 @OneToMany(mappedBy
="fromName", fetch
= FetchType
.EAGER
)
334 @Cascade({CascadeType
.SAVE_UPDATE
})
335 public Set
<NameRelationship
> getRelationsFromThisName() {
336 return relationsFromThisName
;
338 private void setRelationsFromThisName(Set
<NameRelationship
> relationsFromThisName
) {
339 this.relationsFromThisName
= relationsFromThisName
;
343 * Returns the set of all {@link NameRelationship name relationships}
344 * in which this taxon name is involved as a target.
346 * @see #getNameRelations()
347 * @see #getRelationsFromThisName()
348 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
350 @OneToMany(mappedBy
="toName", fetch
= FetchType
.EAGER
)
351 @Cascade({CascadeType
.SAVE_UPDATE
})
352 public Set
<NameRelationship
> getRelationsToThisName() {
353 return relationsToThisName
;
355 private void setRelationsToThisName(Set
<NameRelationship
> relationsToThisName
) {
356 this.relationsToThisName
= relationsToThisName
;
361 * Returns the set of {@link NomenclaturalStatus nomenclatural status} assigned
362 * to this taxon name according to its corresponding nomenclature code.
363 * This includes the {@link NomenclaturalStatusType type} of the nomenclatural status
364 * and the nomenclatural code rule considered.
366 * @see NomenclaturalStatus
367 * @see NomenclaturalStatusType
369 @OneToMany(fetch
= FetchType
.EAGER
)
370 @Cascade({CascadeType
.SAVE_UPDATE
})
371 public Set
<NomenclaturalStatus
> getStatus() {
377 protected void setStatus(Set
<NomenclaturalStatus
> nomStatus
) {
378 this.status
= nomStatus
;
381 * Adds a new {@link NomenclaturalStatus nomenclatural status}
382 * to this taxon name's set of nomenclatural status.
384 * @param nomStatus the nomenclatural status to be added
387 public void addStatus(NomenclaturalStatus nomStatus
) {
388 this.status
.add(nomStatus
);
391 * Removes one element from the set of nomenclatural status of this taxon name.
392 * Type and ruleConsidered attributes of the nomenclatural status object
395 * @param nomStatus the nomenclatural status of this taxon name which should be deleted
398 public void removeStatus(NomenclaturalStatus nomStatus
) {
399 //TODO to be implemented?
400 logger
.warn("not yet fully implemented?");
401 this.status
.remove(nomStatus
);
406 * Indicates if this taxon name is a {@link NameRelationshipType.BASIONYM() basionym}
407 * or a {@link NameRelationshipType.REPLACED_SYNONYM() replaced synonym}
408 * of any other taxon name. Returns true, if a basionym or a replaced synonym
409 * relationship from this taxon name to another taxon name exists,
410 * false otherwise (also in case this taxon name is the only one in the
411 * homotypical group).
414 public boolean isOriginalCombination(){
415 Set
<NameRelationship
> relationsFromThisName
= this.getRelationsFromThisName();
416 for (NameRelationship relation
: relationsFromThisName
) {
417 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) ||
418 relation
.getType().equals(NameRelationshipType
.REPLACED_SYNONYM())) {
426 * Returns the taxon name which is the {@link NameRelationshipType.BASIONYM() basionym} of this taxon name.
427 * The basionym of a taxon name is its epithet-bringing synonym.
428 * For instance Pinus abies L. was published by Linnaeus and the botanist
429 * Karsten transferred later this taxon to the genus Picea. Therefore,
430 * Pinus abies L. is the basionym of the new combination Picea abies (L.) H. Karst.
433 public T
getBasionym(){
434 //TODO: pick the right name relationships...
438 * Assigns another taxon name as {@link NameRelationshipType.BASIONYM() basionym} of this taxon name.
439 * The basionym relationship will be added to this taxon name
440 * and to the basionym. The basionym cannot have itself a basionym.
442 * @see #getBasionym()
443 * @see #setBasionym(TaxonNameBase, String)
445 public void setBasionym(T basionym
){
446 setBasionym(basionym
, null);
449 * Assigns another taxon name as {@link NameRelationshipType.BASIONYM() basionym} of this taxon name
450 * and keeps the nomenclatural rule considered for it. The basionym
451 * relationship will be added to this taxon name and to the basionym.
452 * The basionym cannot have itself a basionym.
454 * @see #getBasionym()
455 * @see #setBasionym(TaxonNameBase)
457 public void setBasionym(T basionym
, String ruleConsidered
){
458 basionym
.addRelationshipToName(this, NameRelationshipType
.BASIONYM(), ruleConsidered
);
465 public INameCacheStrategy
<T
> getCacheStrategy() {
466 return cacheStrategy
;
468 public void setCacheStrategy(INameCacheStrategy cacheStrategy
) {
469 this.cacheStrategy
= cacheStrategy
;
473 //@Cascade({CascadeType.SAVE_UPDATE})
474 public Rank
getRank(){
477 public void setRank(Rank rank
){
482 @Cascade({CascadeType
.SAVE_UPDATE
})
483 public ReferenceBase
getNomenclaturalReference(){
484 return (ReferenceBase
) this.nomenclaturalReference
;
486 public void setNomenclaturalReference(INomenclaturalReference nomenclaturalReference
){
487 this.nomenclaturalReference
= nomenclaturalReference
;
491 public String
getAppendedPhrase(){
492 return this.appendedPhrase
;
494 public void setAppendedPhrase(String appendedPhrase
){
495 this.appendedPhrase
= appendedPhrase
;
498 public String
getNomenclaturalMicroReference(){
499 return this.nomenclaturalMicroReference
;
501 public void setNomenclaturalMicroReference(String nomenclaturalMicroReference
){
502 this.nomenclaturalMicroReference
= nomenclaturalMicroReference
;
505 public boolean getHasProblem(){
506 return this.hasProblem
;
508 public void setHasProblem(boolean hasProblem
){
509 this.hasProblem
= hasProblem
;
512 * Same as getHasProblem()
515 public boolean hasProblem(){
516 return getHasProblem();
521 //TODO @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE_ORPHAN})
522 @Cascade(CascadeType
.SAVE_UPDATE
)
523 public Set
<NameTypeDesignation
> getNameTypeDesignations() {
524 return nameTypeDesignations
;
526 protected void setNameTypeDesignations(Set
<NameTypeDesignation
> nameTypeDesignations
) {
527 this.nameTypeDesignations
= nameTypeDesignations
;
530 public void addTypeDesignation(TaxonNameBase typeSpecies
, ReferenceBase citation
, String citationMicroReference
, String originalNameString
, boolean isRejectedType
, boolean isConservedType
) {
531 NameTypeDesignation td
= new NameTypeDesignation(this, typeSpecies
, citation
, citationMicroReference
, originalNameString
, isRejectedType
, isConservedType
);
533 public void addTypeDesignation(Specimen typeSpecimen
, TypeDesignationStatus status
, ReferenceBase citation
, String citationMicroReference
, String originalNameString
) {
534 this.homotypicalGroup
.addTypeDesignation(typeSpecimen
, status
, citation
, citationMicroReference
, originalNameString
);
536 public void removeTypeDesignation(NameTypeDesignation typeDesignation
) {
537 this.nameTypeDesignations
.remove(typeDesignation
);
539 public void removeTypeDesignation(SpecimenTypeDesignation typeDesignation
) {
540 this.homotypicalGroup
.removeTypeDesignation(typeDesignation
);
545 @Cascade({CascadeType
.SAVE_UPDATE
})
546 public HomotypicalGroup
getHomotypicalGroup() {
547 return homotypicalGroup
;
549 public void setHomotypicalGroup(HomotypicalGroup newHomotypicalGroup
) {
550 if(this.homotypicalGroup
== newHomotypicalGroup
) return;
551 if (homotypicalGroup
!= null) {
552 homotypicalGroup
.typifiedNames
.remove(this);
554 if (newHomotypicalGroup
!= null) {
555 //hack for avoiding org.hibernate.LazyInitializationException: illegal access to loading collection
556 if (newHomotypicalGroup
.typifiedNames
instanceof PersistentSet
){
559 newHomotypicalGroup
.typifiedNames
.add(this);
562 this.homotypicalGroup
= newHomotypicalGroup
;
566 public StrictReferenceBase
getCitation(){
567 logger
.warn("getCitation not yet implemented");
572 public String
getCitationString(){
573 logger
.warn("getCitationString not yet implemented");
578 public String
[] getProblems(){
579 logger
.warn("getProblems not yet implemented");
584 * returns year of according nomenclatural reference, null if nomenclatural
585 * reference does not exist
588 public String
getReferenceYear(){
589 if (this.getNomenclaturalReference() != null ){
590 return this.getNomenclaturalReference().getYear();
596 @OneToMany(mappedBy
="name", fetch
= FetchType
.EAGER
)
597 public Set
<TaxonBase
> getTaxonBases() {
598 return this.taxonBases
;
600 protected void setTaxonBases(Set
<TaxonBase
> taxonBases
) {
601 if (taxonBases
== null){
602 taxonBases
= new HashSet
<TaxonBase
>();
604 this.taxonBases
= taxonBases
;
607 // public void addSynonym(Synonym synonym) {
608 // synonym.setName(this);
610 // public void removeSynonym(Synonym synonym) {
611 // synonym.setName(null);
615 * Return a set of taxa that use this name
619 public Set
<Taxon
> getTaxa(){
620 Set
<Taxon
> result
= new HashSet
<Taxon
>();
621 for (TaxonBase taxonBase
: this.taxonBases
){
622 if (taxonBase
instanceof Taxon
){
623 result
.add((Taxon
)taxonBase
);
630 * Return a set of synonyms that use this name
633 // TODO: implement this method via bidirectional TaxonBase-NameBase relation or use a DAO instead
637 public Set
<Synonym
> getSynonyms() {
638 Set
<Synonym
> result
= new HashSet
<Synonym
>();
639 for (TaxonBase taxonBase
: this.taxonBases
){
640 if (taxonBase
instanceof Synonym
){
641 result
.add((Synonym
)taxonBase
);
648 public Set
<SpecimenTypeDesignation
> getSpecimenTypeDesignations() {
649 return this.getHomotypicalGroup().getTypeDesignations();
652 // Rank comparison shortcuts
654 public boolean isSupraGeneric() {
655 return getRank().isSupraGeneric();
658 public boolean isGenus() {
659 return getRank().isGenus();
662 public boolean isInfraGeneric() {
663 return getRank().isInfraGeneric();
666 public boolean isSpecies() {
667 return getRank().isSpecies();
670 public boolean isInfraSpecific() {
671 return getRank().isInfraSpecific();
675 abstract public NomenclaturalCode
getNomeclaturalCode();