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
.lang
.reflect
.Method
;
13 import java
.util
.HashSet
;
14 import java
.util
.List
;
18 import javax
.persistence
.Column
;
19 import javax
.persistence
.Entity
;
20 import javax
.persistence
.FetchType
;
21 import javax
.persistence
.Inheritance
;
22 import javax
.persistence
.InheritanceType
;
23 import javax
.persistence
.JoinTable
;
24 import javax
.persistence
.ManyToMany
;
25 import javax
.persistence
.ManyToOne
;
26 import javax
.persistence
.OneToMany
;
27 import javax
.persistence
.Transient
;
28 import javax
.validation
.Valid
;
29 import javax
.validation
.constraints
.NotNull
;
30 import javax
.validation
.constraints
.Size
;
31 import javax
.xml
.bind
.annotation
.XmlAccessType
;
32 import javax
.xml
.bind
.annotation
.XmlAccessorType
;
33 import javax
.xml
.bind
.annotation
.XmlAttribute
;
34 import javax
.xml
.bind
.annotation
.XmlElement
;
35 import javax
.xml
.bind
.annotation
.XmlElementWrapper
;
36 import javax
.xml
.bind
.annotation
.XmlIDREF
;
37 import javax
.xml
.bind
.annotation
.XmlRootElement
;
38 import javax
.xml
.bind
.annotation
.XmlSchemaType
;
39 import javax
.xml
.bind
.annotation
.XmlType
;
41 import org
.apache
.log4j
.Logger
;
42 import org
.hibernate
.annotations
.Cascade
;
43 import org
.hibernate
.annotations
.CascadeType
;
44 import org
.hibernate
.annotations
.Index
;
45 import org
.hibernate
.annotations
.Table
;
46 import org
.hibernate
.envers
.Audited
;
47 import org
.hibernate
.search
.annotations
.Field
;
48 import org
.hibernate
.search
.annotations
.IndexedEmbedded
;
49 import org
.hibernate
.validator
.constraints
.NotEmpty
;
50 import org
.springframework
.util
.ReflectionUtils
;
52 import eu
.etaxonomy
.cdm
.model
.common
.IParsable
;
53 import eu
.etaxonomy
.cdm
.model
.common
.IReferencedEntity
;
54 import eu
.etaxonomy
.cdm
.model
.common
.IRelated
;
55 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
56 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
57 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
58 import eu
.etaxonomy
.cdm
.model
.occurrence
.Specimen
;
59 import eu
.etaxonomy
.cdm
.model
.reference
.INomenclaturalReference
;
60 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
61 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
62 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
63 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
64 import eu
.etaxonomy
.cdm
.strategy
.cache
.name
.CacheUpdate
;
65 import eu
.etaxonomy
.cdm
.strategy
.cache
.name
.INameCacheStrategy
;
66 import eu
.etaxonomy
.cdm
.strategy
.match
.IMatchable
;
67 import eu
.etaxonomy
.cdm
.strategy
.match
.Match
;
68 import eu
.etaxonomy
.cdm
.strategy
.match
.Match
.ReplaceMode
;
69 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchMode
;
70 import eu
.etaxonomy
.cdm
.strategy
.merge
.Merge
;
71 import eu
.etaxonomy
.cdm
.strategy
.merge
.MergeMode
;
72 import eu
.etaxonomy
.cdm
.strategy
.parser
.ParserProblem
;
73 import eu
.etaxonomy
.cdm
.validation
.Level2
;
74 import eu
.etaxonomy
.cdm
.validation
.annotation
.NullOrNotEmpty
;
77 * The upmost (abstract) class for scientific taxon names regardless of any
78 * particular {@link NomenclaturalCode nomenclature code}. The scientific taxon name does not depend
79 * on the use made of it in a publication or a treatment
80 * ({@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon concept respectively potential taxon})
81 * as an {@link eu.etaxonomy.cdm.model.taxon.Taxon "accepted" respectively "correct" (taxon) name}
82 * or as a {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
84 * This class corresponds partially to: <ul>
85 * <li> TaxonName according to the TDWG ontology
86 * <li> ScientificName and CanonicalName according to the TCS
87 * <li> ScientificName according to the ABCD schema
92 * @created 08-Nov-2007 13:06:57
94 @XmlAccessorType(XmlAccessType
.FIELD
)
95 @XmlType(name
= "TaxonNameBase", propOrder
= {
97 "nomenclaturalMicroReference",
98 "nomenclaturalReference",
101 "protectedFullTitleCache",
104 "relationsFromThisName",
105 "relationsToThisName",
110 @XmlRootElement(name
= "TaxonNameBase")
113 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
114 @Table(appliesTo
="TaxonNameBase", indexes
= { @Index(name
= "taxonNameBaseTitleCacheIndex", columnNames
= { "titleCache" }) })
115 public abstract class TaxonNameBase
<T
extends TaxonNameBase
<?
,?
>, S
extends INameCacheStrategy
> extends IdentifiableEntity
<S
> implements IReferencedEntity
, IParsable
, IRelated
, IMatchable
, Cloneable
{
116 private static final long serialVersionUID
= -4530368639601532116L;
117 private static final Logger logger
= Logger
.getLogger(TaxonNameBase
.class);
119 @XmlElement(name
= "FullTitleCache")
120 @Column(length
=330, name
="fullTitleCache")
121 @Match(value
=MatchMode
.CACHE
, cacheReplaceMode
=ReplaceMode
.ALL
)
122 @CacheUpdate(noUpdate
="titleCache")
123 @NotEmpty(groups
= Level2
.class)
125 protected String fullTitleCache
;
127 //if true titleCache will not be automatically generated/updated
128 @XmlElement(name
= "ProtectedFullTitleCache")
129 @CacheUpdate(value
="fullTitleCache", noUpdate
="titleCache")
130 private boolean protectedFullTitleCache
;
132 @XmlElementWrapper(name
= "Descriptions")
133 @XmlElement(name
= "Description")
134 @OneToMany(mappedBy
="taxonName", fetch
= FetchType
.LAZY
)
135 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
, CascadeType
.DELETE_ORPHAN
})
137 private Set
<TaxonNameDescription
> descriptions
= new HashSet
<TaxonNameDescription
>();
139 @XmlElement(name
= "AppendedPhrase")
140 @Field(index
= org
.hibernate
.search
.annotations
.Index
.TOKENIZED
)
141 @CacheUpdate(value
="nameCache")
144 private String appendedPhrase
;
146 @XmlElement(name
= "NomenclaturalMicroReference")
147 @Field(index
= org
.hibernate
.search
.annotations
.Index
.TOKENIZED
)
148 @CacheUpdate(noUpdate
="titleCache")
151 private String nomenclaturalMicroReference
;
154 @CacheUpdate(noUpdate
={"titleCache","fullTitleCache"})
155 private int parsingProblem
= 0;
158 @CacheUpdate(noUpdate
={"titleCache","fullTitleCache"})
159 private int problemStarts
= -1;
162 @CacheUpdate(noUpdate
={"titleCache","fullTitleCache"})
163 private int problemEnds
= -1;
165 @XmlElementWrapper(name
= "TypeDesignations")
166 @XmlElement(name
= "TypeDesignation")
168 @XmlSchemaType(name
= "IDREF")
169 @ManyToMany(fetch
= FetchType
.LAZY
)
171 name
="TaxonNameBase_TypeDesignationBase",
172 joinColumns
=@javax.persistence
.JoinColumn(name
="TaxonNameBase_id"),
173 inverseJoinColumns
=@javax.persistence
.JoinColumn(name
="typedesignations_id")
175 @Cascade({CascadeType
.SAVE_UPDATE
})
177 private Set
<TypeDesignationBase
> typeDesignations
= new HashSet
<TypeDesignationBase
>();
179 @XmlElement(name
= "HomotypicalGroup")
181 @XmlSchemaType(name
= "IDREF")
182 @ManyToOne(fetch
= FetchType
.LAZY
)
183 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
184 @Match(MatchMode
.IGNORE
)
185 @CacheUpdate(noUpdate
="titleCache")
187 private HomotypicalGroup homotypicalGroup
;
189 @XmlElementWrapper(name
= "RelationsFromThisName")
190 @XmlElement(name
= "RelationFromThisName")
191 @OneToMany(mappedBy
="relatedFrom", fetch
= FetchType
.LAZY
)
192 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE_ORPHAN
})
193 @Merge(MergeMode
.RELATION
)
196 private Set
<NameRelationship
> relationsFromThisName
= new HashSet
<NameRelationship
>();
198 @XmlElementWrapper(name
= "RelationsToThisName")
199 @XmlElement(name
= "RelationToThisName")
201 @XmlSchemaType(name
= "IDREF")
202 @OneToMany(mappedBy
="relatedTo", fetch
= FetchType
.LAZY
)
203 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE_ORPHAN
})
204 @Merge(MergeMode
.RELATION
)
207 private Set
<NameRelationship
> relationsToThisName
= new HashSet
<NameRelationship
>();
209 @XmlElementWrapper(name
= "NomenclaturalStatuses")
210 @XmlElement(name
= "NomenclaturalStatus")
211 @OneToMany(fetch
= FetchType
.LAZY
)
212 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
,CascadeType
.DELETE
,CascadeType
.DELETE_ORPHAN
})
214 private Set
<NomenclaturalStatus
> status
= new HashSet
<NomenclaturalStatus
>();
216 @XmlElementWrapper(name
= "TaxonBases")
217 @XmlElement(name
= "TaxonBase")
219 @XmlSchemaType(name
= "IDREF")
220 @OneToMany(mappedBy
="name", fetch
= FetchType
.LAZY
)
222 private Set
<TaxonBase
> taxonBases
= new HashSet
<TaxonBase
>();
224 @XmlElement(name
= "Rank")
226 @XmlSchemaType(name
= "IDREF")
227 @ManyToOne(fetch
= FetchType
.EAGER
)
228 @CacheUpdate(value
="nameCache")
232 @XmlElement(name
= "NomenclaturalReference")
234 @XmlSchemaType(name
= "IDREF")
235 @ManyToOne(fetch
= FetchType
.LAZY
)
236 @Cascade({CascadeType
.SAVE_UPDATE
})
237 @CacheUpdate(noUpdate
="titleCache")
239 private Reference nomenclaturalReference
;
241 // ************* CONSTRUCTORS *************/
243 * Class constructor: creates a new empty taxon name.
245 * @see #TaxonNameBase(Rank)
246 * @see #TaxonNameBase(HomotypicalGroup)
247 * @see #TaxonNameBase(Rank, HomotypicalGroup)
249 public TaxonNameBase() {
253 * Class constructor: creates a new taxon name
254 * only containing its {@link Rank rank}.
256 * @param rank the rank to be assigned to <i>this</i> taxon name
257 * @see #TaxonNameBase()
258 * @see #TaxonNameBase(HomotypicalGroup)
259 * @see #TaxonNameBase(Rank, HomotypicalGroup)
261 public TaxonNameBase(Rank rank
) {
265 * Class constructor: creates a new taxon name
266 * only containing its {@link HomotypicalGroup homotypical group}.
267 * The new taxon name will be also added to the set of taxon names
268 * belonging to this homotypical group.
270 * @param homotypicalGroup the homotypical group to which <i>this</i> taxon name belongs
271 * @see #TaxonNameBase()
272 * @see #TaxonNameBase(Rank)
273 * @see #TaxonNameBase(Rank, HomotypicalGroup)
275 public TaxonNameBase(HomotypicalGroup homotypicalGroup
) {
276 this(null, homotypicalGroup
);
279 * Class constructor: creates a new taxon name
280 * only containing its {@link Rank rank} and
281 * its {@link HomotypicalGroup homotypical group}.
282 * The new taxon name will be also added to the set of taxon names
283 * belonging to this homotypical group.
285 * @param rank the rank to be assigned to <i>this</i> taxon name
286 * @param homotypicalGroup the homotypical group to which <i>this</i> taxon name belongs
287 * @see #TaxonNameBase()
288 * @see #TaxonNameBase(Rank)
289 * @see #TaxonNameBase(HomotypicalGroup)
291 public TaxonNameBase(Rank rank
, HomotypicalGroup homotypicalGroup
) {
294 if (homotypicalGroup
== null){
295 homotypicalGroup
= new HomotypicalGroup();
297 homotypicalGroup
.addTypifiedName(this);
300 abstract protected Map
<String
, java
.lang
.reflect
.Field
> getAllFields();
302 //********* METHODS **************************************/
305 * Returns the boolean value "false" since the components of <i>this</i> taxon name
306 * cannot follow the rules of a corresponding {@link NomenclaturalCode nomenclatural code}
307 * which is not defined for this class. The nomenclature code depends on
308 * the concrete name subclass ({@link BacterialName BacterialName},
309 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName},
310 * {@link ZoologicalName ZoologicalName} or {@link ViralName ViralName})
311 * to which a taxon name belongs.
316 public abstract boolean isCodeCompliant();
318 public abstract String
generateFullTitle();
323 public List
<Object
> getTaggedName(){
324 return getCacheStrategy().getTaggedTitle(this);
328 public String
getFullTitleCache(){
329 if (protectedFullTitleCache
){
330 return this.fullTitleCache
;
332 if (fullTitleCache
== null ){
333 this.fullTitleCache
= getTruncatedCache(generateFullTitle());
335 return fullTitleCache
;
339 public void setFullTitleCache(String fullTitleCache
){
340 setFullTitleCache(fullTitleCache
, PROTECTED
);
343 public void setFullTitleCache(String fullTitleCache
, boolean protectCache
){
344 fullTitleCache
= getTruncatedCache(fullTitleCache
);
345 this.fullTitleCache
= fullTitleCache
;
346 this.setProtectedFullTitleCache(protectCache
);
350 public boolean isProtectedFullTitleCache() {
351 return protectedFullTitleCache
;
354 public void setProtectedFullTitleCache(boolean protectedFullTitleCache
) {
355 this.protectedFullTitleCache
= protectedFullTitleCache
;
359 * Returns the set of all {@link NameRelationship name relationships}
360 * in which <i>this</i> taxon name is involved. A taxon name can be both source
361 * in some name relationships or target in some others.
363 * @see #getRelationsToThisName()
364 * @see #getRelationsFromThisName()
365 * @see #addNameRelationship(NameRelationship)
366 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
367 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
370 public Set
<NameRelationship
> getNameRelations() {
371 Set
<NameRelationship
> rels
= new HashSet
<NameRelationship
>();
372 rels
.addAll(getRelationsFromThisName());
373 rels
.addAll(getRelationsToThisName());
378 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
379 * and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
380 * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
382 * @param toName the taxon name of the target for this new name relationship
383 * @param type the type of this new name relationship
384 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
385 * @see #getRelationsToThisName()
386 * @see #getNameRelations()
387 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
388 * @see #addNameRelationship(NameRelationship)
390 public void addRelationshipToName(TaxonNameBase toName
, NameRelationshipType type
, String ruleConsidered
){
391 addRelationshipToName(toName
, type
, null, null, ruleConsidered
);
392 // NameRelationship rel = new NameRelationship(toName, this, type, ruleConsidered);
396 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
397 * and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
398 * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
400 * @param toName the taxon name of the target for this new name relationship
401 * @param type the type of this new name relationship
402 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
404 * @see #getRelationsToThisName()
405 * @see #getNameRelations()
406 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
407 * @see #addNameRelationship(NameRelationship)
409 public NameRelationship
addRelationshipToName(TaxonNameBase toName
, NameRelationshipType type
, Reference citation
, String microCitation
, String ruleConsidered
){
411 throw new NullPointerException("Null is not allowed as name for a name relationship");
413 NameRelationship rel
= new NameRelationship(toName
, this, type
, citation
, microCitation
, ruleConsidered
);
418 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
419 * and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
420 * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
422 * @param fromName the taxon name of the source for this new name relationship
423 * @param type the type of this new name relationship
424 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
425 * @param citation the reference in which this relation was described
426 * @param microCitation the reference detail for this relation (e.g. page)
427 * @see #getRelationsFromThisName()
428 * @see #getNameRelations()
429 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
430 * @see #addNameRelationship(NameRelationship)
432 public void addRelationshipFromName(TaxonNameBase fromName
, NameRelationshipType type
, String ruleConsidered
){
433 fromName
.addRelationshipToName(this, type
, null, null, ruleConsidered
);
434 // NameRelationship rel = new NameRelationship(this, fromName, type, ruleConsidered);
437 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
438 * and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
439 * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
441 * @param fromName the taxon name of the source for this new name relationship
442 * @param type the type of this new name relationship
443 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
444 * @param citation the reference in which this relation was described
445 * @param microCitation the reference detail for this relation (e.g. page)
446 * @see #getRelationsFromThisName()
447 * @see #getNameRelations()
448 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
449 * @see #addNameRelationship(NameRelationship)
451 public void addRelationshipFromName(TaxonNameBase fromName
, NameRelationshipType type
, Reference citation
, String microCitation
, String ruleConsidered
){
452 fromName
.addRelationshipToName(this, type
, citation
, microCitation
, ruleConsidered
);
456 * Adds an existing {@link NameRelationship name relationship} either to the set of
457 * {@link #getRelationsToThisName() relations to <i>this</i> taxon name} or to the set of
458 * {@link #getRelationsFromThisName() relations from <i>this</i> taxon name}. If neither the
459 * source nor the target of the name relationship match with <i>this</i> taxon name
460 * no addition will be carried out.
462 * @param rel the name relationship to be added to one of <i>this</i> taxon name's name relationships sets
463 * @see #getNameRelations()
464 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
465 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
467 protected void addNameRelationship(NameRelationship rel
) {
468 if (rel
!=null && rel
.getToName().equals(this)){
469 this.relationsToThisName
.add(rel
);
470 }else if(rel
!=null && rel
.getFromName().equals(this)){
471 this.relationsFromThisName
.add(rel
);
473 throw new RuntimeException("NameRelationship is either null or the relationship does not reference this name");
477 * Removes one {@link NameRelationship name relationship} from one of both sets of
478 * {@link #getNameRelations() name relationships} in which <i>this</i> taxon name is involved.
479 * The name relationship will also be removed from one of both sets belonging
480 * to the second taxon name involved. Furthermore the fromName and toName
481 * attributes of the name relationship object will be nullified.
483 * @param nameRelation the name relationship which should be deleted from one of both sets
484 * @see #getNameRelations()
486 public void removeNameRelationship(NameRelationship nameRelation
) {
488 TaxonNameBase fromName
= nameRelation
.getFromName();
489 TaxonNameBase toName
= nameRelation
.getToName();
491 if (nameRelation
!= null) {
492 nameRelation
.setToName(null);
493 nameRelation
.setFromName(null);
496 if (fromName
!= null) {
497 fromName
.removeNameRelationship(nameRelation
);
500 if (toName
!= null) {
501 toName
.removeNameRelationship(nameRelation
);
504 this.relationsToThisName
.remove(nameRelation
);
505 this.relationsFromThisName
.remove(nameRelation
);
508 public void removeRelationToTaxonName(TaxonNameBase toTaxonName
) {
509 Set
<NameRelationship
> nameRelationships
= new HashSet
<NameRelationship
>();
510 // nameRelationships.addAll(this.getNameRelations());
511 nameRelationships
.addAll(this.getRelationsFromThisName());
512 nameRelationships
.addAll(this.getRelationsToThisName());
513 for(NameRelationship nameRelationship
: nameRelationships
) {
514 // remove name relationship from this side
515 if (nameRelationship
.getFromName().equals(this) && nameRelationship
.getToName().equals(toTaxonName
)) {
516 this.removeNameRelationship(nameRelationship
);
523 * Does exactly the same as the addNameRelationship method provided that
524 * the given relationship is a name relationship.
526 * @param relation the relationship to be added to one of <i>this</i> taxon name's name relationships sets
527 * @see #addNameRelationship(NameRelationship)
528 * @see #getNameRelations()
529 * @see NameRelationship
530 * @see eu.etaxonomy.cdm.model.common.RelationshipBase
532 public void addRelationship(RelationshipBase relation
) {
533 if (relation
instanceof NameRelationship
){
534 addNameRelationship((NameRelationship
)relation
);
535 NameRelationshipType type
= (NameRelationshipType
)relation
.getType();
536 if (type
!= null && ( type
.isBasionymRelation() || type
.isReplacedSynonymRelation() ) ){
537 TaxonNameBase fromName
= ((NameRelationship
)relation
).getFromName();
538 TaxonNameBase toName
= ((NameRelationship
)relation
).getToName();
539 fromName
.mergeHomotypicGroups(toName
);
542 logger
.warn("Relationship not of type NameRelationship!");
543 throw new IllegalArgumentException("Relationship not of type NameRelationship");
549 * Returns the set of all {@link NameRelationship name relationships}
550 * in which <i>this</i> taxon name is involved as a source ("from"-side).
552 * @see #getNameRelations()
553 * @see #getRelationsToThisName()
554 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
556 public Set
<NameRelationship
> getRelationsFromThisName() {
557 if(relationsFromThisName
== null) {
558 this.relationsFromThisName
= new HashSet
<NameRelationship
>();
560 return relationsFromThisName
;
564 * Returns the set of all {@link NameRelationship name relationships}
565 * in which <i>this</i> taxon name is involved as a target ("to"-side).
567 * @see #getNameRelations()
568 * @see #getRelationsFromThisName()
569 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
571 public Set
<NameRelationship
> getRelationsToThisName() {
572 if(relationsToThisName
== null) {
573 this.relationsToThisName
= new HashSet
<NameRelationship
>();
575 return relationsToThisName
;
579 * Returns the set of {@link NomenclaturalStatus nomenclatural status} assigned
580 * to <i>this</i> taxon name according to its corresponding nomenclature code.
581 * This includes the {@link NomenclaturalStatusType type} of the nomenclatural status
582 * and the nomenclatural code rule considered.
584 * @see NomenclaturalStatus
585 * @see NomenclaturalStatusType
587 public Set
<NomenclaturalStatus
> getStatus() {
589 this.status
= new HashSet
<NomenclaturalStatus
>();
595 * Adds a new {@link NomenclaturalStatus nomenclatural status}
596 * to <i>this</i> taxon name's set of nomenclatural status.
598 * @param nomStatus the nomenclatural status to be added
601 public void addStatus(NomenclaturalStatus nomStatus
) {
602 this.status
.add(nomStatus
);
606 * Removes one element from the set of nomenclatural status of <i>this</i> taxon name.
607 * Type and ruleConsidered attributes of the nomenclatural status object
610 * @param nomStatus the nomenclatural status of <i>this</i> taxon name which should be deleted
613 public void removeStatus(NomenclaturalStatus nomStatus
) {
614 //TODO to be implemented?
615 logger
.warn("not yet fully implemented?");
616 this.status
.remove(nomStatus
);
621 * Indicates whether <i>this</i> taxon name is a {@link NameRelationshipType#BASIONYM() basionym}
622 * or a {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym}
623 * of any other taxon name. Returns "true", if a basionym or a replaced
624 * synonym {@link NameRelationship relationship} from <i>this</i> taxon name to another taxon name exists,
625 * false otherwise (also in case <i>this</i> taxon name is the only one in the
626 * homotypical group).
629 public boolean isOriginalCombination(){
630 Set
<NameRelationship
> relationsFromThisName
= this.getRelationsFromThisName();
631 for (NameRelationship relation
: relationsFromThisName
) {
632 if (relation
.getType().isBasionymRelation() ||
633 relation
.getType().isReplacedSynonymRelation()) {
641 * Returns the taxon name which is the {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
642 * The basionym of a taxon name is its epithet-bringing synonym.
643 * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
644 * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
645 * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
647 * If more than one basionym exists one is choosen at radom.
649 * If no basionym exists null is returned.
652 public TaxonNameBase
getBasionym(){
653 Set
<TaxonNameBase
> basionyms
= getBasionyms();
654 if (basionyms
.size() == 0){
657 return basionyms
.iterator().next();
662 * Returns the set of taxon names which are the {@link NameRelationshipType#BASIONYM() basionyms} of <i>this</i> taxon name.
663 * The basionym of a taxon name is its epithet-bringing synonym.
664 * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
665 * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
666 * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
669 public Set
<TaxonNameBase
> getBasionyms(){
670 Set
<TaxonNameBase
> result
= new HashSet
<TaxonNameBase
>();
671 Set
<NameRelationship
> rels
= this.getRelationsToThisName();
672 for (NameRelationship rel
: rels
){
673 if (rel
.getType().isBasionymRelation()){
674 TaxonNameBase basionym
= rel
.getFromName();
675 result
.add(basionym
);
682 * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
683 * The basionym {@link NameRelationship relationship} will be added to <i>this</i> taxon name
684 * and to the basionym. The basionym cannot have itself a basionym.
685 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
686 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
688 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
689 * @see #getBasionym()
690 * @see #addBasionym(TaxonNameBase, String)
692 public void addBasionym(T basionym
){
693 addBasionym(basionym
, null, null, null);
696 * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name
697 * and keeps the nomenclatural rule considered for it. The basionym
698 * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the basionym.
699 * The basionym cannot have itself a basionym.
700 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
701 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
703 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
704 * @param ruleConsidered the string identifying the nomenclatural rule
706 * @see #getBasionym()
707 * @see #addBasionym(TaxonNameBase)
709 public NameRelationship
addBasionym(T basionym
, Reference citation
, String microcitation
, String ruleConsidered
){
710 if (basionym
!= null){
711 return basionym
.addRelationshipToName(this, NameRelationshipType
.BASIONYM(), citation
, microcitation
, ruleConsidered
);
718 * Assigns a taxon name as {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym} of <i>this</i> taxon name
719 * and keeps the nomenclatural rule considered for it. The replaced synonym
720 * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the replaced synonym.
721 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the replaced synonym
722 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
724 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
725 * @param ruleConsidered the string identifying the nomenclatural rule
726 * @see #getBasionym()
727 * @see #addBasionym(TaxonNameBase)
729 //TODO: Check if true: The replaced synonym cannot have itself a replaced synonym (?).
730 public void addReplacedSynonym(T replacedSynonym
, Reference citation
, String microcitation
, String ruleConsidered
){
731 if (replacedSynonym
!= null){
732 replacedSynonym
.addRelationshipToName(this, NameRelationshipType
.REPLACED_SYNONYM(), citation
, microcitation
, ruleConsidered
);
737 * Removes the {@link NameRelationshipType#BASIONYM() basionym} {@link NameRelationship relationship} from the set of
738 * {@link #getRelationsToThisName() name relationships to} <i>this</i> taxon name. The same relationhip will be
739 * removed from the set of {@link #getRelationsFromThisName() name relationships from} the taxon name
740 * previously used as basionym.
742 * @see #getBasionym()
743 * @see #addBasionym(TaxonNameBase)
745 public void removeBasionyms(){
746 Set
<NameRelationship
> removeRelations
= new HashSet
<NameRelationship
>();
747 for (NameRelationship nameRelation
: this.getRelationsToThisName()){
748 if (nameRelation
.getType().isBasionymRelation()){
749 removeRelations
.add(nameRelation
);
752 // Removing relations from a set through which we are iterating causes a
753 // ConcurrentModificationException. Therefore, we delete the targeted
754 // relations in a second step.
755 for (NameRelationship relation
: removeRelations
){
756 this.removeNameRelationship(relation
);
761 * Returns the taxonomic {@link Rank rank} of <i>this</i> taxon name.
765 public Rank
getRank(){
772 public void setRank(Rank rank
){
777 * Returns the {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} of <i>this</i> taxon name.
778 * The nomenclatural reference is here meant to be the one publication
779 * <i>this</i> taxon name was originally published in while fulfilling the formal
780 * requirements as specified by the corresponding {@link NomenclaturalCode nomenclatural code}.
782 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference
783 * @see eu.etaxonomy.cdm.model.reference.Reference
785 public INomenclaturalReference
getNomenclaturalReference(){
786 return this.nomenclaturalReference
;
789 * Assigns a {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} to <i>this</i> taxon name.
790 * The corresponding {@link eu.etaxonomy.cdm.model.reference.Reference.isNomenclaturallyRelevant nomenclaturally relevant flag} will be set to true
791 * as it is obviously used for nomenclatural purposes.
793 * @throws IllegalArgumentException if parameter <code>nomenclaturalReference</code> is not assignable from {@link INomenclaturalReference}
794 * @see #getNomenclaturalReference()
796 public void setNomenclaturalReference(INomenclaturalReference nomenclaturalReference
){
797 if(nomenclaturalReference
!= null){
798 if(!INomenclaturalReference
.class.isAssignableFrom(nomenclaturalReference
.getClass())){
799 throw new IllegalArgumentException("Parameter nomenclaturalReference is not assignable from INomenclaturalReference");
801 this.nomenclaturalReference
= (Reference
)nomenclaturalReference
;
803 this.nomenclaturalReference
= null;
808 * Returns the appended phrase string assigned to <i>this</i> taxon name.
809 * The appended phrase is a non-atomised addition to a name. It is
810 * not ruled by a nomenclatural code.
812 public String
getAppendedPhrase(){
813 return this.appendedPhrase
;
817 * @see #getAppendedPhrase()
819 public void setAppendedPhrase(String appendedPhrase
){
820 this.appendedPhrase
= appendedPhrase
;
824 * Returns the details string of the {@link #getNomenclaturalReference() nomenclatural reference} assigned
825 * to <i>this</i> taxon name. The details describe the exact localisation within
826 * the publication used as nomenclature reference. These are mostly
827 * (implicitly) pages but can also be figures or tables or any other
828 * element of a publication. A nomenclatural micro reference (details)
829 * requires the existence of a nomenclatural reference.
831 //Details of the nomenclatural reference (protologue).
832 public String
getNomenclaturalMicroReference(){
833 return this.nomenclaturalMicroReference
;
836 * @see #getNomenclaturalMicroReference()
838 public void setNomenclaturalMicroReference(String nomenclaturalMicroReference
){
839 this.nomenclaturalMicroReference
= nomenclaturalMicroReference
;
843 * @see eu.etaxonomy.cdm.model.common.IParsable#getHasProblem()
845 public int getParsingProblem(){
846 return this.parsingProblem
;
850 * @see eu.etaxonomy.cdm.model.common.IParsable#setHasProblem(int)
852 public void setParsingProblem(int parsingProblem
){
853 this.parsingProblem
= parsingProblem
;
857 * @see eu.etaxonomy.cdm.model.common.IParsable#addProblem(eu.etaxonomy.cdm.strategy.parser.NameParserWarning)
859 public void addParsingProblem(ParserProblem problem
){
860 parsingProblem
= ParserProblem
.addProblem(parsingProblem
, problem
);
864 * @see eu.etaxonomy.cdm.model.common.IParsable#removeParsingProblem(eu.etaxonomy.cdm.strategy.parser.ParserProblem)
866 public void removeParsingProblem(ParserProblem problem
) {
867 parsingProblem
= ParserProblem
.removeProblem(parsingProblem
, problem
);
873 public void addParsingProblems(int problems
){
874 parsingProblem
= ParserProblem
.addProblems(parsingProblem
, problems
);
878 * @see eu.etaxonomy.cdm.model.common.IParsable#hasProblem()
880 public boolean hasProblem(){
881 return parsingProblem
!= 0;
887 * @see eu.etaxonomy.cdm.model.common.IParsable#hasProblem(eu.etaxonomy.cdm.strategy.parser.ParserProblem)
889 public boolean hasProblem(ParserProblem problem
) {
890 return getParsingProblems().contains(problem
);
895 * @see eu.etaxonomy.cdm.model.common.IParsable#problemStarts()
897 public int getProblemStarts(){
898 return this.problemStarts
;
902 * @see eu.etaxonomy.cdm.model.common.IParsable#setProblemStarts(int)
904 public void setProblemStarts(int start
) {
905 this.problemStarts
= start
;
909 * @see eu.etaxonomy.cdm.model.common.IParsable#problemEnds()
911 public int getProblemEnds(){
912 return this.problemEnds
;
916 * @see eu.etaxonomy.cdm.model.common.IParsable#setProblemEnds(int)
918 public void setProblemEnds(int end
) {
919 this.problemEnds
= end
;
922 //*********************** TYPE DESIGNATION *********************************************//
925 * Returns the set of {@link TypeDesignationBase type designations} assigned
926 * to <i>this</i> taxon name.
927 * @see NameTypeDesignation
928 * @see SpecimenTypeDesignation
930 public Set
<TypeDesignationBase
> getTypeDesignations() {
931 if(typeDesignations
== null) {
932 this.typeDesignations
= new HashSet
<TypeDesignationBase
>();
934 return typeDesignations
;
938 * Removes one element from the set of {@link TypeDesignationBase type designations} assigned to
939 * <i>this</i> taxon name. The type designation itself will be nullified.
941 * @param typeDesignation the type designation which should be deleted
943 @SuppressWarnings("deprecation")
944 public void removeTypeDesignation(TypeDesignationBase typeDesignation
) {
945 this.typeDesignations
.remove(typeDesignation
);
946 typeDesignation
.removeTypifiedName(this);
950 * Returns the set of {@link SpecimenTypeDesignation specimen type designations} assigned
951 * to <i>this</i> taxon name. The {@link Rank rank} of <i>this</i> taxon name is generally
952 * "species" or below. The specimen type designations include all the
953 * specimens on which the typification of this name is based (which are
954 * exclusively used to typify taxon names belonging to the same
955 * {@link HomotypicalGroup homotypical group} to which <i>this</i> taxon name
956 * belongs) and eventually the status of these designations.
958 * @see SpecimenTypeDesignation
959 * @see NameTypeDesignation
960 * @see HomotypicalGroup
963 public Set
<SpecimenTypeDesignation
> getSpecimenTypeDesignationsOfHomotypicalGroup() {
964 return this.getHomotypicalGroup().getSpecimenTypeDesignations();
967 //*********************** NAME TYPE DESIGNATION *********************************************//
970 * Returns the set of {@link NameTypeDesignation name type designations} assigned
971 * to <i>this</i> taxon name the rank of which must be above "species".
972 * The name type designations include all the taxon names used to typify
973 * <i>this</i> taxon name and eventually the rejected or conserved status
974 * of these designations.
976 * @see NameTypeDesignation
977 * @see SpecimenTypeDesignation
980 public Set
<NameTypeDesignation
> getNameTypeDesignations() {
981 Set
<NameTypeDesignation
> result
= new HashSet
<NameTypeDesignation
>();
982 for (TypeDesignationBase typeDesignation
: this.typeDesignations
){
983 if (typeDesignation
instanceof NameTypeDesignation
){
984 result
.add((NameTypeDesignation
)typeDesignation
);
991 * Creates and adds a new {@link NameTypeDesignation name type designation}
992 * to <i>this</i> taxon name's set of type designations.
994 * @param typeSpecies the taxon name to be used as type of <i>this</i> taxon name
995 * @param citation the reference for this new designation
996 * @param citationMicroReference the string with the details (generally pages) within the reference
997 * @param originalNameString the taxon name string used in the reference to assert this designation
998 * @param isRejectedType the boolean status for a rejected name type designation
999 * @param isConservedType the boolean status for a conserved name type designation
1000 * @param isLectoType the boolean status for a lectotype name type designation
1001 * @param isNotDesignated the boolean status for a name type designation without name type
1002 * @param addToAllHomotypicNames the boolean indicating whether the name type designation should be
1003 * added to all taxon names of the homotypical group this taxon name belongs to
1005 * @see #getNameTypeDesignations()
1006 * @see NameTypeDesignation
1007 * @see TypeDesignationBase#isNotDesignated()
1009 public NameTypeDesignation
addNameTypeDesignation(TaxonNameBase typeSpecies
,
1011 String citationMicroReference
,
1012 String originalNameString
,
1013 NameTypeDesignationStatus status
,
1014 boolean isRejectedType
,
1015 boolean isConservedType
,
1016 /*boolean isLectoType, */
1017 boolean isNotDesignated
,
1018 boolean addToAllHomotypicNames
) {
1019 NameTypeDesignation nameTypeDesignation
= new NameTypeDesignation(typeSpecies
, citation
, citationMicroReference
, originalNameString
, status
, isRejectedType
, isConservedType
, isNotDesignated
);
1020 //nameTypeDesignation.setLectoType(isLectoType);
1021 addTypeDesignation(nameTypeDesignation
, addToAllHomotypicNames
);
1022 return nameTypeDesignation
;
1026 * Creates and adds a new {@link NameTypeDesignation name type designation}
1027 * to <i>this</i> taxon name's set of type designations.
1029 * @param typeSpecies the taxon name to be used as type of <i>this</i> taxon name
1030 * @param citation the reference for this new designation
1031 * @param citationMicroReference the string with the details (generally pages) within the reference
1032 * @param originalNameString the taxon name string used in the reference to assert this designation
1033 * @param status the name type designation status
1034 * @param addToAllHomotypicNames the boolean indicating whether the name type designation should be
1035 * added to all taxon names of the homotypical group this taxon name belongs to
1037 * @see #getNameTypeDesignations()
1038 * @see NameTypeDesignation
1039 * @see TypeDesignationBase#isNotDesignated()
1041 public NameTypeDesignation
addNameTypeDesignation(TaxonNameBase typeSpecies
,
1043 String citationMicroReference
,
1044 String originalNameString
,
1045 NameTypeDesignationStatus status
,
1046 boolean addToAllHomotypicNames
) {
1047 NameTypeDesignation nameTypeDesignation
= new NameTypeDesignation(typeSpecies
, status
, citation
, citationMicroReference
, originalNameString
);
1048 addTypeDesignation(nameTypeDesignation
, addToAllHomotypicNames
);
1049 return nameTypeDesignation
;
1052 //*********************** SPECIMEN TYPE DESIGNATION *********************************************//
1055 * Returns the set of {@link SpecimenTypeDesignation specimen type designations}
1056 * that typify <i>this</i> taxon name.
1059 public Set
<SpecimenTypeDesignation
> getSpecimenTypeDesignations() {
1060 Set
<SpecimenTypeDesignation
> result
= new HashSet
<SpecimenTypeDesignation
>();
1061 for (TypeDesignationBase typeDesignation
: this.typeDesignations
){
1062 if (typeDesignation
instanceof SpecimenTypeDesignation
){
1063 result
.add((SpecimenTypeDesignation
)typeDesignation
);
1071 * Creates and adds a new {@link SpecimenTypeDesignation specimen type designation}
1072 * to <i>this</i> taxon name's set of type designations.
1074 * @param typeSpecimen the specimen to be used as a type for <i>this</i> taxon name
1075 * @param status the specimen type designation status
1076 * @param citation the reference for this new specimen type designation
1077 * @param citationMicroReference the string with the details (generally pages) within the reference
1078 * @param originalNameString the taxon name used in the reference to assert this designation
1079 * @param isNotDesignated the boolean status for a specimen type designation without specimen type
1080 * @param addToAllHomotypicNames the boolean indicating whether the specimen type designation should be
1081 * added to all taxon names of the homotypical group the typified
1082 * taxon name belongs to
1084 * @see #getSpecimenTypeDesignations()
1085 * @see SpecimenTypeDesignationStatus
1086 * @see SpecimenTypeDesignation
1087 * @see TypeDesignationBase#isNotDesignated()
1089 public SpecimenTypeDesignation
addSpecimenTypeDesignation(Specimen typeSpecimen
,
1090 SpecimenTypeDesignationStatus status
,
1092 String citationMicroReference
,
1093 String originalNameString
,
1094 boolean isNotDesignated
,
1095 boolean addToAllHomotypicNames
) {
1096 SpecimenTypeDesignation specimenTypeDesignation
= new SpecimenTypeDesignation(typeSpecimen
, status
, citation
, citationMicroReference
, originalNameString
, isNotDesignated
);
1097 addTypeDesignation(specimenTypeDesignation
, addToAllHomotypicNames
);
1098 return specimenTypeDesignation
;
1101 //used by merge strategy
1102 private boolean addTypeDesignation(TypeDesignationBase typeDesignation
){
1103 return addTypeDesignation(typeDesignation
, true);
1107 * Adds a {@link TypeDesignationBase type designation} to <code>this</code> taxon name's set of type designations
1109 * @param typeDesignation the typeDesignation to be added to <code>this</code> taxon name
1110 * @param addToAllNames the boolean indicating whether the type designation should be
1111 * added to all taxon names of the homotypical group the typified
1112 * taxon name belongs to
1113 * @return true if the operation was succesful
1115 * @throws IllegalArgumentException if the type designation already has typified names, an {@link IllegalArgumentException exception}
1116 * is thrown. We do this to prevent a type designation to be used for multiple taxon names.
1119 public boolean addTypeDesignation(TypeDesignationBase typeDesignation
, boolean addToAllNames
){
1120 //currently typeDesignations are not persisted with the homotypical group
1121 //so explicit adding to the homotypical group is not necessary.
1122 if (typeDesignation
!= null){
1123 checkHomotypicalGroup(typeDesignation
);
1124 this.typeDesignations
.add(typeDesignation
);
1125 typeDesignation
.addTypifiedName(this);
1128 for (TaxonNameBase taxonName
: this.getHomotypicalGroup().getTypifiedNames()){
1129 if (taxonName
!= this){
1130 taxonName
.addTypeDesignation(typeDesignation
, false);
1139 * Throws an Exception this type designation already has typified names from another homotypical group.
1140 * @param typeDesignation
1142 private void checkHomotypicalGroup(TypeDesignationBase typeDesignation
) {
1143 if(typeDesignation
.getTypifiedNames().size() > 0){
1144 Set
<HomotypicalGroup
> groups
= new HashSet
<HomotypicalGroup
>();
1145 Set
<TaxonNameBase
> names
= typeDesignation
.getTypifiedNames();
1146 for (TaxonNameBase taxonName
: names
){
1147 groups
.add(taxonName
.getHomotypicalGroup());
1149 if (groups
.size() > 1){
1150 throw new IllegalArgumentException("TypeDesignation already has typified names from another homotypical group.");
1157 //*********************** HOMOTYPICAL GROUP *********************************************//
1161 * Returns the {@link HomotypicalGroup homotypical group} to which
1162 * <i>this</i> taxon name belongs. A homotypical group represents all taxon names
1163 * that share the same types.
1165 * @see HomotypicalGroup
1168 public HomotypicalGroup
getHomotypicalGroup() {
1169 return homotypicalGroup
;
1173 * @see #getHomotypicalGroup()
1175 public void setHomotypicalGroup(HomotypicalGroup homotypicalGroup
) {
1176 if (homotypicalGroup
== null){
1177 throw new IllegalArgumentException("HomotypicalGroup of name should never be null but was set to 'null'");
1179 this.homotypicalGroup
= homotypicalGroup
;
1180 if (! homotypicalGroup
.typifiedNames
.contains(this)){
1181 homotypicalGroup
.addTypifiedName(this);
1187 // *************************************************************************//
1190 * @see #getNomenclaturalReference()
1193 public Reference
getCitation(){
1194 //TODO What is the purpose of this method differing from the getNomenclaturalReference method?
1195 logger
.warn("getCitation not yet implemented");
1200 * Returns the complete string containing the
1201 * {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation() nomenclatural reference citation}
1202 * and the {@link #getNomenclaturalMicroReference() details} assigned to <i>this</i> taxon name.
1204 * @return the string containing the nomenclatural reference of <i>this</i> taxon name
1205 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation()
1206 * @see #getNomenclaturalReference()
1207 * @see #getNomenclaturalMicroReference()
1210 public String
getCitationString(){
1211 return getNomenclaturalReference().getNomenclaturalCitation(getNomenclaturalMicroReference());
1215 * Returns the parsing problems
1218 public List
<ParserProblem
> getParsingProblems(){
1219 return ParserProblem
.warningList(this.parsingProblem
);
1223 * Returns the string containing the publication date (generally only year)
1224 * of the {@link #getNomenclaturalReference() nomenclatural reference} for <i>this</i> taxon name, null if there is
1225 * no nomenclatural reference.
1227 * @return the string containing the publication date of <i>this</i> taxon name
1228 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getYear()
1231 public String
getReferenceYear(){
1232 if (this.getNomenclaturalReference() != null ){
1233 return this.getNomenclaturalReference().getYear();
1240 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1241 * In this context a taxon base means the use of a taxon name by a reference
1242 * either as a {@link eu.etaxonomy.cdm.model.taxon.Taxon taxon} ("accepted/correct" name) or
1243 * as a (junior) {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
1244 * A taxon name can be used by several distinct {@link eu.etaxonomy.cdm.model.reference.Reference references} but only once
1245 * within a taxonomic treatment (identified by one reference).
1248 * @see #getSynonyms()
1250 public Set
<TaxonBase
> getTaxonBases() {
1251 if(taxonBases
== null) {
1252 this.taxonBases
= new HashSet
<TaxonBase
>();
1254 return this.taxonBases
;
1258 * Adds a new {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon base}
1259 * to the set of taxon bases using <i>this</i> taxon name.
1261 * @param taxonBase the taxon base to be added
1262 * @see #getTaxonBases()
1263 * @see #removeTaxonBase(TaxonBase)
1266 public void addTaxonBase(TaxonBase taxonBase
){
1267 Method method
= ReflectionUtils
.findMethod(TaxonBase
.class, "setName", new Class
[] {TaxonNameBase
.class});
1268 ReflectionUtils
.makeAccessible(method
);
1269 ReflectionUtils
.invokeMethod(method
, taxonBase
, new Object
[] {this});
1270 taxonBases
.add(taxonBase
);
1273 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1275 * @param taxonBase the taxon base which should be removed from the corresponding set
1276 * @see #getTaxonBases()
1277 * @see #addTaxonBase(TaxonBase)
1279 public void removeTaxonBase(TaxonBase taxonBase
){
1280 Method method
= ReflectionUtils
.findMethod(TaxonBase
.class, "setName", new Class
[] {TaxonNameBase
.class});
1281 ReflectionUtils
.makeAccessible(method
);
1282 ReflectionUtils
.invokeMethod(method
, taxonBase
, new Object
[] {null});
1283 taxonBases
.remove(taxonBase
);
1287 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Taxon taxa} ("accepted/correct" names according to any
1288 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1289 * the set returned by getTaxonBases().
1291 * @see eu.etaxonomy.cdm.model.taxon.Taxon
1292 * @see #getTaxonBases()
1293 * @see #getSynonyms()
1296 public Set
<Taxon
> getTaxa(){
1297 Set
<Taxon
> result
= new HashSet
<Taxon
>();
1298 for (TaxonBase taxonBase
: this.taxonBases
){
1299 if (taxonBase
instanceof Taxon
){
1300 result
.add((Taxon
)taxonBase
);
1307 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Synonym (junior) synonyms} (according to any
1308 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1309 * the set returned by getTaxonBases().
1311 * @see eu.etaxonomy.cdm.model.taxon.Synonym
1312 * @see #getTaxonBases()
1316 public Set
<Synonym
> getSynonyms() {
1317 Set
<Synonym
> result
= new HashSet
<Synonym
>();
1318 for (TaxonBase taxonBase
: this.taxonBases
){
1319 if (taxonBase
instanceof Synonym
){
1320 result
.add((Synonym
)taxonBase
);
1327 // *********** DESCRIPTIONS *************************************
1330 * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1331 * to <i>this</i> taxon name. A taxon name description is a piece of information
1332 * concerning the taxon name like for instance the content of its first
1333 * publication (protolog) or a picture of this publication.
1335 * @see #addDescription(TaxonNameDescription)
1336 * @see #removeDescription(TaxonNameDescription)
1337 * @see eu.etaxonomy.cdm.model.description.TaxonNameDescription
1339 public Set
<TaxonNameDescription
> getDescriptions() {
1340 return descriptions
;
1344 * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name description}
1345 * to the set of taxon name descriptions assigned to <i>this</i> taxon name. The
1346 * content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute} of the
1347 * taxon name description itself will be replaced with <i>this</i> taxon name.
1349 * @param description the taxon name description to be added
1350 * @see #getDescriptions()
1351 * @see #removeDescription(TaxonNameDescription)
1353 public void addDescription(TaxonNameDescription description
) {
1354 java
.lang
.reflect
.Field field
= ReflectionUtils
.findField(TaxonNameDescription
.class, "taxonName", TaxonNameBase
.class);
1355 ReflectionUtils
.makeAccessible(field
);
1356 ReflectionUtils
.setField(field
, description
, this);
1357 descriptions
.add(description
);
1360 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1361 * to <i>this</i> taxon name. The content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute}
1362 * of the description itself will be set to "null".
1364 * @param description the taxon name description which should be removed
1365 * @see #getDescriptions()
1366 * @see #addDescription(TaxonNameDescription)
1367 * @see eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName()
1369 public void removeDescription(TaxonNameDescription description
) {
1370 java
.lang
.reflect
.Field field
= ReflectionUtils
.findField(TaxonNameDescription
.class, "taxonName", TaxonNameBase
.class);
1371 ReflectionUtils
.makeAccessible(field
);
1372 ReflectionUtils
.setField(field
, description
, null);
1373 descriptions
.remove(description
);
1376 // *********** HOMOTYPIC GROUP METHODS **************************************************
1379 public void mergeHomotypicGroups(TaxonNameBase name
){
1380 this.getHomotypicalGroup().merge(name
.getHomotypicalGroup());
1381 //HomotypicalGroup thatGroup = name.homotypicalGroup;
1382 name
.setHomotypicalGroup(this.homotypicalGroup
);
1386 * Returns the boolean value indicating whether a given taxon name belongs
1387 * to the same {@link HomotypicalGroup homotypical group} as <i>this</i> taxon name (true)
1388 * or not (false). Returns "true" only if the homotypical groups of both
1389 * taxon names exist and if they are identical.
1391 * @param homoTypicName the taxon name the homotypical group of which is to be checked
1392 * @return the boolean value of the check
1393 * @see HomotypicalGroup
1396 public boolean isHomotypic(TaxonNameBase homoTypicName
) {
1397 if (homoTypicName
== null) {
1400 HomotypicalGroup homotypicGroup
= homoTypicName
.getHomotypicalGroup();
1401 if (homotypicGroup
== null || this.getHomotypicalGroup() == null) {
1404 if (homotypicGroup
.equals(this.getHomotypicalGroup())) {
1412 * Checks whether name is a basionym for ALL names
1413 * in its homotypical group.
1414 * Returns <code>false</code> if there are no other names in the group
1419 public boolean isGroupsBasionym() {
1420 Set
<TaxonNameBase
> typifiedNames
= homotypicalGroup
.getTypifiedNames();
1422 // Check whether there are any other names in the group
1423 if (typifiedNames
.size() == 1) {
1427 boolean isBasionymToAll
= true;
1429 for (TaxonNameBase taxonName
: typifiedNames
) {
1430 if (!taxonName
.equals(this)) {
1431 if (! isBasionymFor(taxonName
)) {
1440 * Checks whether a basionym relationship exists between fromName and toName.
1447 public boolean isBasionymFor(TaxonNameBase newCombinationName
) {
1448 Set
<NameRelationship
> relations
= newCombinationName
.getRelationsToThisName();
1449 for (NameRelationship relation
: relations
) {
1450 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) &&
1451 relation
.getFromName().equals(this)) {
1459 * Creates a basionym relationship to all other names in this names homotypical
1462 * @see HomotypicalGroup.setGroupBasionym(TaxonNameBase basionymName)
1466 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup#setGroupBasionym(TaxonNameBase)
1469 public void makeGroupsBasionym() {
1470 this.homotypicalGroup
.setGroupBasionym(this);
1474 //********* Rank comparison shortcuts ********************//
1476 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1477 * taxon name is higher than the genus rank (true) or not (false).
1478 * Suprageneric non viral names are monomials.
1479 * Returns false if rank is null.
1482 * @see #isInfraGeneric()
1484 * @see #isInfraSpecific()
1487 public boolean isSupraGeneric() {
1491 return getRank().isSupraGeneric();
1494 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1495 * taxon name is the genus rank (true) or not (false). Non viral names with
1496 * genus rank are monomials. Returns false if rank is null.
1498 * @see #isSupraGeneric()
1499 * @see #isInfraGeneric()
1501 * @see #isInfraSpecific()
1504 public boolean isGenus() {
1508 return getRank().isGenus();
1511 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1512 * taxon name is higher than the species rank and lower than the
1513 * genus rank (true) or not (false). Infrageneric non viral names are
1514 * binomials. Returns false if rank is null.
1516 * @see #isSupraGeneric()
1519 * @see #isInfraSpecific()
1522 public boolean isInfraGeneric() {
1526 return getRank().isInfraGeneric();
1530 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1531 * taxon name is higher than the species rank (true) or not (false).
1532 * Returns false if rank is null.
1535 * @see #isInfraGeneric()
1537 * @see #isInfraSpecific()
1540 public boolean isSupraSpecific(){
1544 return getRank().isHigher(Rank
.SPECIES());
1548 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1549 * taxon name is the species rank (true) or not (false). Non viral names
1550 * with species rank are binomials.
1551 * Returns false if rank is null.
1553 * @see #isSupraGeneric()
1555 * @see #isInfraGeneric()
1556 * @see #isInfraSpecific()
1559 public boolean isSpecies() {
1563 return getRank().isSpecies();
1566 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1567 * taxon name is lower than the species rank (true) or not (false).
1568 * Infraspecific non viral names are trinomials.
1569 * Returns false if rank is null.
1571 * @see #isSupraGeneric()
1573 * @see #isInfraGeneric()
1577 public boolean isInfraSpecific() {
1581 return getRank().isInfraSpecific();
1586 * Returns null as the {@link NomenclaturalCode nomenclatural code} that governs
1587 * the construction of <i>this</i> taxon name since there is no specific
1588 * nomenclatural code defined. The real implementention takes place in the
1589 * subclasses {@link ViralName ViralName}, {@link BacterialName BacterialName},
1590 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
1591 * {@link ZoologicalName ZoologicalName}. Each taxon name is governed by one
1592 * and only one nomenclatural code.
1595 * @see #isCodeCompliant()
1596 * @see #getHasProblem()
1598 abstract public NomenclaturalCode
getNomenclaturalCode();
1601 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1604 * Generates and returns the string with the scientific name of <i>this</i>
1605 * taxon name (only non viral taxon names can be generated from their
1606 * components). This string may be stored in the inherited
1607 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
1608 * This method overrides the generic and inherited
1609 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() method} from
1610 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
1612 * @return the string with the composed name of this non viral taxon name with authorship (and maybe year)
1613 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1614 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
1617 // public abstract String generateTitle();
1620 * Creates a basionym relationship between this name and
1621 * each name in its homotypic group.
1623 * @param basionymName
1626 public void setAsGroupsBasionym() {
1629 HomotypicalGroup homotypicalGroup
= this.getHomotypicalGroup();
1631 if (homotypicalGroup
== null) {
1635 Set
<NameRelationship
> relations
= new HashSet
<NameRelationship
>();
1636 Set
<NameRelationship
> removeRelations
= new HashSet
<NameRelationship
>();
1638 for(TaxonNameBase
<?
, ?
> typifiedName
: homotypicalGroup
.getTypifiedNames()){
1640 Set
<NameRelationship
> nameRelations
= typifiedName
.getRelationsFromThisName();
1642 for(NameRelationship nameRelation
: nameRelations
){
1643 relations
.add(nameRelation
);
1647 for (NameRelationship relation
: relations
) {
1649 // If this is a basionym relation, and toName is in the homotypical group,
1650 // remove the relationship.
1651 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) &&
1652 relation
.getToName().getHomotypicalGroup().equals(homotypicalGroup
)) {
1653 removeRelations
.add(relation
);
1657 // Removing relations from a set through which we are iterating causes a
1658 // ConcurrentModificationException. Therefore, we delete the targeted
1659 // relations in a second step.
1660 for (NameRelationship relation
: removeRelations
) {
1661 this.removeNameRelationship(relation
);
1665 for (TaxonNameBase
<?
, ?
> name
: homotypicalGroup
.getTypifiedNames()) {
1666 if (!name
.equals(this)) {
1668 // First check whether the relationship already exists
1669 if (!this.isBasionymFor(name
)) {
1672 name
.addRelationshipFromName(this,
1673 NameRelationshipType
.BASIONYM(), null);
1680 * Removes basionym relationship between this name and
1681 * each name in its homotypic group.
1683 * @param basionymName
1686 public void removeAsGroupsBasionym() {
1688 HomotypicalGroup homotypicalGroup
= this.getHomotypicalGroup();
1690 if (homotypicalGroup
== null) {
1694 Set
<NameRelationship
> relations
= new HashSet
<NameRelationship
>();
1695 Set
<NameRelationship
> removeRelations
= new HashSet
<NameRelationship
>();
1697 for(TaxonNameBase
<?
, ?
> typifiedName
: homotypicalGroup
.getTypifiedNames()){
1699 Set
<NameRelationship
> nameRelations
= typifiedName
.getRelationsFromThisName();
1701 for(NameRelationship nameRelation
: nameRelations
){
1702 relations
.add(nameRelation
);
1706 for (NameRelationship relation
: relations
) {
1708 // If this is a basionym relation, and toName is in the homotypical group,
1709 // and fromName is basionymName, remove the relationship.
1710 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) &&
1711 relation
.getFromName().equals(this) &&
1712 relation
.getToName().getHomotypicalGroup().equals(homotypicalGroup
)) {
1713 removeRelations
.add(relation
);
1717 // Removing relations from a set through which we are iterating causes a
1718 // ConcurrentModificationException. Therefore, we delete the targeted
1719 // relations in a second step.
1720 for (NameRelationship relation
: removeRelations
) {
1721 this.removeNameRelationship(relation
);
1725 //*********************** CLONE ********************************************************/
1728 * Clones <i>this</i> taxon name. This is a shortcut that enables to create
1729 * a new instance that differs only slightly from <i>this</i> taxon name by
1730 * modifying only some of the attributes.<BR><BR>
1731 * Usages of this name in a taxon concept are <b>not</b> cloned.<BR>
1732 * <b>The name gets a newly created homotypical group</b><BR>
1733 * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1734 * {@link TaxonNameDescription Name descriptions} are cloned and not reused.<BR>
1735 * {@link TypeDesignationBase Type designations} are cloned and not reused.<BR>
1737 * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1738 * @see java.lang.Object#clone()
1741 public Object
clone() {
1742 TaxonNameBase result
;
1744 result
= (TaxonNameBase
)super.clone();
1746 //taxonBases -> empty
1747 result
.taxonBases
= new HashSet
<TaxonBase
>();
1750 if (! protectedFullTitleCache
){
1751 result
.fullTitleCache
= null;
1755 result
.descriptions
= new HashSet
<TaxonNameDescription
>();
1756 for (TaxonNameDescription taxonNameDescription
: getDescriptions()){
1757 TaxonNameDescription newDescription
= (TaxonNameDescription
)taxonNameDescription
.clone();
1758 result
.descriptions
.add(newDescription
);
1762 result
.status
= new HashSet
<NomenclaturalStatus
>();
1763 for (NomenclaturalStatus nomenclaturalStatus
: getStatus()){
1764 NomenclaturalStatus newStatus
= (NomenclaturalStatus
)nomenclaturalStatus
.clone();
1765 result
.status
.add(newStatus
);
1770 result
.relationsToThisName
= new HashSet
<NameRelationship
>();
1771 for (NameRelationship toRelationship
: getRelationsToThisName()){
1772 NameRelationship newRelationship
= (NameRelationship
)toRelationship
.clone();
1773 newRelationship
.setRelatedTo(result
);
1774 result
.relationsToThisName
.add(newRelationship
);
1778 result
.relationsFromThisName
= new HashSet
<NameRelationship
>();
1779 for (NameRelationship fromRelationship
: getRelationsFromThisName()){
1780 NameRelationship newRelationship
= (NameRelationship
)fromRelationship
.clone();
1781 newRelationship
.setRelatedFrom(result
);
1782 result
.relationsFromThisName
.add(newRelationship
);
1786 result
.typeDesignations
= new HashSet
<TypeDesignationBase
>();
1787 for (TypeDesignationBase typeDesignation
: getTypeDesignations()){
1788 TypeDesignationBase newDesignation
= (TypeDesignationBase
)typeDesignation
.clone();
1789 result
.typeDesignations
.add(newDesignation
);
1790 newDesignation
.addTypifiedName(result
);
1794 //TODO still needs to be discussed
1795 homotypicalGroup
= HomotypicalGroup
.NewInstance();
1796 homotypicalGroup
.addTypifiedName(this);
1798 //no changes to: appendedPharse, nomenclaturalReference,
1799 //nomenclaturalMicroReference, parsingProblem, problemEnds, problemStarts
1800 //protectedFullTitleCache, rank
1802 } catch (CloneNotSupportedException e
) {
1803 logger
.warn("Object does not implement cloneable");
1804 e
.printStackTrace();