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
.Iterator
;
15 import java
.util
.List
;
19 import javax
.persistence
.Column
;
20 import javax
.persistence
.Entity
;
21 import javax
.persistence
.FetchType
;
22 import javax
.persistence
.Inheritance
;
23 import javax
.persistence
.InheritanceType
;
24 import javax
.persistence
.JoinTable
;
25 import javax
.persistence
.ManyToMany
;
26 import javax
.persistence
.ManyToOne
;
27 import javax
.persistence
.OneToMany
;
28 import javax
.persistence
.Transient
;
29 import javax
.validation
.Valid
;
30 import javax
.validation
.constraints
.NotNull
;
31 import javax
.validation
.constraints
.Size
;
32 import javax
.xml
.bind
.annotation
.XmlAccessType
;
33 import javax
.xml
.bind
.annotation
.XmlAccessorType
;
34 import javax
.xml
.bind
.annotation
.XmlAttribute
;
35 import javax
.xml
.bind
.annotation
.XmlElement
;
36 import javax
.xml
.bind
.annotation
.XmlElementWrapper
;
37 import javax
.xml
.bind
.annotation
.XmlIDREF
;
38 import javax
.xml
.bind
.annotation
.XmlRootElement
;
39 import javax
.xml
.bind
.annotation
.XmlSchemaType
;
40 import javax
.xml
.bind
.annotation
.XmlType
;
42 import org
.apache
.log4j
.Logger
;
43 import org
.hibernate
.annotations
.Cascade
;
44 import org
.hibernate
.annotations
.CascadeType
;
45 import org
.hibernate
.annotations
.Index
;
46 import org
.hibernate
.annotations
.Table
;
47 import org
.hibernate
.envers
.Audited
;
48 import org
.hibernate
.search
.annotations
.Field
;
49 import org
.hibernate
.search
.annotations
.IndexedEmbedded
;
50 import org
.hibernate
.validator
.constraints
.NotEmpty
;
51 import org
.springframework
.util
.ReflectionUtils
;
53 import eu
.etaxonomy
.cdm
.model
.common
.IParsable
;
54 import eu
.etaxonomy
.cdm
.model
.common
.IReferencedEntity
;
55 import eu
.etaxonomy
.cdm
.model
.common
.IRelated
;
56 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
57 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
58 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
59 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
60 import eu
.etaxonomy
.cdm
.model
.reference
.INomenclaturalReference
;
61 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
62 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
63 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
64 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
65 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
66 import eu
.etaxonomy
.cdm
.strategy
.cache
.name
.CacheUpdate
;
67 import eu
.etaxonomy
.cdm
.strategy
.cache
.name
.INameCacheStrategy
;
68 import eu
.etaxonomy
.cdm
.strategy
.match
.IMatchable
;
69 import eu
.etaxonomy
.cdm
.strategy
.match
.Match
;
70 import eu
.etaxonomy
.cdm
.strategy
.match
.Match
.ReplaceMode
;
71 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchMode
;
72 import eu
.etaxonomy
.cdm
.strategy
.merge
.Merge
;
73 import eu
.etaxonomy
.cdm
.strategy
.merge
.MergeMode
;
74 import eu
.etaxonomy
.cdm
.strategy
.parser
.ParserProblem
;
75 import eu
.etaxonomy
.cdm
.validation
.Level2
;
78 * The upmost (abstract) class for scientific taxon names regardless of any
79 * particular {@link NomenclaturalCode nomenclature code}. The scientific taxon name does not depend
80 * on the use made of it in a publication or a treatment
81 * ({@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon concept respectively potential taxon})
82 * as an {@link eu.etaxonomy.cdm.model.taxon.Taxon "accepted" respectively "correct" (taxon) name}
83 * or as a {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
85 * This class corresponds partially to: <ul>
86 * <li> TaxonName according to the TDWG ontology
87 * <li> ScientificName and CanonicalName according to the TCS
88 * <li> ScientificName according to the ABCD schema
93 * @created 08-Nov-2007 13:06:57
95 @XmlAccessorType(XmlAccessType
.FIELD
)
96 @XmlType(name
= "TaxonNameBase", propOrder
= {
98 "nomenclaturalMicroReference",
99 "nomenclaturalReference",
102 "protectedFullTitleCache",
105 "relationsFromThisName",
106 "relationsToThisName",
111 @XmlRootElement(name
= "TaxonNameBase")
114 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
115 @Table(appliesTo
="TaxonNameBase", indexes
= { @Index(name
= "taxonNameBaseTitleCacheIndex", columnNames
= { "titleCache" }), @Index(name
= "taxonNameBaseNameCacheIndex", columnNames
= { "nameCache" }) })
116 public abstract class TaxonNameBase
<T
extends TaxonNameBase
<?
,?
>, S
extends INameCacheStrategy
> extends IdentifiableEntity
<S
> implements IParsable
, IRelated
, IMatchable
, Cloneable
{
117 private static final long serialVersionUID
= -4530368639601532116L;
118 private static final Logger logger
= Logger
.getLogger(TaxonNameBase
.class);
120 @XmlElement(name
= "FullTitleCache")
121 @Column(length
=330, name
="fullTitleCache")
122 @Match(value
=MatchMode
.CACHE
, cacheReplaceMode
=ReplaceMode
.ALL
)
123 @CacheUpdate(noUpdate
="titleCache")
124 @NotEmpty(groups
= Level2
.class)
125 @Size(max
= 800) //see #1592
126 protected String fullTitleCache
;
128 //if true titleCache will not be automatically generated/updated
129 @XmlElement(name
= "ProtectedFullTitleCache")
130 @CacheUpdate(value
="fullTitleCache", noUpdate
="titleCache")
131 private boolean protectedFullTitleCache
;
133 @XmlElementWrapper(name
= "Descriptions")
134 @XmlElement(name
= "Description")
135 @OneToMany(mappedBy
="taxonName", fetch
= FetchType
.LAZY
, orphanRemoval
=true)
136 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
138 private Set
<TaxonNameDescription
> descriptions
= new HashSet
<TaxonNameDescription
>();
140 @XmlElement(name
= "AppendedPhrase")
142 @CacheUpdate(value
="nameCache")
146 private String appendedPhrase
;
148 @XmlElement(name
= "NomenclaturalMicroReference")
150 @CacheUpdate(noUpdate
="titleCache")
154 private String nomenclaturalMicroReference
;
157 @CacheUpdate(noUpdate
={"titleCache","fullTitleCache"})
158 private int parsingProblem
= 0;
161 @CacheUpdate(noUpdate
={"titleCache","fullTitleCache"})
162 private int problemStarts
= -1;
165 @CacheUpdate(noUpdate
={"titleCache","fullTitleCache"})
166 private int problemEnds
= -1;
168 @XmlElementWrapper(name
= "TypeDesignations")
169 @XmlElement(name
= "TypeDesignation")
171 @XmlSchemaType(name
= "IDREF")
172 @ManyToMany(fetch
= FetchType
.LAZY
)
174 name
="TaxonNameBase_TypeDesignationBase",
175 joinColumns
=@javax.persistence
.JoinColumn(name
="TaxonNameBase_id"),
176 inverseJoinColumns
=@javax.persistence
.JoinColumn(name
="typedesignations_id")
178 @Cascade({CascadeType
.SAVE_UPDATE
})
180 private Set
<TypeDesignationBase
> typeDesignations
= new HashSet
<TypeDesignationBase
>();
182 @XmlElement(name
= "HomotypicalGroup")
184 @XmlSchemaType(name
= "IDREF")
185 @ManyToOne(fetch
= FetchType
.LAZY
)
186 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
187 @Match(MatchMode
.IGNORE
)
188 @CacheUpdate(noUpdate
="titleCache")
191 private HomotypicalGroup homotypicalGroup
;
193 @XmlElementWrapper(name
= "RelationsFromThisName")
194 @XmlElement(name
= "RelationFromThisName")
195 @OneToMany(mappedBy
="relatedFrom", fetch
= FetchType
.LAZY
, orphanRemoval
=true)
196 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
197 @Merge(MergeMode
.RELATION
)
200 private Set
<NameRelationship
> relationsFromThisName
= new HashSet
<NameRelationship
>();
202 @XmlElementWrapper(name
= "RelationsToThisName")
203 @XmlElement(name
= "RelationToThisName")
205 @XmlSchemaType(name
= "IDREF")
206 @OneToMany(mappedBy
="relatedTo", fetch
= FetchType
.LAZY
, orphanRemoval
=true)
207 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
208 @Merge(MergeMode
.RELATION
)
211 private Set
<NameRelationship
> relationsToThisName
= new HashSet
<NameRelationship
>();
213 @XmlElementWrapper(name
= "NomenclaturalStatuses")
214 @XmlElement(name
= "NomenclaturalStatus")
215 @OneToMany(fetch
= FetchType
.LAZY
, orphanRemoval
=true)
216 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
,CascadeType
.DELETE
})
218 @IndexedEmbedded(depth
=1)
219 private Set
<NomenclaturalStatus
> status
= new HashSet
<NomenclaturalStatus
>();
221 @XmlElementWrapper(name
= "TaxonBases")
222 @XmlElement(name
= "TaxonBase")
224 @XmlSchemaType(name
= "IDREF")
225 @OneToMany(mappedBy
="name", fetch
= FetchType
.LAZY
)
227 @IndexedEmbedded(depth
=1)
228 private Set
<TaxonBase
> taxonBases
= new HashSet
<TaxonBase
>();
230 @XmlElement(name
= "Rank")
232 @XmlSchemaType(name
= "IDREF")
233 @ManyToOne(fetch
= FetchType
.EAGER
)
234 @CacheUpdate(value
="nameCache")
235 //TODO Val #3379, handle maybe as groups = Level2.class ??
237 @IndexedEmbedded(depth
=1)
240 @XmlElement(name
= "NomenclaturalReference")
242 @XmlSchemaType(name
= "IDREF")
243 @ManyToOne(fetch
= FetchType
.LAZY
)
244 @Cascade({CascadeType
.SAVE_UPDATE
})
245 @CacheUpdate(noUpdate
="titleCache")
247 private Reference nomenclaturalReference
;
249 // ************* CONSTRUCTORS *************/
251 * Class constructor: creates a new empty taxon name.
253 * @see #TaxonNameBase(Rank)
254 * @see #TaxonNameBase(HomotypicalGroup)
255 * @see #TaxonNameBase(Rank, HomotypicalGroup)
257 public TaxonNameBase() {
261 * Class constructor: creates a new taxon name
262 * only containing its {@link Rank rank}.
264 * @param rank the rank to be assigned to <i>this</i> taxon name
265 * @see #TaxonNameBase()
266 * @see #TaxonNameBase(HomotypicalGroup)
267 * @see #TaxonNameBase(Rank, HomotypicalGroup)
269 public TaxonNameBase(Rank rank
) {
273 * Class constructor: creates a new taxon name
274 * only containing its {@link HomotypicalGroup homotypical group}.
275 * The new taxon name will be also added to the set of taxon names
276 * belonging to this homotypical group.
278 * @param homotypicalGroup the homotypical group to which <i>this</i> taxon name belongs
279 * @see #TaxonNameBase()
280 * @see #TaxonNameBase(Rank)
281 * @see #TaxonNameBase(Rank, HomotypicalGroup)
283 public TaxonNameBase(HomotypicalGroup homotypicalGroup
) {
284 this(null, homotypicalGroup
);
287 * Class constructor: creates a new taxon name
288 * only containing its {@link Rank rank} and
289 * its {@link HomotypicalGroup homotypical group}.
290 * The new taxon name will be also added to the set of taxon names
291 * belonging to this homotypical group.
293 * @param rank the rank to be assigned to <i>this</i> taxon name
294 * @param homotypicalGroup the homotypical group to which <i>this</i> taxon name belongs
295 * @see #TaxonNameBase()
296 * @see #TaxonNameBase(Rank)
297 * @see #TaxonNameBase(HomotypicalGroup)
299 public TaxonNameBase(Rank rank
, HomotypicalGroup homotypicalGroup
) {
302 if (homotypicalGroup
== null){
303 homotypicalGroup
= new HomotypicalGroup();
305 homotypicalGroup
.addTypifiedName(this);
308 abstract protected Map
<String
, java
.lang
.reflect
.Field
> getAllFields();
310 //********* METHODS **************************************/
313 * Returns the boolean value "false" since the components of <i>this</i> taxon name
314 * cannot follow the rules of a corresponding {@link NomenclaturalCode nomenclatural code}
315 * which is not defined for this class. The nomenclature code depends on
316 * the concrete name subclass ({@link BacterialName BacterialName},
317 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName},
318 * {@link ZoologicalName ZoologicalName} or {@link ViralName ViralName})
319 * to which a taxon name belongs.
324 public abstract boolean isCodeCompliant();
326 public abstract String
generateFullTitle();
331 public List
<TaggedText
> getTaggedName(){
332 return getCacheStrategy().getTaggedTitle(this);
336 public String
getFullTitleCache(){
337 if (protectedFullTitleCache
){
338 return this.fullTitleCache
;
340 if (fullTitleCache
== null ){
341 this.fullTitleCache
= getTruncatedCache(generateFullTitle());
343 return fullTitleCache
;
347 public void setFullTitleCache(String fullTitleCache
){
348 setFullTitleCache(fullTitleCache
, PROTECTED
);
351 public void setFullTitleCache(String fullTitleCache
, boolean protectCache
){
352 fullTitleCache
= getTruncatedCache(fullTitleCache
);
353 this.fullTitleCache
= fullTitleCache
;
354 this.setProtectedFullTitleCache(protectCache
);
358 public boolean isProtectedFullTitleCache() {
359 return protectedFullTitleCache
;
362 public void setProtectedFullTitleCache(boolean protectedFullTitleCache
) {
363 this.protectedFullTitleCache
= protectedFullTitleCache
;
367 * Returns the set of all {@link NameRelationship name relationships}
368 * in which <i>this</i> taxon name is involved. A taxon name can be both source
369 * in some name relationships or target in some others.
371 * @see #getRelationsToThisName()
372 * @see #getRelationsFromThisName()
373 * @see #addNameRelationship(NameRelationship)
374 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
375 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
378 public Set
<NameRelationship
> getNameRelations() {
379 Set
<NameRelationship
> rels
= new HashSet
<NameRelationship
>();
380 rels
.addAll(getRelationsFromThisName());
381 rels
.addAll(getRelationsToThisName());
386 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
387 * and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
388 * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
390 * @param toName the taxon name of the target for this new name relationship
391 * @param type the type of this new name relationship
392 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
393 * @see #getRelationsToThisName()
394 * @see #getNameRelations()
395 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
396 * @see #addNameRelationship(NameRelationship)
398 public void addRelationshipToName(TaxonNameBase toName
, NameRelationshipType type
, String ruleConsidered
){
399 addRelationshipToName(toName
, type
, null, null, ruleConsidered
);
400 // NameRelationship rel = new NameRelationship(toName, this, type, ruleConsidered);
404 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
405 * and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
406 * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
408 * @param toName the taxon name of the target for this new name relationship
409 * @param type the type of this new name relationship
410 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
412 * @see #getRelationsToThisName()
413 * @see #getNameRelations()
414 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
415 * @see #addNameRelationship(NameRelationship)
417 public NameRelationship
addRelationshipToName(TaxonNameBase toName
, NameRelationshipType type
, Reference citation
, String microCitation
, String ruleConsidered
){
419 throw new NullPointerException("Null is not allowed as name for a name relationship");
421 NameRelationship rel
= new NameRelationship(toName
, this, type
, citation
, microCitation
, ruleConsidered
);
426 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
427 * and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
428 * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
430 * @param fromName the taxon name of the source for this new name relationship
431 * @param type the type of this new name relationship
432 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
433 * @param citation the reference in which this relation was described
434 * @param microCitation the reference detail for this relation (e.g. page)
435 * @see #getRelationsFromThisName()
436 * @see #getNameRelations()
437 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
438 * @see #addNameRelationship(NameRelationship)
440 public void addRelationshipFromName(TaxonNameBase fromName
, NameRelationshipType type
, String ruleConsidered
){
441 //fromName.addRelationshipToName(this, type, null, null, ruleConsidered);
442 NameRelationship rel
= this.addRelationshipFromName(fromName
, type
, null, null, ruleConsidered
);
444 // NameRelationship rel = new NameRelationship(this, fromName, type, ruleConsidered);
447 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
448 * and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
449 * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
451 * @param fromName the taxon name of the source for this new name relationship
452 * @param type the type of this new name relationship
453 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
454 * @param citation the reference in which this relation was described
455 * @param microCitation the reference detail for this relation (e.g. page)
456 * @see #getRelationsFromThisName()
457 * @see #getNameRelations()
458 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
459 * @see #addNameRelationship(NameRelationship)
461 public NameRelationship
addRelationshipFromName(TaxonNameBase fromName
, NameRelationshipType type
, Reference citation
, String microCitation
, String ruleConsidered
){
462 return fromName
.addRelationshipToName(this, type
, citation
, microCitation
, ruleConsidered
);
466 * Adds an existing {@link NameRelationship name relationship} either to the set of
467 * {@link #getRelationsToThisName() relations to <i>this</i> taxon name} or to the set of
468 * {@link #getRelationsFromThisName() relations from <i>this</i> taxon name}. If neither the
469 * source nor the target of the name relationship match with <i>this</i> taxon name
470 * no addition will be carried out.
472 * @param rel the name relationship to be added to one of <i>this</i> taxon name's name relationships sets
473 * @see #getNameRelations()
474 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
475 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
477 protected void addNameRelationship(NameRelationship rel
) {
478 if (rel
!=null && rel
.getToName().equals(this)){
479 this.relationsToThisName
.add(rel
);
480 }else if(rel
!=null && rel
.getFromName().equals(this)){
481 this.relationsFromThisName
.add(rel
);
483 throw new RuntimeException("NameRelationship is either null or the relationship does not reference this name");
487 * Removes one {@link NameRelationship name relationship} from one of both sets of
488 * {@link #getNameRelations() name relationships} in which <i>this</i> taxon name is involved.
489 * The name relationship will also be removed from one of both sets belonging
490 * to the second taxon name involved. Furthermore the fromName and toName
491 * attributes of the name relationship object will be nullified.
493 * @param nameRelation the name relationship which should be deleted from one of both sets
494 * @see #getNameRelations()
496 public void removeNameRelationship(NameRelationship nameRelation
) {
498 TaxonNameBase fromName
= nameRelation
.getFromName();
499 TaxonNameBase toName
= nameRelation
.getToName();
501 if (nameRelation
!= null) {
502 nameRelation
.setToName(null);
503 nameRelation
.setFromName(null);
506 if (fromName
!= null) {
507 fromName
.removeNameRelationship(nameRelation
);
510 if (toName
!= null) {
511 toName
.removeNameRelationship(nameRelation
);
514 this.relationsToThisName
.remove(nameRelation
);
515 this.relationsFromThisName
.remove(nameRelation
);
518 public void removeRelationToTaxonName(TaxonNameBase toTaxonName
) {
519 Set
<NameRelationship
> nameRelationships
= new HashSet
<NameRelationship
>();
520 // nameRelationships.addAll(this.getNameRelations());
521 nameRelationships
.addAll(this.getRelationsFromThisName());
522 nameRelationships
.addAll(this.getRelationsToThisName());
523 for(NameRelationship nameRelationship
: nameRelationships
) {
524 // remove name relationship from this side
525 if (nameRelationship
.getFromName().equals(this) && nameRelationship
.getToName().equals(toTaxonName
)) {
526 this.removeNameRelationship(nameRelationship
);
533 * Does exactly the same as the addNameRelationship method provided that
534 * the given relationship is a name relationship.
536 * @param relation the relationship to be added to one of <i>this</i> taxon name's name relationships sets
537 * @see #addNameRelationship(NameRelationship)
538 * @see #getNameRelations()
539 * @see NameRelationship
540 * @see eu.etaxonomy.cdm.model.common.RelationshipBase
543 public void addRelationship(RelationshipBase relation
) {
544 if (relation
instanceof NameRelationship
){
545 addNameRelationship((NameRelationship
)relation
);
546 NameRelationshipType type
= (NameRelationshipType
)relation
.getType();
547 if (type
!= null && ( type
.isBasionymRelation() || type
.isReplacedSynonymRelation() ) ){
548 TaxonNameBase fromName
= ((NameRelationship
)relation
).getFromName();
549 TaxonNameBase toName
= ((NameRelationship
)relation
).getToName();
550 fromName
.mergeHomotypicGroups(toName
);
553 logger
.warn("Relationship not of type NameRelationship!");
554 throw new IllegalArgumentException("Relationship not of type NameRelationship");
560 * Returns the set of all {@link NameRelationship name relationships}
561 * in which <i>this</i> taxon name is involved as a source ("from"-side).
563 * @see #getNameRelations()
564 * @see #getRelationsToThisName()
565 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
567 public Set
<NameRelationship
> getRelationsFromThisName() {
568 if(relationsFromThisName
== null) {
569 this.relationsFromThisName
= new HashSet
<NameRelationship
>();
571 return relationsFromThisName
;
575 * Returns the set of all {@link NameRelationship name relationships}
576 * in which <i>this</i> taxon name is involved as a target ("to"-side).
578 * @see #getNameRelations()
579 * @see #getRelationsFromThisName()
580 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
582 public Set
<NameRelationship
> getRelationsToThisName() {
583 if(relationsToThisName
== null) {
584 this.relationsToThisName
= new HashSet
<NameRelationship
>();
586 return relationsToThisName
;
590 * Returns the set of {@link NomenclaturalStatus nomenclatural status} assigned
591 * to <i>this</i> taxon name according to its corresponding nomenclature code.
592 * This includes the {@link NomenclaturalStatusType type} of the nomenclatural status
593 * and the nomenclatural code rule considered.
595 * @see NomenclaturalStatus
596 * @see NomenclaturalStatusType
598 public Set
<NomenclaturalStatus
> getStatus() {
600 this.status
= new HashSet
<NomenclaturalStatus
>();
606 * Adds a new {@link NomenclaturalStatus nomenclatural status}
607 * to <i>this</i> taxon name's set of nomenclatural status.
609 * @param nomStatus the nomenclatural status to be added
612 public void addStatus(NomenclaturalStatus nomStatus
) {
613 this.status
.add(nomStatus
);
617 * Removes one element from the set of nomenclatural status of <i>this</i> taxon name.
618 * Type and ruleConsidered attributes of the nomenclatural status object
621 * @param nomStatus the nomenclatural status of <i>this</i> taxon name which should be deleted
624 public void removeStatus(NomenclaturalStatus nomStatus
) {
625 //TODO to be implemented?
626 logger
.warn("not yet fully implemented?");
627 this.status
.remove(nomStatus
);
632 * Indicates whether <i>this</i> taxon name is a {@link NameRelationshipType#BASIONYM() basionym}
633 * or a {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym}
634 * of any other taxon name. Returns "true", if a basionym or a replaced
635 * synonym {@link NameRelationship relationship} from <i>this</i> taxon name to another taxon name exists,
636 * false otherwise (also in case <i>this</i> taxon name is the only one in the
637 * homotypical group).
640 public boolean isOriginalCombination(){
641 Set
<NameRelationship
> relationsFromThisName
= this.getRelationsFromThisName();
642 for (NameRelationship relation
: relationsFromThisName
) {
643 if (relation
.getType().isBasionymRelation() ||
644 relation
.getType().isReplacedSynonymRelation()) {
652 * Indicates <i>this</i> taxon name is a {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym}
653 * of any other taxon name. Returns "true", if a replaced
654 * synonym {@link NameRelationship relationship} from <i>this</i> taxon name to another taxon name exists,
655 * false otherwise (also in case <i>this</i> taxon name is the only one in the
656 * homotypical group).
659 public boolean isReplacedSynonym(){
660 Set
<NameRelationship
> relationsFromThisName
= this.getRelationsFromThisName();
661 for (NameRelationship relation
: relationsFromThisName
) {
662 if (relation
.getType().isReplacedSynonymRelation()) {
670 * Returns the taxon name which is the {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
671 * The basionym of a taxon name is its epithet-bringing synonym.
672 * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
673 * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
674 * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
676 * If more than one basionym exists one is choosen at radom.
678 * If no basionym exists null is returned.
681 public TaxonNameBase
getBasionym(){
682 Set
<TaxonNameBase
> basionyms
= getBasionyms();
683 if (basionyms
.size() == 0){
686 return basionyms
.iterator().next();
691 * Returns the set of taxon names which are the {@link NameRelationshipType#BASIONYM() basionyms} of <i>this</i> taxon name.
692 * The basionym of a taxon name is its epithet-bringing synonym.
693 * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
694 * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
695 * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
698 public Set
<TaxonNameBase
> getBasionyms(){
699 Set
<TaxonNameBase
> result
= new HashSet
<TaxonNameBase
>();
700 Set
<NameRelationship
> rels
= this.getRelationsToThisName();
701 for (NameRelationship rel
: rels
){
702 if (rel
.getType().isBasionymRelation()){
703 TaxonNameBase basionym
= rel
.getFromName();
704 result
.add(basionym
);
711 * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
712 * The basionym {@link NameRelationship relationship} will be added to <i>this</i> taxon name
713 * and to the basionym. The basionym cannot have itself a basionym.
714 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
715 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
717 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
718 * @see #getBasionym()
719 * @see #addBasionym(TaxonNameBase, String)
721 public void addBasionym(T basionym
){
722 addBasionym(basionym
, null, null, null);
725 * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name
726 * and keeps the nomenclatural rule considered for it. The basionym
727 * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the basionym.
728 * The basionym cannot have itself a basionym.
729 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
730 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
732 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
733 * @param ruleConsidered the string identifying the nomenclatural rule
735 * @see #getBasionym()
736 * @see #addBasionym(TaxonNameBase)
738 public NameRelationship
addBasionym(T basionym
, Reference citation
, String microcitation
, String ruleConsidered
){
739 if (basionym
!= null){
740 return basionym
.addRelationshipToName(this, NameRelationshipType
.BASIONYM(), citation
, microcitation
, ruleConsidered
);
747 * Returns the set of taxon names which are the {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonyms} of <i>this</i> taxon name.
751 public Set
<TaxonNameBase
> getReplacedSynonyms(){
752 Set
<TaxonNameBase
> result
= new HashSet
<TaxonNameBase
>();
753 Set
<NameRelationship
> rels
= this.getRelationsToThisName();
754 for (NameRelationship rel
: rels
){
755 if (rel
.getType().isReplacedSynonymRelation()){
756 TaxonNameBase replacedSynonym
= rel
.getFromName();
757 result
.add(replacedSynonym
);
764 * Assigns a taxon name as {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym} of <i>this</i> taxon name
765 * and keeps the nomenclatural rule considered for it. The replaced synonym
766 * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the replaced synonym.
767 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the replaced synonym
768 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
770 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
771 * @param ruleConsidered the string identifying the nomenclatural rule
772 * @see #getBasionym()
773 * @see #addBasionym(TaxonNameBase)
775 //TODO: Check if true: The replaced synonym cannot have itself a replaced synonym (?).
776 public void addReplacedSynonym(T replacedSynonym
, Reference citation
, String microcitation
, String ruleConsidered
){
777 if (replacedSynonym
!= null){
778 replacedSynonym
.addRelationshipToName(this, NameRelationshipType
.REPLACED_SYNONYM(), citation
, microcitation
, ruleConsidered
);
783 * Removes the {@link NameRelationshipType#BASIONYM() basionym} {@link NameRelationship relationship} from the set of
784 * {@link #getRelationsToThisName() name relationships to} <i>this</i> taxon name. The same relationhip will be
785 * removed from the set of {@link #getRelationsFromThisName() name relationships from} the taxon name
786 * previously used as basionym.
788 * @see #getBasionym()
789 * @see #addBasionym(TaxonNameBase)
791 public void removeBasionyms(){
792 Set
<NameRelationship
> removeRelations
= new HashSet
<NameRelationship
>();
793 for (NameRelationship nameRelation
: this.getRelationsToThisName()){
794 if (nameRelation
.getType().isBasionymRelation()){
795 removeRelations
.add(nameRelation
);
798 // Removing relations from a set through which we are iterating causes a
799 // ConcurrentModificationException. Therefore, we delete the targeted
800 // relations in a second step.
801 for (NameRelationship relation
: removeRelations
){
802 this.removeNameRelationship(relation
);
807 * Returns the taxonomic {@link Rank rank} of <i>this</i> taxon name.
811 public Rank
getRank(){
818 public void setRank(Rank rank
){
823 * Returns the {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} of <i>this</i> taxon name.
824 * The nomenclatural reference is here meant to be the one publication
825 * <i>this</i> taxon name was originally published in while fulfilling the formal
826 * requirements as specified by the corresponding {@link NomenclaturalCode nomenclatural code}.
828 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference
829 * @see eu.etaxonomy.cdm.model.reference.Reference
831 public INomenclaturalReference
getNomenclaturalReference(){
832 return this.nomenclaturalReference
;
835 * Assigns a {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} to <i>this</i> taxon name.
836 * The corresponding {@link eu.etaxonomy.cdm.model.reference.Reference.isNomenclaturallyRelevant nomenclaturally relevant flag} will be set to true
837 * as it is obviously used for nomenclatural purposes.
839 * @throws IllegalArgumentException if parameter <code>nomenclaturalReference</code> is not assignable from {@link INomenclaturalReference}
840 * @see #getNomenclaturalReference()
842 public void setNomenclaturalReference(INomenclaturalReference nomenclaturalReference
){
843 if(nomenclaturalReference
!= null){
844 if(!INomenclaturalReference
.class.isAssignableFrom(nomenclaturalReference
.getClass())){
845 throw new IllegalArgumentException("Parameter nomenclaturalReference is not assignable from INomenclaturalReference");
847 this.nomenclaturalReference
= (Reference
)nomenclaturalReference
;
849 this.nomenclaturalReference
= null;
854 * Returns the appended phrase string assigned to <i>this</i> taxon name.
855 * The appended phrase is a non-atomised addition to a name. It is
856 * not ruled by a nomenclatural code.
858 public String
getAppendedPhrase(){
859 return this.appendedPhrase
;
863 * @see #getAppendedPhrase()
865 public void setAppendedPhrase(String appendedPhrase
){
866 this.appendedPhrase
= appendedPhrase
;
870 * Returns the details string of the {@link #getNomenclaturalReference() nomenclatural reference} assigned
871 * to <i>this</i> taxon name. The details describe the exact localisation within
872 * the publication used as nomenclature reference. These are mostly
873 * (implicitly) pages but can also be figures or tables or any other
874 * element of a publication. A nomenclatural micro reference (details)
875 * requires the existence of a nomenclatural reference.
877 //Details of the nomenclatural reference (protologue).
878 public String
getNomenclaturalMicroReference(){
879 return this.nomenclaturalMicroReference
;
882 * @see #getNomenclaturalMicroReference()
884 public void setNomenclaturalMicroReference(String nomenclaturalMicroReference
){
885 this.nomenclaturalMicroReference
= nomenclaturalMicroReference
;
889 * @see eu.etaxonomy.cdm.model.common.IParsable#getHasProblem()
892 public int getParsingProblem(){
893 return this.parsingProblem
;
897 * @see eu.etaxonomy.cdm.model.common.IParsable#setHasProblem(int)
900 public void setParsingProblem(int parsingProblem
){
901 this.parsingProblem
= parsingProblem
;
905 * @see eu.etaxonomy.cdm.model.common.IParsable#addProblem(eu.etaxonomy.cdm.strategy.parser.NameParserWarning)
908 public void addParsingProblem(ParserProblem problem
){
909 parsingProblem
= ParserProblem
.addProblem(parsingProblem
, problem
);
913 * @see eu.etaxonomy.cdm.model.common.IParsable#removeParsingProblem(eu.etaxonomy.cdm.strategy.parser.ParserProblem)
916 public void removeParsingProblem(ParserProblem problem
) {
917 parsingProblem
= ParserProblem
.removeProblem(parsingProblem
, problem
);
923 public void addParsingProblems(int problems
){
924 parsingProblem
= ParserProblem
.addProblems(parsingProblem
, problems
);
928 * @see eu.etaxonomy.cdm.model.common.IParsable#hasProblem()
931 public boolean hasProblem(){
932 return parsingProblem
!= 0;
938 * @see eu.etaxonomy.cdm.model.common.IParsable#hasProblem(eu.etaxonomy.cdm.strategy.parser.ParserProblem)
941 public boolean hasProblem(ParserProblem problem
) {
942 return getParsingProblems().contains(problem
);
947 * @see eu.etaxonomy.cdm.model.common.IParsable#problemStarts()
950 public int getProblemStarts(){
951 return this.problemStarts
;
955 * @see eu.etaxonomy.cdm.model.common.IParsable#setProblemStarts(int)
958 public void setProblemStarts(int start
) {
959 this.problemStarts
= start
;
963 * @see eu.etaxonomy.cdm.model.common.IParsable#problemEnds()
966 public int getProblemEnds(){
967 return this.problemEnds
;
971 * @see eu.etaxonomy.cdm.model.common.IParsable#setProblemEnds(int)
974 public void setProblemEnds(int end
) {
975 this.problemEnds
= end
;
978 //*********************** TYPE DESIGNATION *********************************************//
981 * Returns the set of {@link TypeDesignationBase type designations} assigned
982 * to <i>this</i> taxon name.
983 * @see NameTypeDesignation
984 * @see SpecimenTypeDesignation
986 public Set
<TypeDesignationBase
> getTypeDesignations() {
987 if(typeDesignations
== null) {
988 this.typeDesignations
= new HashSet
<TypeDesignationBase
>();
990 return typeDesignations
;
994 * Removes one element from the set of {@link TypeDesignationBase type designations} assigned to
995 * <i>this</i> taxon name. The type designation itself will be nullified.
997 * @param typeDesignation the type designation which should be deleted
999 @SuppressWarnings("deprecation")
1000 public void removeTypeDesignation(TypeDesignationBase typeDesignation
) {
1001 this.typeDesignations
.remove(typeDesignation
);
1002 typeDesignation
.removeTypifiedName(this);
1006 * Returns the set of {@link SpecimenTypeDesignation specimen type designations} assigned
1007 * to <i>this</i> taxon name. The {@link Rank rank} of <i>this</i> taxon name is generally
1008 * "species" or below. The specimen type designations include all the
1009 * specimens on which the typification of this name is based (which are
1010 * exclusively used to typify taxon names belonging to the same
1011 * {@link HomotypicalGroup homotypical group} to which <i>this</i> taxon name
1012 * belongs) and eventually the status of these designations.
1014 * @see SpecimenTypeDesignation
1015 * @see NameTypeDesignation
1016 * @see HomotypicalGroup
1019 public Set
<SpecimenTypeDesignation
> getSpecimenTypeDesignationsOfHomotypicalGroup() {
1020 return this.getHomotypicalGroup().getSpecimenTypeDesignations();
1023 //*********************** NAME TYPE DESIGNATION *********************************************//
1026 * Returns the set of {@link NameTypeDesignation name type designations} assigned
1027 * to <i>this</i> taxon name the rank of which must be above "species".
1028 * The name type designations include all the taxon names used to typify
1029 * <i>this</i> taxon name and eventually the rejected or conserved status
1030 * of these designations.
1032 * @see NameTypeDesignation
1033 * @see SpecimenTypeDesignation
1036 public Set
<NameTypeDesignation
> getNameTypeDesignations() {
1037 Set
<NameTypeDesignation
> result
= new HashSet
<NameTypeDesignation
>();
1038 for (TypeDesignationBase typeDesignation
: this.typeDesignations
){
1039 if (typeDesignation
instanceof NameTypeDesignation
){
1040 result
.add((NameTypeDesignation
)typeDesignation
);
1047 * Creates and adds a new {@link NameTypeDesignation name type designation}
1048 * to <i>this</i> taxon name's set of type designations.
1050 * @param typeSpecies the taxon name to be used as type of <i>this</i> taxon name
1051 * @param citation the reference for this new designation
1052 * @param citationMicroReference the string with the details (generally pages) within the reference
1053 * @param originalNameString the taxon name string used in the reference to assert this designation
1054 * @param isRejectedType the boolean status for a rejected name type designation
1055 * @param isConservedType the boolean status for a conserved name type designation
1056 * @param isLectoType the boolean status for a lectotype name type designation
1057 * @param isNotDesignated the boolean status for a name type designation without name type
1058 * @param addToAllHomotypicNames the boolean indicating whether the name type designation should be
1059 * added to all taxon names of the homotypical group this taxon name belongs to
1061 * @see #getNameTypeDesignations()
1062 * @see NameTypeDesignation
1063 * @see TypeDesignationBase#isNotDesignated()
1065 public NameTypeDesignation
addNameTypeDesignation(TaxonNameBase typeSpecies
,
1067 String citationMicroReference
,
1068 String originalNameString
,
1069 NameTypeDesignationStatus status
,
1070 boolean isRejectedType
,
1071 boolean isConservedType
,
1072 /*boolean isLectoType, */
1073 boolean isNotDesignated
,
1074 boolean addToAllHomotypicNames
) {
1075 NameTypeDesignation nameTypeDesignation
= new NameTypeDesignation(typeSpecies
, citation
, citationMicroReference
, originalNameString
, status
, isRejectedType
, isConservedType
, isNotDesignated
);
1076 //nameTypeDesignation.setLectoType(isLectoType);
1077 addTypeDesignation(nameTypeDesignation
, addToAllHomotypicNames
);
1078 return nameTypeDesignation
;
1082 * Creates and adds a new {@link NameTypeDesignation name type designation}
1083 * to <i>this</i> taxon name's set of type designations.
1085 * @param typeSpecies the taxon name to be used as type of <i>this</i> taxon name
1086 * @param citation the reference for this new designation
1087 * @param citationMicroReference the string with the details (generally pages) within the reference
1088 * @param originalNameString the taxon name string used in the reference to assert this designation
1089 * @param status the name type designation status
1090 * @param addToAllHomotypicNames the boolean indicating whether the name type designation should be
1091 * added to all taxon names of the homotypical group this taxon name belongs to
1093 * @see #getNameTypeDesignations()
1094 * @see NameTypeDesignation
1095 * @see TypeDesignationBase#isNotDesignated()
1097 public NameTypeDesignation
addNameTypeDesignation(TaxonNameBase typeSpecies
,
1099 String citationMicroReference
,
1100 String originalNameString
,
1101 NameTypeDesignationStatus status
,
1102 boolean addToAllHomotypicNames
) {
1103 NameTypeDesignation nameTypeDesignation
= new NameTypeDesignation(typeSpecies
, status
, citation
, citationMicroReference
, originalNameString
);
1104 addTypeDesignation(nameTypeDesignation
, addToAllHomotypicNames
);
1105 return nameTypeDesignation
;
1108 //*********************** SPECIMEN TYPE DESIGNATION *********************************************//
1111 * Returns the set of {@link SpecimenTypeDesignation specimen type designations}
1112 * that typify <i>this</i> taxon name.
1115 public Set
<SpecimenTypeDesignation
> getSpecimenTypeDesignations() {
1116 Set
<SpecimenTypeDesignation
> result
= new HashSet
<SpecimenTypeDesignation
>();
1117 for (TypeDesignationBase typeDesignation
: this.typeDesignations
){
1118 if (typeDesignation
instanceof SpecimenTypeDesignation
){
1119 result
.add((SpecimenTypeDesignation
)typeDesignation
);
1127 * Creates and adds a new {@link SpecimenTypeDesignation specimen type designation}
1128 * to <i>this</i> taxon name's set of type designations.
1130 * @param typeSpecimen the specimen to be used as a type for <i>this</i> taxon name
1131 * @param status the specimen type designation status
1132 * @param citation the reference for this new specimen type designation
1133 * @param citationMicroReference the string with the details (generally pages) within the reference
1134 * @param originalNameString the taxon name used in the reference to assert this designation
1135 * @param isNotDesignated the boolean status for a specimen type designation without specimen type
1136 * @param addToAllHomotypicNames the boolean indicating whether the specimen type designation should be
1137 * added to all taxon names of the homotypical group the typified
1138 * taxon name belongs to
1140 * @see #getSpecimenTypeDesignations()
1141 * @see SpecimenTypeDesignationStatus
1142 * @see SpecimenTypeDesignation
1143 * @see TypeDesignationBase#isNotDesignated()
1145 public SpecimenTypeDesignation
addSpecimenTypeDesignation(DerivedUnit typeSpecimen
,
1146 SpecimenTypeDesignationStatus status
,
1147 Reference
<?
> citation
,
1148 String citationMicroReference
,
1149 String originalNameString
,
1150 boolean isNotDesignated
,
1151 boolean addToAllHomotypicNames
) {
1152 SpecimenTypeDesignation specimenTypeDesignation
= new SpecimenTypeDesignation(typeSpecimen
, status
, citation
, citationMicroReference
, originalNameString
, isNotDesignated
);
1153 addTypeDesignation(specimenTypeDesignation
, addToAllHomotypicNames
);
1154 return specimenTypeDesignation
;
1157 //used by merge strategy
1158 private boolean addTypeDesignation(TypeDesignationBase typeDesignation
){
1159 return addTypeDesignation(typeDesignation
, true);
1163 * Adds a {@link TypeDesignationBase type designation} to <code>this</code> taxon name's set of type designations
1165 * @param typeDesignation the typeDesignation to be added to <code>this</code> taxon name
1166 * @param addToAllNames the boolean indicating whether the type designation should be
1167 * added to all taxon names of the homotypical group the typified
1168 * taxon name belongs to
1169 * @return true if the operation was succesful
1171 * @throws IllegalArgumentException if the type designation already has typified names, an {@link IllegalArgumentException exception}
1172 * is thrown. We do this to prevent a type designation to be used for multiple taxon names.
1175 public boolean addTypeDesignation(TypeDesignationBase typeDesignation
, boolean addToAllNames
){
1176 //currently typeDesignations are not persisted with the homotypical group
1177 //so explicit adding to the homotypical group is not necessary.
1178 if (typeDesignation
!= null){
1179 checkHomotypicalGroup(typeDesignation
);
1180 this.typeDesignations
.add(typeDesignation
);
1181 typeDesignation
.addTypifiedName(this);
1184 for (TaxonNameBase taxonName
: this.getHomotypicalGroup().getTypifiedNames()){
1185 if (taxonName
!= this){
1186 taxonName
.addTypeDesignation(typeDesignation
, false);
1195 * Throws an Exception this type designation already has typified names from another homotypical group.
1196 * @param typeDesignation
1198 private void checkHomotypicalGroup(TypeDesignationBase typeDesignation
) {
1199 if(typeDesignation
.getTypifiedNames().size() > 0){
1200 Set
<HomotypicalGroup
> groups
= new HashSet
<HomotypicalGroup
>();
1201 Set
<TaxonNameBase
> names
= typeDesignation
.getTypifiedNames();
1202 for (TaxonNameBase taxonName
: names
){
1203 groups
.add(taxonName
.getHomotypicalGroup());
1205 if (groups
.size() > 1){
1206 throw new IllegalArgumentException("TypeDesignation already has typified names from another homotypical group.");
1213 //*********************** HOMOTYPICAL GROUP *********************************************//
1217 * Returns the {@link HomotypicalGroup homotypical group} to which
1218 * <i>this</i> taxon name belongs. A homotypical group represents all taxon names
1219 * that share the same types.
1221 * @see HomotypicalGroup
1224 public HomotypicalGroup
getHomotypicalGroup() {
1225 return homotypicalGroup
;
1229 * @see #getHomotypicalGroup()
1231 public void setHomotypicalGroup(HomotypicalGroup homotypicalGroup
) {
1232 if (homotypicalGroup
== null){
1233 throw new IllegalArgumentException("HomotypicalGroup of name should never be null but was set to 'null'");
1235 this.homotypicalGroup
= homotypicalGroup
;
1236 if (! homotypicalGroup
.typifiedNames
.contains(this)){
1237 homotypicalGroup
.addTypifiedName(this);
1243 // *************************************************************************//
1246 * Returns the complete string containing the
1247 * {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation() nomenclatural reference citation}
1248 * and the {@link #getNomenclaturalMicroReference() details} assigned to <i>this</i> taxon name.
1250 * @return the string containing the nomenclatural reference of <i>this</i> taxon name
1251 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation()
1252 * @see #getNomenclaturalReference()
1253 * @see #getNomenclaturalMicroReference()
1256 public String
getCitationString(){
1257 return getNomenclaturalReference().getNomenclaturalCitation(getNomenclaturalMicroReference());
1261 * Returns the parsing problems
1265 public List
<ParserProblem
> getParsingProblems(){
1266 return ParserProblem
.warningList(this.parsingProblem
);
1270 * Returns the string containing the publication date (generally only year)
1271 * of the {@link #getNomenclaturalReference() nomenclatural reference} for <i>this</i> taxon name, null if there is
1272 * no nomenclatural reference.
1274 * @return the string containing the publication date of <i>this</i> taxon name
1275 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getYear()
1278 public String
getReferenceYear(){
1279 if (this.getNomenclaturalReference() != null ){
1280 return this.getNomenclaturalReference().getYear();
1287 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1288 * In this context a taxon base means the use of a taxon name by a reference
1289 * either as a {@link eu.etaxonomy.cdm.model.taxon.Taxon taxon} ("accepted/correct" name) or
1290 * as a (junior) {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
1291 * A taxon name can be used by several distinct {@link eu.etaxonomy.cdm.model.reference.Reference references} but only once
1292 * within a taxonomic treatment (identified by one reference).
1295 * @see #getSynonyms()
1297 public Set
<TaxonBase
> getTaxonBases() {
1298 if(taxonBases
== null) {
1299 this.taxonBases
= new HashSet
<TaxonBase
>();
1301 return this.taxonBases
;
1305 * Adds a new {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon base}
1306 * to the set of taxon bases using <i>this</i> taxon name.
1308 * @param taxonBase the taxon base to be added
1309 * @see #getTaxonBases()
1310 * @see #removeTaxonBase(TaxonBase)
1313 public void addTaxonBase(TaxonBase taxonBase
){
1314 Method method
= ReflectionUtils
.findMethod(TaxonBase
.class, "setName", new Class
[] {TaxonNameBase
.class});
1315 ReflectionUtils
.makeAccessible(method
);
1316 ReflectionUtils
.invokeMethod(method
, taxonBase
, new Object
[] {this});
1317 taxonBases
.add(taxonBase
);
1320 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1322 * @param taxonBase the taxon base which should be removed from the corresponding set
1323 * @see #getTaxonBases()
1324 * @see #addTaxonBase(TaxonBase)
1326 public void removeTaxonBase(TaxonBase taxonBase
){
1327 Method method
= ReflectionUtils
.findMethod(TaxonBase
.class, "setName", new Class
[] {TaxonNameBase
.class});
1328 ReflectionUtils
.makeAccessible(method
);
1329 ReflectionUtils
.invokeMethod(method
, taxonBase
, new Object
[] {null});
1330 boolean removed
= false;
1332 if (taxonBases
.contains(taxonBase
)){
1333 removed
= taxonBases
.remove(taxonBase
);
1336 if (!removed
&& !taxonBases
.isEmpty()){
1337 HashSet
<TaxonBase
> copyTaxonBase
= new HashSet
<TaxonBase
>();
1338 Iterator
<TaxonBase
> iterator
= taxonBases
.iterator();
1339 while (iterator
.hasNext()){
1340 TaxonBase taxonBaseTest
= iterator
.next();
1341 if (taxonBaseTest
.equals(taxonBase
)){
1342 removed
= taxonBases
.remove(taxonBaseTest
);
1358 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Taxon taxa} ("accepted/correct" names according to any
1359 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1360 * the set returned by getTaxonBases().
1362 * @see eu.etaxonomy.cdm.model.taxon.Taxon
1363 * @see #getTaxonBases()
1364 * @see #getSynonyms()
1367 public Set
<Taxon
> getTaxa(){
1368 Set
<Taxon
> result
= new HashSet
<Taxon
>();
1369 for (TaxonBase taxonBase
: this.taxonBases
){
1370 if (taxonBase
instanceof Taxon
){
1371 result
.add((Taxon
)taxonBase
);
1378 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Synonym (junior) synonyms} (according to any
1379 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1380 * the set returned by getTaxonBases().
1382 * @see eu.etaxonomy.cdm.model.taxon.Synonym
1383 * @see #getTaxonBases()
1387 public Set
<Synonym
> getSynonyms() {
1388 Set
<Synonym
> result
= new HashSet
<Synonym
>();
1389 for (TaxonBase taxonBase
: this.taxonBases
){
1390 if (taxonBase
instanceof Synonym
){
1391 result
.add((Synonym
)taxonBase
);
1398 // *********** DESCRIPTIONS *************************************
1401 * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1402 * to <i>this</i> taxon name. A taxon name description is a piece of information
1403 * concerning the taxon name like for instance the content of its first
1404 * publication (protolog) or a picture of this publication.
1406 * @see #addDescription(TaxonNameDescription)
1407 * @see #removeDescription(TaxonNameDescription)
1408 * @see eu.etaxonomy.cdm.model.description.TaxonNameDescription
1410 public Set
<TaxonNameDescription
> getDescriptions() {
1411 return descriptions
;
1415 * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name description}
1416 * to the set of taxon name descriptions assigned to <i>this</i> taxon name. The
1417 * content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute} of the
1418 * taxon name description itself will be replaced with <i>this</i> taxon name.
1420 * @param description the taxon name description to be added
1421 * @see #getDescriptions()
1422 * @see #removeDescription(TaxonNameDescription)
1424 public void addDescription(TaxonNameDescription description
) {
1425 java
.lang
.reflect
.Field field
= ReflectionUtils
.findField(TaxonNameDescription
.class, "taxonName", TaxonNameBase
.class);
1426 ReflectionUtils
.makeAccessible(field
);
1427 ReflectionUtils
.setField(field
, description
, this);
1428 descriptions
.add(description
);
1431 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1432 * to <i>this</i> taxon name. The content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute}
1433 * of the description itself will be set to "null".
1435 * @param description the taxon name description which should be removed
1436 * @see #getDescriptions()
1437 * @see #addDescription(TaxonNameDescription)
1438 * @see eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName()
1440 public void removeDescription(TaxonNameDescription description
) {
1441 java
.lang
.reflect
.Field field
= ReflectionUtils
.findField(TaxonNameDescription
.class, "taxonName", TaxonNameBase
.class);
1442 ReflectionUtils
.makeAccessible(field
);
1443 ReflectionUtils
.setField(field
, description
, null);
1444 descriptions
.remove(description
);
1447 // *********** HOMOTYPIC GROUP METHODS **************************************************
1450 public void mergeHomotypicGroups(TaxonNameBase name
){
1451 this.getHomotypicalGroup().merge(name
.getHomotypicalGroup());
1452 //HomotypicalGroup thatGroup = name.homotypicalGroup;
1453 name
.setHomotypicalGroup(this.homotypicalGroup
);
1457 * Returns the boolean value indicating whether a given taxon name belongs
1458 * to the same {@link HomotypicalGroup homotypical group} as <i>this</i> taxon name (true)
1459 * or not (false). Returns "true" only if the homotypical groups of both
1460 * taxon names exist and if they are identical.
1462 * @param homoTypicName the taxon name the homotypical group of which is to be checked
1463 * @return the boolean value of the check
1464 * @see HomotypicalGroup
1467 public boolean isHomotypic(TaxonNameBase homoTypicName
) {
1468 if (homoTypicName
== null) {
1471 HomotypicalGroup homotypicGroup
= homoTypicName
.getHomotypicalGroup();
1472 if (homotypicGroup
== null || this.getHomotypicalGroup() == null) {
1475 if (homotypicGroup
.equals(this.getHomotypicalGroup())) {
1483 * Checks whether name is a basionym for ALL names
1484 * in its homotypical group.
1485 * Returns <code>false</code> if there are no other names in the group
1490 public boolean isGroupsBasionym() {
1491 if (homotypicalGroup
== null){
1492 homotypicalGroup
= HomotypicalGroup
.NewInstance();
1493 homotypicalGroup
.addTypifiedName(this);
1495 Set
<TaxonNameBase
> typifiedNames
= homotypicalGroup
.getTypifiedNames();
1497 // Check whether there are any other names in the group
1498 if (typifiedNames
.size() == 1) {
1502 boolean isBasionymToAll
= true;
1504 for (TaxonNameBase taxonName
: typifiedNames
) {
1505 if (!taxonName
.equals(this)) {
1506 if (! isBasionymFor(taxonName
)) {
1515 * Checks whether a basionym relationship exists between fromName and toName.
1522 public boolean isBasionymFor(TaxonNameBase newCombinationName
) {
1523 Set
<NameRelationship
> relations
= newCombinationName
.getRelationsToThisName();
1524 for (NameRelationship relation
: relations
) {
1525 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) &&
1526 relation
.getFromName().equals(this)) {
1534 * Creates a basionym relationship to all other names in this names homotypical
1537 * @see HomotypicalGroup.setGroupBasionym(TaxonNameBase basionymName)
1541 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup#setGroupBasionym(TaxonNameBase)
1544 public void makeGroupsBasionym() {
1545 this.homotypicalGroup
.setGroupBasionym(this);
1549 //********* Rank comparison shortcuts ********************//
1551 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1552 * taxon name is higher than the genus rank (true) or not (false).
1553 * Suprageneric non viral names are monomials.
1554 * Returns false if rank is null.
1557 * @see #isInfraGeneric()
1559 * @see #isInfraSpecific()
1562 public boolean isSupraGeneric() {
1566 return getRank().isSupraGeneric();
1569 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1570 * taxon name is the genus rank (true) or not (false). Non viral names with
1571 * genus rank are monomials. Returns false if rank is null.
1573 * @see #isSupraGeneric()
1574 * @see #isInfraGeneric()
1576 * @see #isInfraSpecific()
1579 public boolean isGenus() {
1583 return getRank().isGenus();
1586 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1587 * taxon name is higher than the species rank and lower than the
1588 * genus rank (true) or not (false). Infrageneric non viral names are
1589 * binomials. Returns false if rank is null.
1591 * @see #isSupraGeneric()
1594 * @see #isInfraSpecific()
1597 public boolean isInfraGeneric() {
1601 return getRank().isInfraGeneric();
1605 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1606 * taxon name is higher than the species rank (true) or not (false).
1607 * Returns false if rank is null.
1610 * @see #isInfraGeneric()
1612 * @see #isInfraSpecific()
1615 public boolean isSupraSpecific(){
1619 return getRank().isHigher(Rank
.SPECIES());
1623 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1624 * taxon name is the species rank (true) or not (false). Non viral names
1625 * with species rank are binomials.
1626 * Returns false if rank is null.
1628 * @see #isSupraGeneric()
1630 * @see #isInfraGeneric()
1631 * @see #isInfraSpecific()
1634 public boolean isSpecies() {
1638 return getRank().isSpecies();
1641 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1642 * taxon name is lower than the species rank (true) or not (false).
1643 * Infraspecific non viral names are trinomials.
1644 * Returns false if rank is null.
1646 * @see #isSupraGeneric()
1648 * @see #isInfraGeneric()
1652 public boolean isInfraSpecific() {
1656 return getRank().isInfraSpecific();
1661 * Returns null as the {@link NomenclaturalCode nomenclatural code} that governs
1662 * the construction of <i>this</i> taxon name since there is no specific
1663 * nomenclatural code defined. The real implementention takes place in the
1664 * subclasses {@link ViralName ViralName}, {@link BacterialName BacterialName},
1665 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
1666 * {@link ZoologicalName ZoologicalName}. Each taxon name is governed by one
1667 * and only one nomenclatural code.
1670 * @see #isCodeCompliant()
1671 * @see #getHasProblem()
1673 abstract public NomenclaturalCode
getNomenclaturalCode();
1676 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1679 * Generates and returns the string with the scientific name of <i>this</i>
1680 * taxon name (only non viral taxon names can be generated from their
1681 * components). This string may be stored in the inherited
1682 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
1683 * This method overrides the generic and inherited
1684 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() method} from
1685 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
1687 * @return the string with the composed name of this non viral taxon name with authorship (and maybe year)
1688 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1689 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
1692 // public abstract String generateTitle();
1695 * Creates a basionym relationship between this name and
1696 * each name in its homotypic group.
1698 * @param basionymName
1701 public void setAsGroupsBasionym() {
1704 HomotypicalGroup homotypicalGroup
= this.getHomotypicalGroup();
1706 if (homotypicalGroup
== null) {
1710 Set
<NameRelationship
> relations
= new HashSet
<NameRelationship
>();
1711 Set
<NameRelationship
> removeRelations
= new HashSet
<NameRelationship
>();
1713 for(TaxonNameBase
<?
, ?
> typifiedName
: homotypicalGroup
.getTypifiedNames()){
1715 Set
<NameRelationship
> nameRelations
= typifiedName
.getRelationsFromThisName();
1717 for(NameRelationship nameRelation
: nameRelations
){
1718 relations
.add(nameRelation
);
1722 for (NameRelationship relation
: relations
) {
1724 // If this is a basionym relation, and toName is in the homotypical group,
1725 // remove the relationship.
1726 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) &&
1727 relation
.getToName().getHomotypicalGroup().equals(homotypicalGroup
)) {
1728 removeRelations
.add(relation
);
1732 // Removing relations from a set through which we are iterating causes a
1733 // ConcurrentModificationException. Therefore, we delete the targeted
1734 // relations in a second step.
1735 for (NameRelationship relation
: removeRelations
) {
1736 this.removeNameRelationship(relation
);
1740 for (TaxonNameBase
<?
, ?
> name
: homotypicalGroup
.getTypifiedNames()) {
1741 if (!name
.equals(this)) {
1743 // First check whether the relationship already exists
1744 if (!this.isBasionymFor(name
)) {
1747 name
.addRelationshipFromName(this,
1748 NameRelationshipType
.BASIONYM(), null);
1755 * Removes basionym relationship between this name and
1756 * each name in its homotypic group.
1758 * @param basionymName
1761 public void removeAsGroupsBasionym() {
1763 HomotypicalGroup homotypicalGroup
= this.getHomotypicalGroup();
1765 if (homotypicalGroup
== null) {
1769 Set
<NameRelationship
> relations
= new HashSet
<NameRelationship
>();
1770 Set
<NameRelationship
> removeRelations
= new HashSet
<NameRelationship
>();
1772 for(TaxonNameBase
<?
, ?
> typifiedName
: homotypicalGroup
.getTypifiedNames()){
1774 Set
<NameRelationship
> nameRelations
= typifiedName
.getRelationsFromThisName();
1776 for(NameRelationship nameRelation
: nameRelations
){
1777 relations
.add(nameRelation
);
1781 for (NameRelationship relation
: relations
) {
1783 // If this is a basionym relation, and toName is in the homotypical group,
1784 // and fromName is basionymName, remove the relationship.
1785 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) &&
1786 relation
.getFromName().equals(this) &&
1787 relation
.getToName().getHomotypicalGroup().equals(homotypicalGroup
)) {
1788 removeRelations
.add(relation
);
1792 // Removing relations from a set through which we are iterating causes a
1793 // ConcurrentModificationException. Therefore, we delete the targeted
1794 // relations in a second step.
1795 for (NameRelationship relation
: removeRelations
) {
1796 this.removeNameRelationship(relation
);
1800 //*********************** CLONE ********************************************************/
1803 * Clones <i>this</i> taxon name. This is a shortcut that enables to create
1804 * a new instance that differs only slightly from <i>this</i> taxon name by
1805 * modifying only some of the attributes.<BR><BR>
1806 * Usages of this name in a taxon concept are <b>not</b> cloned.<BR>
1807 * <b>The name gets a newly created homotypical group</b><BR>
1808 * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1809 * {@link TaxonNameDescription Name descriptions} are cloned and not reused.<BR>
1810 * {@link TypeDesignationBase Type designations} are cloned and not reused.<BR>
1812 * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1813 * @see java.lang.Object#clone()
1816 public Object
clone() {
1817 TaxonNameBase result
;
1819 result
= (TaxonNameBase
)super.clone();
1821 //taxonBases -> empty
1822 result
.taxonBases
= new HashSet
<TaxonBase
>();
1825 if (! protectedFullTitleCache
){
1826 result
.fullTitleCache
= null;
1830 result
.descriptions
= new HashSet
<TaxonNameDescription
>();
1831 for (TaxonNameDescription taxonNameDescription
: getDescriptions()){
1832 TaxonNameDescription newDescription
= (TaxonNameDescription
)taxonNameDescription
.clone();
1833 result
.descriptions
.add(newDescription
);
1837 result
.status
= new HashSet
<NomenclaturalStatus
>();
1838 for (NomenclaturalStatus nomenclaturalStatus
: getStatus()){
1839 NomenclaturalStatus newStatus
= (NomenclaturalStatus
)nomenclaturalStatus
.clone();
1840 result
.status
.add(newStatus
);
1845 result
.relationsToThisName
= new HashSet
<NameRelationship
>();
1846 for (NameRelationship toRelationship
: getRelationsToThisName()){
1847 NameRelationship newRelationship
= (NameRelationship
)toRelationship
.clone();
1848 newRelationship
.setRelatedTo(result
);
1849 result
.relationsToThisName
.add(newRelationship
);
1853 result
.relationsFromThisName
= new HashSet
<NameRelationship
>();
1854 for (NameRelationship fromRelationship
: getRelationsFromThisName()){
1855 NameRelationship newRelationship
= (NameRelationship
)fromRelationship
.clone();
1856 newRelationship
.setRelatedFrom(result
);
1857 result
.relationsFromThisName
.add(newRelationship
);
1861 result
.typeDesignations
= new HashSet
<TypeDesignationBase
>();
1862 for (TypeDesignationBase typeDesignation
: getTypeDesignations()){
1863 TypeDesignationBase newDesignation
= (TypeDesignationBase
)typeDesignation
.clone();
1864 result
.typeDesignations
.add(newDesignation
);
1865 newDesignation
.addTypifiedName(result
);
1869 //TODO still needs to be discussed
1870 homotypicalGroup
= HomotypicalGroup
.NewInstance();
1871 homotypicalGroup
.addTypifiedName(this);
1873 //no changes to: appendedPharse, nomenclaturalReference,
1874 //nomenclaturalMicroReference, parsingProblem, problemEnds, problemStarts
1875 //protectedFullTitleCache, rank
1877 } catch (CloneNotSupportedException e
) {
1878 logger
.warn("Object does not implement cloneable");
1879 e
.printStackTrace();