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
.ArrayList
;
14 import java
.util
.HashSet
;
15 import java
.util
.Iterator
;
16 import java
.util
.List
;
19 import java
.util
.concurrent
.CopyOnWriteArrayList
;
21 import javax
.persistence
.Column
;
22 import javax
.persistence
.Entity
;
23 import javax
.persistence
.FetchType
;
24 import javax
.persistence
.Inheritance
;
25 import javax
.persistence
.InheritanceType
;
26 import javax
.persistence
.JoinTable
;
27 import javax
.persistence
.ManyToMany
;
28 import javax
.persistence
.ManyToOne
;
29 import javax
.persistence
.OneToMany
;
30 import javax
.persistence
.Transient
;
31 import javax
.validation
.Valid
;
32 import javax
.validation
.constraints
.NotNull
;
33 import javax
.validation
.constraints
.Size
;
34 import javax
.xml
.bind
.annotation
.XmlAccessType
;
35 import javax
.xml
.bind
.annotation
.XmlAccessorType
;
36 import javax
.xml
.bind
.annotation
.XmlAttribute
;
37 import javax
.xml
.bind
.annotation
.XmlElement
;
38 import javax
.xml
.bind
.annotation
.XmlElementWrapper
;
39 import javax
.xml
.bind
.annotation
.XmlIDREF
;
40 import javax
.xml
.bind
.annotation
.XmlRootElement
;
41 import javax
.xml
.bind
.annotation
.XmlSchemaType
;
42 import javax
.xml
.bind
.annotation
.XmlType
;
44 import org
.apache
.log4j
.Logger
;
45 import org
.hibernate
.annotations
.Cascade
;
46 import org
.hibernate
.annotations
.CascadeType
;
47 import org
.hibernate
.annotations
.Index
;
48 import org
.hibernate
.annotations
.Table
;
49 import org
.hibernate
.envers
.Audited
;
50 import org
.hibernate
.search
.annotations
.Field
;
51 import org
.hibernate
.search
.annotations
.IndexedEmbedded
;
52 import org
.hibernate
.validator
.constraints
.NotEmpty
;
53 import org
.springframework
.util
.ReflectionUtils
;
55 import eu
.etaxonomy
.cdm
.model
.common
.IParsable
;
56 import eu
.etaxonomy
.cdm
.model
.common
.IReferencedEntity
;
57 import eu
.etaxonomy
.cdm
.model
.common
.IRelated
;
58 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
59 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
60 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
61 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
62 import eu
.etaxonomy
.cdm
.model
.reference
.INomenclaturalReference
;
63 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
64 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
65 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
66 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
67 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
68 import eu
.etaxonomy
.cdm
.strategy
.cache
.name
.CacheUpdate
;
69 import eu
.etaxonomy
.cdm
.strategy
.cache
.name
.INameCacheStrategy
;
70 import eu
.etaxonomy
.cdm
.strategy
.match
.IMatchable
;
71 import eu
.etaxonomy
.cdm
.strategy
.match
.Match
;
72 import eu
.etaxonomy
.cdm
.strategy
.match
.Match
.ReplaceMode
;
73 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchMode
;
74 import eu
.etaxonomy
.cdm
.strategy
.merge
.Merge
;
75 import eu
.etaxonomy
.cdm
.strategy
.merge
.MergeMode
;
76 import eu
.etaxonomy
.cdm
.strategy
.parser
.ParserProblem
;
77 import eu
.etaxonomy
.cdm
.validation
.Level2
;
80 * The upmost (abstract) class for scientific taxon names regardless of any
81 * particular {@link NomenclaturalCode nomenclature code}. The scientific taxon name does not depend
82 * on the use made of it in a publication or a treatment
83 * ({@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon concept respectively potential taxon})
84 * as an {@link eu.etaxonomy.cdm.model.taxon.Taxon "accepted" respectively "correct" (taxon) name}
85 * or as a {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
87 * This class corresponds partially to: <ul>
88 * <li> TaxonName according to the TDWG ontology
89 * <li> ScientificName and CanonicalName according to the TCS
90 * <li> ScientificName according to the ABCD schema
95 * @created 08-Nov-2007 13:06:57
97 @XmlAccessorType(XmlAccessType
.FIELD
)
98 @XmlType(name
= "TaxonNameBase", propOrder
= {
100 "nomenclaturalMicroReference",
101 "nomenclaturalReference",
104 "protectedFullTitleCache",
107 "relationsFromThisName",
108 "relationsToThisName",
113 @XmlRootElement(name
= "TaxonNameBase")
116 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
117 @Table(appliesTo
="TaxonNameBase", indexes
= { @Index(name
= "taxonNameBaseTitleCacheIndex", columnNames
= { "titleCache" }), @Index(name
= "taxonNameBaseNameCacheIndex", columnNames
= { "nameCache" }) })
118 public abstract class TaxonNameBase
<T
extends TaxonNameBase
<?
,?
>, S
extends INameCacheStrategy
> extends IdentifiableEntity
<S
> implements IParsable
, IRelated
, IMatchable
, Cloneable
{
119 private static final long serialVersionUID
= -4530368639601532116L;
120 private static final Logger logger
= Logger
.getLogger(TaxonNameBase
.class);
122 @XmlElement(name
= "FullTitleCache")
123 @Column(length
=330, name
="fullTitleCache")
124 @Match(value
=MatchMode
.CACHE
, cacheReplaceMode
=ReplaceMode
.ALL
)
125 @CacheUpdate(noUpdate
="titleCache")
126 @NotEmpty(groups
= Level2
.class)
127 @Size(max
= 800) //see #1592
128 protected String fullTitleCache
;
130 //if true titleCache will not be automatically generated/updated
131 @XmlElement(name
= "ProtectedFullTitleCache")
132 @CacheUpdate(value
="fullTitleCache", noUpdate
="titleCache")
133 private boolean protectedFullTitleCache
;
135 @XmlElementWrapper(name
= "Descriptions")
136 @XmlElement(name
= "Description")
137 @OneToMany(mappedBy
="taxonName", fetch
= FetchType
.LAZY
, orphanRemoval
=true)
138 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
140 private Set
<TaxonNameDescription
> descriptions
= new HashSet
<TaxonNameDescription
>();
142 @XmlElement(name
= "AppendedPhrase")
144 @CacheUpdate(value
="nameCache")
148 private String appendedPhrase
;
150 @XmlElement(name
= "NomenclaturalMicroReference")
152 @CacheUpdate(noUpdate
="titleCache")
156 private String nomenclaturalMicroReference
;
159 @CacheUpdate(noUpdate
={"titleCache","fullTitleCache"})
160 private int parsingProblem
= 0;
163 @CacheUpdate(noUpdate
={"titleCache","fullTitleCache"})
164 private int problemStarts
= -1;
167 @CacheUpdate(noUpdate
={"titleCache","fullTitleCache"})
168 private int problemEnds
= -1;
170 @XmlElementWrapper(name
= "TypeDesignations")
171 @XmlElement(name
= "TypeDesignation")
173 @XmlSchemaType(name
= "IDREF")
174 @ManyToMany(fetch
= FetchType
.LAZY
)
176 name
="TaxonNameBase_TypeDesignationBase",
177 joinColumns
=@javax.persistence
.JoinColumn(name
="TaxonNameBase_id"),
178 inverseJoinColumns
=@javax.persistence
.JoinColumn(name
="typedesignations_id")
180 @Cascade({CascadeType
.SAVE_UPDATE
})
182 private Set
<TypeDesignationBase
> typeDesignations
= new HashSet
<TypeDesignationBase
>();
184 @XmlElement(name
= "HomotypicalGroup")
186 @XmlSchemaType(name
= "IDREF")
187 @ManyToOne(fetch
= FetchType
.LAZY
)
188 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
189 @Match(MatchMode
.IGNORE
)
190 @CacheUpdate(noUpdate
="titleCache")
193 private HomotypicalGroup homotypicalGroup
;
195 @XmlElementWrapper(name
= "RelationsFromThisName")
196 @XmlElement(name
= "RelationFromThisName")
197 @OneToMany(mappedBy
="relatedFrom", fetch
= FetchType
.LAZY
, orphanRemoval
=true)
198 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
199 @Merge(MergeMode
.RELATION
)
202 private Set
<NameRelationship
> relationsFromThisName
= new HashSet
<NameRelationship
>();
204 @XmlElementWrapper(name
= "RelationsToThisName")
205 @XmlElement(name
= "RelationToThisName")
207 @XmlSchemaType(name
= "IDREF")
208 @OneToMany(mappedBy
="relatedTo", fetch
= FetchType
.LAZY
, orphanRemoval
=true)
209 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
210 @Merge(MergeMode
.RELATION
)
213 private Set
<NameRelationship
> relationsToThisName
= new HashSet
<NameRelationship
>();
215 @XmlElementWrapper(name
= "NomenclaturalStatuses")
216 @XmlElement(name
= "NomenclaturalStatus")
217 @OneToMany(fetch
= FetchType
.LAZY
, orphanRemoval
=true)
218 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
,CascadeType
.DELETE
})
220 @IndexedEmbedded(depth
=1)
221 private Set
<NomenclaturalStatus
> status
= new HashSet
<NomenclaturalStatus
>();
223 @XmlElementWrapper(name
= "TaxonBases")
224 @XmlElement(name
= "TaxonBase")
226 @XmlSchemaType(name
= "IDREF")
227 @OneToMany(mappedBy
="name", fetch
= FetchType
.LAZY
)
229 @IndexedEmbedded(depth
=1)
230 private Set
<TaxonBase
> taxonBases
= new HashSet
<TaxonBase
>();
232 @XmlElement(name
= "Rank")
234 @XmlSchemaType(name
= "IDREF")
235 @ManyToOne(fetch
= FetchType
.EAGER
)
236 @CacheUpdate(value
="nameCache")
237 //TODO Val #3379, handle maybe as groups = Level2.class ??
239 @IndexedEmbedded(depth
=1)
242 @XmlElement(name
= "NomenclaturalReference")
244 @XmlSchemaType(name
= "IDREF")
245 @ManyToOne(fetch
= FetchType
.LAZY
)
246 @Cascade({CascadeType
.SAVE_UPDATE
})
247 @CacheUpdate(noUpdate
="titleCache")
249 private Reference nomenclaturalReference
;
251 // ************* CONSTRUCTORS *************/
253 * Class constructor: creates a new empty taxon name.
255 * @see #TaxonNameBase(Rank)
256 * @see #TaxonNameBase(HomotypicalGroup)
257 * @see #TaxonNameBase(Rank, HomotypicalGroup)
259 public TaxonNameBase() {
263 * Class constructor: creates a new taxon name
264 * only containing its {@link Rank rank}.
266 * @param rank the rank to be assigned to <i>this</i> taxon name
267 * @see #TaxonNameBase()
268 * @see #TaxonNameBase(HomotypicalGroup)
269 * @see #TaxonNameBase(Rank, HomotypicalGroup)
271 public TaxonNameBase(Rank rank
) {
275 * Class constructor: creates a new taxon name
276 * only containing its {@link HomotypicalGroup homotypical group}.
277 * The new taxon name will be also added to the set of taxon names
278 * belonging to this homotypical group.
280 * @param homotypicalGroup the homotypical group to which <i>this</i> taxon name belongs
281 * @see #TaxonNameBase()
282 * @see #TaxonNameBase(Rank)
283 * @see #TaxonNameBase(Rank, HomotypicalGroup)
285 public TaxonNameBase(HomotypicalGroup homotypicalGroup
) {
286 this(null, homotypicalGroup
);
289 * Class constructor: creates a new taxon name
290 * only containing its {@link Rank rank} and
291 * its {@link HomotypicalGroup homotypical group}.
292 * The new taxon name will be also added to the set of taxon names
293 * belonging to this homotypical group.
295 * @param rank the rank to be assigned to <i>this</i> taxon name
296 * @param homotypicalGroup the homotypical group to which <i>this</i> taxon name belongs
297 * @see #TaxonNameBase()
298 * @see #TaxonNameBase(Rank)
299 * @see #TaxonNameBase(HomotypicalGroup)
301 public TaxonNameBase(Rank rank
, HomotypicalGroup homotypicalGroup
) {
304 if (homotypicalGroup
== null){
305 homotypicalGroup
= new HomotypicalGroup();
307 homotypicalGroup
.addTypifiedName(this);
310 abstract protected Map
<String
, java
.lang
.reflect
.Field
> getAllFields();
312 //********* METHODS **************************************/
315 * Returns the boolean value "false" since the components of <i>this</i> taxon name
316 * cannot follow the rules of a corresponding {@link NomenclaturalCode nomenclatural code}
317 * which is not defined for this class. The nomenclature code depends on
318 * the concrete name subclass ({@link BacterialName BacterialName},
319 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName},
320 * {@link ZoologicalName ZoologicalName} or {@link ViralName ViralName})
321 * to which a taxon name belongs.
326 public abstract boolean isCodeCompliant();
328 public abstract String
generateFullTitle();
333 public List
<TaggedText
> getTaggedName(){
334 return getCacheStrategy().getTaggedTitle(this);
338 public String
getFullTitleCache(){
339 if (protectedFullTitleCache
){
340 return this.fullTitleCache
;
342 if (fullTitleCache
== null ){
343 this.fullTitleCache
= getTruncatedCache(generateFullTitle());
345 return fullTitleCache
;
349 public void setFullTitleCache(String fullTitleCache
){
350 setFullTitleCache(fullTitleCache
, PROTECTED
);
353 public void setFullTitleCache(String fullTitleCache
, boolean protectCache
){
354 fullTitleCache
= getTruncatedCache(fullTitleCache
);
355 this.fullTitleCache
= fullTitleCache
;
356 this.setProtectedFullTitleCache(protectCache
);
360 public boolean isProtectedFullTitleCache() {
361 return protectedFullTitleCache
;
364 public void setProtectedFullTitleCache(boolean protectedFullTitleCache
) {
365 this.protectedFullTitleCache
= protectedFullTitleCache
;
369 * Returns the set of all {@link NameRelationship name relationships}
370 * in which <i>this</i> taxon name is involved. A taxon name can be both source
371 * in some name relationships or target in some others.
373 * @see #getRelationsToThisName()
374 * @see #getRelationsFromThisName()
375 * @see #addNameRelationship(NameRelationship)
376 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
377 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
380 public Set
<NameRelationship
> getNameRelations() {
381 Set
<NameRelationship
> rels
= new HashSet
<NameRelationship
>();
382 rels
.addAll(getRelationsFromThisName());
383 rels
.addAll(getRelationsToThisName());
388 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
389 * and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
390 * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
392 * @param toName the taxon name of the target for this new name relationship
393 * @param type the type of this new name relationship
394 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
395 * @see #getRelationsToThisName()
396 * @see #getNameRelations()
397 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
398 * @see #addNameRelationship(NameRelationship)
400 public void addRelationshipToName(TaxonNameBase toName
, NameRelationshipType type
, String ruleConsidered
){
401 addRelationshipToName(toName
, type
, null, null, ruleConsidered
);
402 // NameRelationship rel = new NameRelationship(toName, this, type, ruleConsidered);
406 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
407 * and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
408 * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
410 * @param toName the taxon name of the target for this new name relationship
411 * @param type the type of this new name relationship
412 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
414 * @see #getRelationsToThisName()
415 * @see #getNameRelations()
416 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
417 * @see #addNameRelationship(NameRelationship)
419 public NameRelationship
addRelationshipToName(TaxonNameBase toName
, NameRelationshipType type
, Reference citation
, String microCitation
, String ruleConsidered
){
421 throw new NullPointerException("Null is not allowed as name for a name relationship");
423 NameRelationship rel
= new NameRelationship(toName
, this, type
, citation
, microCitation
, ruleConsidered
);
428 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
429 * and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
430 * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
432 * @param fromName the taxon name of the source for this new name relationship
433 * @param type the type of this new name relationship
434 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
435 * @param citation the reference in which this relation was described
436 * @param microCitation the reference detail for this relation (e.g. page)
437 * @see #getRelationsFromThisName()
438 * @see #getNameRelations()
439 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
440 * @see #addNameRelationship(NameRelationship)
442 public void addRelationshipFromName(TaxonNameBase fromName
, NameRelationshipType type
, String ruleConsidered
){
443 //fromName.addRelationshipToName(this, type, null, null, ruleConsidered);
444 NameRelationship rel
= this.addRelationshipFromName(fromName
, type
, null, null, ruleConsidered
);
446 // NameRelationship rel = new NameRelationship(this, fromName, type, ruleConsidered);
449 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
450 * and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
451 * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
453 * @param fromName the taxon name of the source for this new name relationship
454 * @param type the type of this new name relationship
455 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
456 * @param citation the reference in which this relation was described
457 * @param microCitation the reference detail for this relation (e.g. page)
458 * @see #getRelationsFromThisName()
459 * @see #getNameRelations()
460 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
461 * @see #addNameRelationship(NameRelationship)
463 public NameRelationship
addRelationshipFromName(TaxonNameBase fromName
, NameRelationshipType type
, Reference citation
, String microCitation
, String ruleConsidered
){
464 return fromName
.addRelationshipToName(this, type
, citation
, microCitation
, ruleConsidered
);
468 * Adds an existing {@link NameRelationship name relationship} either to the set of
469 * {@link #getRelationsToThisName() relations to <i>this</i> taxon name} or to the set of
470 * {@link #getRelationsFromThisName() relations from <i>this</i> taxon name}. If neither the
471 * source nor the target of the name relationship match with <i>this</i> taxon name
472 * no addition will be carried out.
474 * @param rel the name relationship to be added to one of <i>this</i> taxon name's name relationships sets
475 * @see #getNameRelations()
476 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
477 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
479 protected void addNameRelationship(NameRelationship rel
) {
480 if (rel
!=null && rel
.getToName().equals(this)){
481 this.relationsToThisName
.add(rel
);
482 }else if(rel
!=null && rel
.getFromName().equals(this)){
483 this.relationsFromThisName
.add(rel
);
485 throw new RuntimeException("NameRelationship is either null or the relationship does not reference this name");
489 * Removes one {@link NameRelationship name relationship} from one of both sets of
490 * {@link #getNameRelations() name relationships} in which <i>this</i> taxon name is involved.
491 * The name relationship will also be removed from one of both sets belonging
492 * to the second taxon name involved. Furthermore the fromName and toName
493 * attributes of the name relationship object will be nullified.
495 * @param nameRelation the name relationship which should be deleted from one of both sets
496 * @see #getNameRelations()
498 public void removeNameRelationship(NameRelationship nameRelation
) {
500 TaxonNameBase fromName
= nameRelation
.getFromName();
501 TaxonNameBase toName
= nameRelation
.getToName();
503 if (nameRelation
!= null) {
504 nameRelation
.setToName(null);
505 nameRelation
.setFromName(null);
508 if (fromName
!= null) {
509 fromName
.removeNameRelationship(nameRelation
);
512 if (toName
!= null) {
513 toName
.removeNameRelationship(nameRelation
);
516 this.relationsToThisName
.remove(nameRelation
);
517 this.relationsFromThisName
.remove(nameRelation
);
520 public void removeRelationToTaxonName(TaxonNameBase toTaxonName
) {
521 Set
<NameRelationship
> nameRelationships
= new HashSet
<NameRelationship
>();
522 // nameRelationships.addAll(this.getNameRelations());
523 nameRelationships
.addAll(this.getRelationsFromThisName());
524 nameRelationships
.addAll(this.getRelationsToThisName());
525 for(NameRelationship nameRelationship
: nameRelationships
) {
526 // remove name relationship from this side
527 if (nameRelationship
.getFromName().equals(this) && nameRelationship
.getToName().equals(toTaxonName
)) {
528 this.removeNameRelationship(nameRelationship
);
535 * Does exactly the same as the addNameRelationship method provided that
536 * the given relationship is a name relationship.
538 * @param relation the relationship to be added to one of <i>this</i> taxon name's name relationships sets
539 * @see #addNameRelationship(NameRelationship)
540 * @see #getNameRelations()
541 * @see NameRelationship
542 * @see eu.etaxonomy.cdm.model.common.RelationshipBase
545 public void addRelationship(RelationshipBase relation
) {
546 if (relation
instanceof NameRelationship
){
547 addNameRelationship((NameRelationship
)relation
);
548 NameRelationshipType type
= (NameRelationshipType
)relation
.getType();
549 if (type
!= null && ( type
.isBasionymRelation() || type
.isReplacedSynonymRelation() ) ){
550 TaxonNameBase fromName
= ((NameRelationship
)relation
).getFromName();
551 TaxonNameBase toName
= ((NameRelationship
)relation
).getToName();
552 fromName
.mergeHomotypicGroups(toName
);
555 logger
.warn("Relationship not of type NameRelationship!");
556 throw new IllegalArgumentException("Relationship not of type NameRelationship");
562 * Returns the set of all {@link NameRelationship name relationships}
563 * in which <i>this</i> taxon name is involved as a source ("from"-side).
565 * @see #getNameRelations()
566 * @see #getRelationsToThisName()
567 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
569 public Set
<NameRelationship
> getRelationsFromThisName() {
570 if(relationsFromThisName
== null) {
571 this.relationsFromThisName
= new HashSet
<NameRelationship
>();
573 return relationsFromThisName
;
577 * Returns the set of all {@link NameRelationship name relationships}
578 * in which <i>this</i> taxon name is involved as a target ("to"-side).
580 * @see #getNameRelations()
581 * @see #getRelationsFromThisName()
582 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
584 public Set
<NameRelationship
> getRelationsToThisName() {
585 if(relationsToThisName
== null) {
586 this.relationsToThisName
= new HashSet
<NameRelationship
>();
588 return relationsToThisName
;
592 * Returns the set of {@link NomenclaturalStatus nomenclatural status} assigned
593 * to <i>this</i> taxon name according to its corresponding nomenclature code.
594 * This includes the {@link NomenclaturalStatusType type} of the nomenclatural status
595 * and the nomenclatural code rule considered.
597 * @see NomenclaturalStatus
598 * @see NomenclaturalStatusType
600 public Set
<NomenclaturalStatus
> getStatus() {
602 this.status
= new HashSet
<NomenclaturalStatus
>();
608 * Adds a new {@link NomenclaturalStatus nomenclatural status}
609 * to <i>this</i> taxon name's set of nomenclatural status.
611 * @param nomStatus the nomenclatural status to be added
614 public void addStatus(NomenclaturalStatus nomStatus
) {
615 this.status
.add(nomStatus
);
619 * Removes one element from the set of nomenclatural status of <i>this</i> taxon name.
620 * Type and ruleConsidered attributes of the nomenclatural status object
623 * @param nomStatus the nomenclatural status of <i>this</i> taxon name which should be deleted
626 public void removeStatus(NomenclaturalStatus nomStatus
) {
627 //TODO to be implemented?
628 logger
.warn("not yet fully implemented?");
629 this.status
.remove(nomStatus
);
634 * Indicates whether <i>this</i> taxon name is a {@link NameRelationshipType#BASIONYM() basionym}
635 * or a {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym}
636 * of any other taxon name. Returns "true", if a basionym or a replaced
637 * synonym {@link NameRelationship relationship} from <i>this</i> taxon name to another taxon name exists,
638 * false otherwise (also in case <i>this</i> taxon name is the only one in the
639 * homotypical group).
642 public boolean isOriginalCombination(){
643 Set
<NameRelationship
> relationsFromThisName
= this.getRelationsFromThisName();
644 for (NameRelationship relation
: relationsFromThisName
) {
645 if (relation
.getType().isBasionymRelation() ||
646 relation
.getType().isReplacedSynonymRelation()) {
654 * Indicates <i>this</i> taxon name is a {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym}
655 * of any other taxon name. Returns "true", if a replaced
656 * synonym {@link NameRelationship relationship} from <i>this</i> taxon name to another taxon name exists,
657 * false otherwise (also in case <i>this</i> taxon name is the only one in the
658 * homotypical group).
661 public boolean isReplacedSynonym(){
662 Set
<NameRelationship
> relationsFromThisName
= this.getRelationsFromThisName();
663 for (NameRelationship relation
: relationsFromThisName
) {
664 if (relation
.getType().isReplacedSynonymRelation()) {
672 * Returns the taxon name which is the {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
673 * The basionym of a taxon name is its epithet-bringing synonym.
674 * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
675 * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
676 * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
678 * If more than one basionym exists one is choosen at radom.
680 * If no basionym exists null is returned.
683 public TaxonNameBase
getBasionym(){
684 Set
<TaxonNameBase
> basionyms
= getBasionyms();
685 if (basionyms
.size() == 0){
688 return basionyms
.iterator().next();
693 * Returns the set of taxon names which are the {@link NameRelationshipType#BASIONYM() basionyms} of <i>this</i> taxon name.
694 * The basionym of a taxon name is its epithet-bringing synonym.
695 * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
696 * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
697 * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
700 public Set
<TaxonNameBase
> getBasionyms(){
701 Set
<TaxonNameBase
> result
= new HashSet
<TaxonNameBase
>();
702 Set
<NameRelationship
> rels
= this.getRelationsToThisName();
703 for (NameRelationship rel
: rels
){
704 if (rel
.getType().isBasionymRelation()){
705 TaxonNameBase basionym
= rel
.getFromName();
706 result
.add(basionym
);
713 * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
714 * The basionym {@link NameRelationship relationship} will be added to <i>this</i> taxon name
715 * and to the basionym. The basionym cannot have itself a basionym.
716 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
717 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
719 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
720 * @see #getBasionym()
721 * @see #addBasionym(TaxonNameBase, String)
723 public void addBasionym(T basionym
){
724 addBasionym(basionym
, null, null, null);
727 * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name
728 * and keeps the nomenclatural rule considered for it. The basionym
729 * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the basionym.
730 * The basionym cannot have itself a basionym.
731 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
732 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
734 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
735 * @param ruleConsidered the string identifying the nomenclatural rule
737 * @see #getBasionym()
738 * @see #addBasionym(TaxonNameBase)
740 public NameRelationship
addBasionym(T basionym
, Reference citation
, String microcitation
, String ruleConsidered
){
741 if (basionym
!= null){
742 return basionym
.addRelationshipToName(this, NameRelationshipType
.BASIONYM(), citation
, microcitation
, ruleConsidered
);
749 * Returns the set of taxon names which are the {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonyms} of <i>this</i> taxon name.
753 public Set
<TaxonNameBase
> getReplacedSynonyms(){
754 Set
<TaxonNameBase
> result
= new HashSet
<TaxonNameBase
>();
755 Set
<NameRelationship
> rels
= this.getRelationsToThisName();
756 for (NameRelationship rel
: rels
){
757 if (rel
.getType().isReplacedSynonymRelation()){
758 TaxonNameBase replacedSynonym
= rel
.getFromName();
759 result
.add(replacedSynonym
);
766 * Assigns a taxon name as {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym} of <i>this</i> taxon name
767 * and keeps the nomenclatural rule considered for it. The replaced synonym
768 * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the replaced synonym.
769 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the replaced synonym
770 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
772 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
773 * @param ruleConsidered the string identifying the nomenclatural rule
774 * @see #getBasionym()
775 * @see #addBasionym(TaxonNameBase)
777 //TODO: Check if true: The replaced synonym cannot have itself a replaced synonym (?).
778 public void addReplacedSynonym(T replacedSynonym
, Reference citation
, String microcitation
, String ruleConsidered
){
779 if (replacedSynonym
!= null){
780 replacedSynonym
.addRelationshipToName(this, NameRelationshipType
.REPLACED_SYNONYM(), citation
, microcitation
, ruleConsidered
);
785 * Removes the {@link NameRelationshipType#BASIONYM() basionym} {@link NameRelationship relationship} from the set of
786 * {@link #getRelationsToThisName() name relationships to} <i>this</i> taxon name. The same relationhip will be
787 * removed from the set of {@link #getRelationsFromThisName() name relationships from} the taxon name
788 * previously used as basionym.
790 * @see #getBasionym()
791 * @see #addBasionym(TaxonNameBase)
793 public void removeBasionyms(){
794 Set
<NameRelationship
> removeRelations
= new HashSet
<NameRelationship
>();
795 for (NameRelationship nameRelation
: this.getRelationsToThisName()){
796 if (nameRelation
.getType().isBasionymRelation()){
797 removeRelations
.add(nameRelation
);
800 // Removing relations from a set through which we are iterating causes a
801 // ConcurrentModificationException. Therefore, we delete the targeted
802 // relations in a second step.
803 for (NameRelationship relation
: removeRelations
){
804 this.removeNameRelationship(relation
);
809 * Returns the taxonomic {@link Rank rank} of <i>this</i> taxon name.
813 public Rank
getRank(){
820 public void setRank(Rank rank
){
825 * Returns the {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} of <i>this</i> taxon name.
826 * The nomenclatural reference is here meant to be the one publication
827 * <i>this</i> taxon name was originally published in while fulfilling the formal
828 * requirements as specified by the corresponding {@link NomenclaturalCode nomenclatural code}.
830 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference
831 * @see eu.etaxonomy.cdm.model.reference.Reference
833 public INomenclaturalReference
getNomenclaturalReference(){
834 return this.nomenclaturalReference
;
837 * Assigns a {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} to <i>this</i> taxon name.
838 * The corresponding {@link eu.etaxonomy.cdm.model.reference.Reference.isNomenclaturallyRelevant nomenclaturally relevant flag} will be set to true
839 * as it is obviously used for nomenclatural purposes.
841 * @throws IllegalArgumentException if parameter <code>nomenclaturalReference</code> is not assignable from {@link INomenclaturalReference}
842 * @see #getNomenclaturalReference()
844 public void setNomenclaturalReference(INomenclaturalReference nomenclaturalReference
){
845 if(nomenclaturalReference
!= null){
846 if(!INomenclaturalReference
.class.isAssignableFrom(nomenclaturalReference
.getClass())){
847 throw new IllegalArgumentException("Parameter nomenclaturalReference is not assignable from INomenclaturalReference");
849 this.nomenclaturalReference
= (Reference
)nomenclaturalReference
;
851 this.nomenclaturalReference
= null;
856 * Returns the appended phrase string assigned to <i>this</i> taxon name.
857 * The appended phrase is a non-atomised addition to a name. It is
858 * not ruled by a nomenclatural code.
860 public String
getAppendedPhrase(){
861 return this.appendedPhrase
;
865 * @see #getAppendedPhrase()
867 public void setAppendedPhrase(String appendedPhrase
){
868 this.appendedPhrase
= appendedPhrase
;
872 * Returns the details string of the {@link #getNomenclaturalReference() nomenclatural reference} assigned
873 * to <i>this</i> taxon name. The details describe the exact localisation within
874 * the publication used as nomenclature reference. These are mostly
875 * (implicitly) pages but can also be figures or tables or any other
876 * element of a publication. A nomenclatural micro reference (details)
877 * requires the existence of a nomenclatural reference.
879 //Details of the nomenclatural reference (protologue).
880 public String
getNomenclaturalMicroReference(){
881 return this.nomenclaturalMicroReference
;
884 * @see #getNomenclaturalMicroReference()
886 public void setNomenclaturalMicroReference(String nomenclaturalMicroReference
){
887 this.nomenclaturalMicroReference
= nomenclaturalMicroReference
;
891 * @see eu.etaxonomy.cdm.model.common.IParsable#getHasProblem()
894 public int getParsingProblem(){
895 return this.parsingProblem
;
899 * @see eu.etaxonomy.cdm.model.common.IParsable#setHasProblem(int)
902 public void setParsingProblem(int parsingProblem
){
903 this.parsingProblem
= parsingProblem
;
907 * @see eu.etaxonomy.cdm.model.common.IParsable#addProblem(eu.etaxonomy.cdm.strategy.parser.NameParserWarning)
910 public void addParsingProblem(ParserProblem problem
){
911 parsingProblem
= ParserProblem
.addProblem(parsingProblem
, problem
);
915 * @see eu.etaxonomy.cdm.model.common.IParsable#removeParsingProblem(eu.etaxonomy.cdm.strategy.parser.ParserProblem)
918 public void removeParsingProblem(ParserProblem problem
) {
919 parsingProblem
= ParserProblem
.removeProblem(parsingProblem
, problem
);
925 public void addParsingProblems(int problems
){
926 parsingProblem
= ParserProblem
.addProblems(parsingProblem
, problems
);
930 * @see eu.etaxonomy.cdm.model.common.IParsable#hasProblem()
933 public boolean hasProblem(){
934 return parsingProblem
!= 0;
940 * @see eu.etaxonomy.cdm.model.common.IParsable#hasProblem(eu.etaxonomy.cdm.strategy.parser.ParserProblem)
943 public boolean hasProblem(ParserProblem problem
) {
944 return getParsingProblems().contains(problem
);
949 * @see eu.etaxonomy.cdm.model.common.IParsable#problemStarts()
952 public int getProblemStarts(){
953 return this.problemStarts
;
957 * @see eu.etaxonomy.cdm.model.common.IParsable#setProblemStarts(int)
960 public void setProblemStarts(int start
) {
961 this.problemStarts
= start
;
965 * @see eu.etaxonomy.cdm.model.common.IParsable#problemEnds()
968 public int getProblemEnds(){
969 return this.problemEnds
;
973 * @see eu.etaxonomy.cdm.model.common.IParsable#setProblemEnds(int)
976 public void setProblemEnds(int end
) {
977 this.problemEnds
= end
;
980 //*********************** TYPE DESIGNATION *********************************************//
983 * Returns the set of {@link TypeDesignationBase type designations} assigned
984 * to <i>this</i> taxon name.
985 * @see NameTypeDesignation
986 * @see SpecimenTypeDesignation
988 public Set
<TypeDesignationBase
> getTypeDesignations() {
989 if(typeDesignations
== null) {
990 this.typeDesignations
= new HashSet
<TypeDesignationBase
>();
992 return typeDesignations
;
996 * Removes one element from the set of {@link TypeDesignationBase type designations} assigned to
997 * <i>this</i> taxon name. The type designation itself will be nullified.
999 * @param typeDesignation the type designation which should be deleted
1001 @SuppressWarnings("deprecation")
1002 public void removeTypeDesignation(TypeDesignationBase typeDesignation
) {
1003 this.typeDesignations
.remove(typeDesignation
);
1004 typeDesignation
.removeTypifiedName(this);
1008 * Returns the set of {@link SpecimenTypeDesignation specimen type designations} assigned
1009 * to <i>this</i> taxon name. The {@link Rank rank} of <i>this</i> taxon name is generally
1010 * "species" or below. The specimen type designations include all the
1011 * specimens on which the typification of this name is based (which are
1012 * exclusively used to typify taxon names belonging to the same
1013 * {@link HomotypicalGroup homotypical group} to which <i>this</i> taxon name
1014 * belongs) and eventually the status of these designations.
1016 * @see SpecimenTypeDesignation
1017 * @see NameTypeDesignation
1018 * @see HomotypicalGroup
1021 public Set
<SpecimenTypeDesignation
> getSpecimenTypeDesignationsOfHomotypicalGroup() {
1022 return this.getHomotypicalGroup().getSpecimenTypeDesignations();
1025 //*********************** NAME TYPE DESIGNATION *********************************************//
1028 * Returns the set of {@link NameTypeDesignation name type designations} assigned
1029 * to <i>this</i> taxon name the rank of which must be above "species".
1030 * The name type designations include all the taxon names used to typify
1031 * <i>this</i> taxon name and eventually the rejected or conserved status
1032 * of these designations.
1034 * @see NameTypeDesignation
1035 * @see SpecimenTypeDesignation
1038 public Set
<NameTypeDesignation
> getNameTypeDesignations() {
1039 Set
<NameTypeDesignation
> result
= new HashSet
<NameTypeDesignation
>();
1040 for (TypeDesignationBase typeDesignation
: this.typeDesignations
){
1041 if (typeDesignation
instanceof NameTypeDesignation
){
1042 result
.add((NameTypeDesignation
)typeDesignation
);
1049 * Creates and adds a new {@link NameTypeDesignation name type designation}
1050 * to <i>this</i> taxon name's set of type designations.
1052 * @param typeSpecies the taxon name to be used as type of <i>this</i> taxon name
1053 * @param citation the reference for this new designation
1054 * @param citationMicroReference the string with the details (generally pages) within the reference
1055 * @param originalNameString the taxon name string used in the reference to assert this designation
1056 * @param isRejectedType the boolean status for a rejected name type designation
1057 * @param isConservedType the boolean status for a conserved name type designation
1058 * @param isLectoType the boolean status for a lectotype name type designation
1059 * @param isNotDesignated the boolean status for a name type designation without name type
1060 * @param addToAllHomotypicNames the boolean indicating whether the name type designation should be
1061 * added to all taxon names of the homotypical group this taxon name belongs to
1063 * @see #getNameTypeDesignations()
1064 * @see NameTypeDesignation
1065 * @see TypeDesignationBase#isNotDesignated()
1067 public NameTypeDesignation
addNameTypeDesignation(TaxonNameBase typeSpecies
,
1069 String citationMicroReference
,
1070 String originalNameString
,
1071 NameTypeDesignationStatus status
,
1072 boolean isRejectedType
,
1073 boolean isConservedType
,
1074 /*boolean isLectoType, */
1075 boolean isNotDesignated
,
1076 boolean addToAllHomotypicNames
) {
1077 NameTypeDesignation nameTypeDesignation
= new NameTypeDesignation(typeSpecies
, citation
, citationMicroReference
, originalNameString
, status
, isRejectedType
, isConservedType
, isNotDesignated
);
1078 //nameTypeDesignation.setLectoType(isLectoType);
1079 addTypeDesignation(nameTypeDesignation
, addToAllHomotypicNames
);
1080 return nameTypeDesignation
;
1084 * Creates and adds a new {@link NameTypeDesignation name type designation}
1085 * to <i>this</i> taxon name's set of type designations.
1087 * @param typeSpecies the taxon name to be used as type of <i>this</i> taxon name
1088 * @param citation the reference for this new designation
1089 * @param citationMicroReference the string with the details (generally pages) within the reference
1090 * @param originalNameString the taxon name string used in the reference to assert this designation
1091 * @param status the name type designation status
1092 * @param addToAllHomotypicNames the boolean indicating whether the name type designation should be
1093 * added to all taxon names of the homotypical group this taxon name belongs to
1095 * @see #getNameTypeDesignations()
1096 * @see NameTypeDesignation
1097 * @see TypeDesignationBase#isNotDesignated()
1099 public NameTypeDesignation
addNameTypeDesignation(TaxonNameBase typeSpecies
,
1101 String citationMicroReference
,
1102 String originalNameString
,
1103 NameTypeDesignationStatus status
,
1104 boolean addToAllHomotypicNames
) {
1105 NameTypeDesignation nameTypeDesignation
= new NameTypeDesignation(typeSpecies
, status
, citation
, citationMicroReference
, originalNameString
);
1106 addTypeDesignation(nameTypeDesignation
, addToAllHomotypicNames
);
1107 return nameTypeDesignation
;
1110 //*********************** SPECIMEN TYPE DESIGNATION *********************************************//
1113 * Returns the set of {@link SpecimenTypeDesignation specimen type designations}
1114 * that typify <i>this</i> taxon name.
1117 public Set
<SpecimenTypeDesignation
> getSpecimenTypeDesignations() {
1118 Set
<SpecimenTypeDesignation
> result
= new HashSet
<SpecimenTypeDesignation
>();
1119 for (TypeDesignationBase typeDesignation
: this.typeDesignations
){
1120 if (typeDesignation
instanceof SpecimenTypeDesignation
){
1121 result
.add((SpecimenTypeDesignation
)typeDesignation
);
1129 * Creates and adds a new {@link SpecimenTypeDesignation specimen type designation}
1130 * to <i>this</i> taxon name's set of type designations.
1132 * @param typeSpecimen the specimen to be used as a type for <i>this</i> taxon name
1133 * @param status the specimen type designation status
1134 * @param citation the reference for this new specimen type designation
1135 * @param citationMicroReference the string with the details (generally pages) within the reference
1136 * @param originalNameString the taxon name used in the reference to assert this designation
1137 * @param isNotDesignated the boolean status for a specimen type designation without specimen type
1138 * @param addToAllHomotypicNames the boolean indicating whether the specimen type designation should be
1139 * added to all taxon names of the homotypical group the typified
1140 * taxon name belongs to
1142 * @see #getSpecimenTypeDesignations()
1143 * @see SpecimenTypeDesignationStatus
1144 * @see SpecimenTypeDesignation
1145 * @see TypeDesignationBase#isNotDesignated()
1147 public SpecimenTypeDesignation
addSpecimenTypeDesignation(DerivedUnit typeSpecimen
,
1148 SpecimenTypeDesignationStatus status
,
1149 Reference
<?
> citation
,
1150 String citationMicroReference
,
1151 String originalNameString
,
1152 boolean isNotDesignated
,
1153 boolean addToAllHomotypicNames
) {
1154 SpecimenTypeDesignation specimenTypeDesignation
= new SpecimenTypeDesignation(typeSpecimen
, status
, citation
, citationMicroReference
, originalNameString
, isNotDesignated
);
1155 addTypeDesignation(specimenTypeDesignation
, addToAllHomotypicNames
);
1156 return specimenTypeDesignation
;
1159 //used by merge strategy
1160 private boolean addTypeDesignation(TypeDesignationBase typeDesignation
){
1161 return addTypeDesignation(typeDesignation
, true);
1165 * Adds a {@link TypeDesignationBase type designation} to <code>this</code> taxon name's set of type designations
1167 * @param typeDesignation the typeDesignation to be added to <code>this</code> taxon name
1168 * @param addToAllNames the boolean indicating whether the type designation should be
1169 * added to all taxon names of the homotypical group the typified
1170 * taxon name belongs to
1171 * @return true if the operation was succesful
1173 * @throws IllegalArgumentException if the type designation already has typified names, an {@link IllegalArgumentException exception}
1174 * is thrown. We do this to prevent a type designation to be used for multiple taxon names.
1177 public boolean addTypeDesignation(TypeDesignationBase typeDesignation
, boolean addToAllNames
){
1178 //currently typeDesignations are not persisted with the homotypical group
1179 //so explicit adding to the homotypical group is not necessary.
1180 if (typeDesignation
!= null){
1181 checkHomotypicalGroup(typeDesignation
);
1182 this.typeDesignations
.add(typeDesignation
);
1183 typeDesignation
.addTypifiedName(this);
1186 for (TaxonNameBase taxonName
: this.getHomotypicalGroup().getTypifiedNames()){
1187 if (taxonName
!= this){
1188 taxonName
.addTypeDesignation(typeDesignation
, false);
1197 * Throws an Exception this type designation already has typified names from another homotypical group.
1198 * @param typeDesignation
1200 private void checkHomotypicalGroup(TypeDesignationBase typeDesignation
) {
1201 if(typeDesignation
.getTypifiedNames().size() > 0){
1202 Set
<HomotypicalGroup
> groups
= new HashSet
<HomotypicalGroup
>();
1203 Set
<TaxonNameBase
> names
= typeDesignation
.getTypifiedNames();
1204 for (TaxonNameBase taxonName
: names
){
1205 groups
.add(taxonName
.getHomotypicalGroup());
1207 if (groups
.size() > 1){
1208 throw new IllegalArgumentException("TypeDesignation already has typified names from another homotypical group.");
1215 //*********************** HOMOTYPICAL GROUP *********************************************//
1219 * Returns the {@link HomotypicalGroup homotypical group} to which
1220 * <i>this</i> taxon name belongs. A homotypical group represents all taxon names
1221 * that share the same types.
1223 * @see HomotypicalGroup
1226 public HomotypicalGroup
getHomotypicalGroup() {
1227 return homotypicalGroup
;
1231 * @see #getHomotypicalGroup()
1233 public void setHomotypicalGroup(HomotypicalGroup homotypicalGroup
) {
1234 if (homotypicalGroup
== null){
1235 throw new IllegalArgumentException("HomotypicalGroup of name should never be null but was set to 'null'");
1237 this.homotypicalGroup
= homotypicalGroup
;
1238 if (! homotypicalGroup
.typifiedNames
.contains(this)){
1239 homotypicalGroup
.addTypifiedName(this);
1245 // *************************************************************************//
1248 * Returns the complete string containing the
1249 * {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation() nomenclatural reference citation}
1250 * and the {@link #getNomenclaturalMicroReference() details} assigned to <i>this</i> taxon name.
1252 * @return the string containing the nomenclatural reference of <i>this</i> taxon name
1253 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation()
1254 * @see #getNomenclaturalReference()
1255 * @see #getNomenclaturalMicroReference()
1258 public String
getCitationString(){
1259 return getNomenclaturalReference().getNomenclaturalCitation(getNomenclaturalMicroReference());
1263 * Returns the parsing problems
1267 public List
<ParserProblem
> getParsingProblems(){
1268 return ParserProblem
.warningList(this.parsingProblem
);
1272 * Returns the string containing the publication date (generally only year)
1273 * of the {@link #getNomenclaturalReference() nomenclatural reference} for <i>this</i> taxon name, null if there is
1274 * no nomenclatural reference.
1276 * @return the string containing the publication date of <i>this</i> taxon name
1277 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getYear()
1280 public String
getReferenceYear(){
1281 if (this.getNomenclaturalReference() != null ){
1282 return this.getNomenclaturalReference().getYear();
1289 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1290 * In this context a taxon base means the use of a taxon name by a reference
1291 * either as a {@link eu.etaxonomy.cdm.model.taxon.Taxon taxon} ("accepted/correct" name) or
1292 * as a (junior) {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
1293 * A taxon name can be used by several distinct {@link eu.etaxonomy.cdm.model.reference.Reference references} but only once
1294 * within a taxonomic treatment (identified by one reference).
1297 * @see #getSynonyms()
1299 public Set
<TaxonBase
> getTaxonBases() {
1300 if(taxonBases
== null) {
1301 this.taxonBases
= new HashSet
<TaxonBase
>();
1303 return this.taxonBases
;
1307 * Adds a new {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon base}
1308 * to the set of taxon bases using <i>this</i> taxon name.
1310 * @param taxonBase the taxon base to be added
1311 * @see #getTaxonBases()
1312 * @see #removeTaxonBase(TaxonBase)
1315 public void addTaxonBase(TaxonBase taxonBase
){
1316 Method method
= ReflectionUtils
.findMethod(TaxonBase
.class, "setName", new Class
[] {TaxonNameBase
.class});
1317 ReflectionUtils
.makeAccessible(method
);
1318 ReflectionUtils
.invokeMethod(method
, taxonBase
, new Object
[] {this});
1319 taxonBases
.add(taxonBase
);
1322 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1324 * @param taxonBase the taxon base which should be removed from the corresponding set
1325 * @see #getTaxonBases()
1326 * @see #addTaxonBase(TaxonBase)
1328 public void removeTaxonBase(TaxonBase taxonBase
){
1329 Method method
= ReflectionUtils
.findMethod(TaxonBase
.class, "setName", new Class
[] {TaxonNameBase
.class});
1330 ReflectionUtils
.makeAccessible(method
);
1331 ReflectionUtils
.invokeMethod(method
, taxonBase
, new Object
[] {null});
1337 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Taxon taxa} ("accepted/correct" names according to any
1338 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1339 * the set returned by getTaxonBases().
1341 * @see eu.etaxonomy.cdm.model.taxon.Taxon
1342 * @see #getTaxonBases()
1343 * @see #getSynonyms()
1346 public Set
<Taxon
> getTaxa(){
1347 Set
<Taxon
> result
= new HashSet
<Taxon
>();
1348 for (TaxonBase taxonBase
: this.taxonBases
){
1349 if (taxonBase
instanceof Taxon
){
1350 result
.add((Taxon
)taxonBase
);
1357 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Synonym (junior) synonyms} (according to any
1358 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1359 * the set returned by getTaxonBases().
1361 * @see eu.etaxonomy.cdm.model.taxon.Synonym
1362 * @see #getTaxonBases()
1366 public Set
<Synonym
> getSynonyms() {
1367 Set
<Synonym
> result
= new HashSet
<Synonym
>();
1368 for (TaxonBase taxonBase
: this.taxonBases
){
1369 if (taxonBase
instanceof Synonym
){
1370 result
.add((Synonym
)taxonBase
);
1377 // *********** DESCRIPTIONS *************************************
1380 * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1381 * to <i>this</i> taxon name. A taxon name description is a piece of information
1382 * concerning the taxon name like for instance the content of its first
1383 * publication (protolog) or a picture of this publication.
1385 * @see #addDescription(TaxonNameDescription)
1386 * @see #removeDescription(TaxonNameDescription)
1387 * @see eu.etaxonomy.cdm.model.description.TaxonNameDescription
1389 public Set
<TaxonNameDescription
> getDescriptions() {
1390 return descriptions
;
1394 * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name description}
1395 * to the set of taxon name descriptions assigned to <i>this</i> taxon name. The
1396 * content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute} of the
1397 * taxon name description itself will be replaced with <i>this</i> taxon name.
1399 * @param description the taxon name description to be added
1400 * @see #getDescriptions()
1401 * @see #removeDescription(TaxonNameDescription)
1403 public void addDescription(TaxonNameDescription description
) {
1404 java
.lang
.reflect
.Field field
= ReflectionUtils
.findField(TaxonNameDescription
.class, "taxonName", TaxonNameBase
.class);
1405 ReflectionUtils
.makeAccessible(field
);
1406 ReflectionUtils
.setField(field
, description
, this);
1407 descriptions
.add(description
);
1410 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1411 * to <i>this</i> taxon name. The content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute}
1412 * of the description itself will be set to "null".
1414 * @param description the taxon name description which should be removed
1415 * @see #getDescriptions()
1416 * @see #addDescription(TaxonNameDescription)
1417 * @see eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName()
1419 public void removeDescription(TaxonNameDescription description
) {
1420 java
.lang
.reflect
.Field field
= ReflectionUtils
.findField(TaxonNameDescription
.class, "taxonName", TaxonNameBase
.class);
1421 ReflectionUtils
.makeAccessible(field
);
1422 ReflectionUtils
.setField(field
, description
, null);
1423 descriptions
.remove(description
);
1426 // *********** HOMOTYPIC GROUP METHODS **************************************************
1429 public void mergeHomotypicGroups(TaxonNameBase name
){
1430 this.getHomotypicalGroup().merge(name
.getHomotypicalGroup());
1431 //HomotypicalGroup thatGroup = name.homotypicalGroup;
1432 name
.setHomotypicalGroup(this.homotypicalGroup
);
1436 * Returns the boolean value indicating whether a given taxon name belongs
1437 * to the same {@link HomotypicalGroup homotypical group} as <i>this</i> taxon name (true)
1438 * or not (false). Returns "true" only if the homotypical groups of both
1439 * taxon names exist and if they are identical.
1441 * @param homoTypicName the taxon name the homotypical group of which is to be checked
1442 * @return the boolean value of the check
1443 * @see HomotypicalGroup
1446 public boolean isHomotypic(TaxonNameBase homoTypicName
) {
1447 if (homoTypicName
== null) {
1450 HomotypicalGroup homotypicGroup
= homoTypicName
.getHomotypicalGroup();
1451 if (homotypicGroup
== null || this.getHomotypicalGroup() == null) {
1454 if (homotypicGroup
.equals(this.getHomotypicalGroup())) {
1462 * Checks whether name is a basionym for ALL names
1463 * in its homotypical group.
1464 * Returns <code>false</code> if there are no other names in the group
1469 public boolean isGroupsBasionym() {
1470 if (homotypicalGroup
== null){
1471 homotypicalGroup
= HomotypicalGroup
.NewInstance();
1472 homotypicalGroup
.addTypifiedName(this);
1474 Set
<TaxonNameBase
> typifiedNames
= homotypicalGroup
.getTypifiedNames();
1476 // Check whether there are any other names in the group
1477 if (typifiedNames
.size() == 1) {
1481 boolean isBasionymToAll
= true;
1483 for (TaxonNameBase taxonName
: typifiedNames
) {
1484 if (!taxonName
.equals(this)) {
1485 if (! isBasionymFor(taxonName
)) {
1494 * Checks whether a basionym relationship exists between fromName and toName.
1501 public boolean isBasionymFor(TaxonNameBase newCombinationName
) {
1502 Set
<NameRelationship
> relations
= newCombinationName
.getRelationsToThisName();
1503 for (NameRelationship relation
: relations
) {
1504 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) &&
1505 relation
.getFromName().equals(this)) {
1513 * Creates a basionym relationship to all other names in this names homotypical
1516 * @see HomotypicalGroup.setGroupBasionym(TaxonNameBase basionymName)
1520 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup#setGroupBasionym(TaxonNameBase)
1523 public void makeGroupsBasionym() {
1524 this.homotypicalGroup
.setGroupBasionym(this);
1528 //********* Rank comparison shortcuts ********************//
1530 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1531 * taxon name is higher than the genus rank (true) or not (false).
1532 * Suprageneric non viral names are monomials.
1533 * Returns false if rank is null.
1536 * @see #isInfraGeneric()
1538 * @see #isInfraSpecific()
1541 public boolean isSupraGeneric() {
1545 return getRank().isSupraGeneric();
1548 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1549 * taxon name is the genus rank (true) or not (false). Non viral names with
1550 * genus rank are monomials. Returns false if rank is null.
1552 * @see #isSupraGeneric()
1553 * @see #isInfraGeneric()
1555 * @see #isInfraSpecific()
1558 public boolean isGenus() {
1562 return getRank().isGenus();
1565 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1566 * taxon name is higher than the species rank and lower than the
1567 * genus rank (true) or not (false). Infrageneric non viral names are
1568 * binomials. Returns false if rank is null.
1570 * @see #isSupraGeneric()
1573 * @see #isInfraSpecific()
1576 public boolean isInfraGeneric() {
1580 return getRank().isInfraGeneric();
1584 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1585 * taxon name is higher than the species rank (true) or not (false).
1586 * Returns false if rank is null.
1589 * @see #isInfraGeneric()
1591 * @see #isInfraSpecific()
1594 public boolean isSupraSpecific(){
1598 return getRank().isHigher(Rank
.SPECIES());
1602 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1603 * taxon name is the species rank (true) or not (false). Non viral names
1604 * with species rank are binomials.
1605 * Returns false if rank is null.
1607 * @see #isSupraGeneric()
1609 * @see #isInfraGeneric()
1610 * @see #isInfraSpecific()
1613 public boolean isSpecies() {
1617 return getRank().isSpecies();
1620 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1621 * taxon name is lower than the species rank (true) or not (false).
1622 * Infraspecific non viral names are trinomials.
1623 * Returns false if rank is null.
1625 * @see #isSupraGeneric()
1627 * @see #isInfraGeneric()
1631 public boolean isInfraSpecific() {
1635 return getRank().isInfraSpecific();
1639 * Returns true if this name's rank indicates a rank that aggregates species like species
1640 * aggregates or species groups, false otherwise. This methods currently returns false
1641 * for all user defined ranks.
1643 *@see Rank#isSpeciesAggregate()
1648 public boolean isSpeciesAggregate() {
1652 return getRank().isSpeciesAggregate();
1657 * Returns null as the {@link NomenclaturalCode nomenclatural code} that governs
1658 * the construction of <i>this</i> taxon name since there is no specific
1659 * nomenclatural code defined. The real implementention takes place in the
1660 * subclasses {@link ViralName ViralName}, {@link BacterialName BacterialName},
1661 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
1662 * {@link ZoologicalName ZoologicalName}. Each taxon name is governed by one
1663 * and only one nomenclatural code.
1666 * @see #isCodeCompliant()
1667 * @see #getHasProblem()
1669 abstract public NomenclaturalCode
getNomenclaturalCode();
1672 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1675 * Generates and returns the string with the scientific name of <i>this</i>
1676 * taxon name (only non viral taxon names can be generated from their
1677 * components). This string may be stored in the inherited
1678 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
1679 * This method overrides the generic and inherited
1680 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() method} from
1681 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
1683 * @return the string with the composed name of this non viral taxon name with authorship (and maybe year)
1684 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1685 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
1688 // public abstract String generateTitle();
1691 * Creates a basionym relationship between this name and
1692 * each name in its homotypic group.
1694 * @param basionymName
1697 public void setAsGroupsBasionym() {
1700 HomotypicalGroup homotypicalGroup
= this.getHomotypicalGroup();
1702 if (homotypicalGroup
== null) {
1706 Set
<NameRelationship
> relations
= new HashSet
<NameRelationship
>();
1707 Set
<NameRelationship
> removeRelations
= new HashSet
<NameRelationship
>();
1709 for(TaxonNameBase
<?
, ?
> typifiedName
: homotypicalGroup
.getTypifiedNames()){
1711 Set
<NameRelationship
> nameRelations
= typifiedName
.getRelationsFromThisName();
1713 for(NameRelationship nameRelation
: nameRelations
){
1714 relations
.add(nameRelation
);
1718 for (NameRelationship relation
: relations
) {
1720 // If this is a basionym relation, and toName is in the homotypical group,
1721 // remove the relationship.
1722 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) &&
1723 relation
.getToName().getHomotypicalGroup().equals(homotypicalGroup
)) {
1724 removeRelations
.add(relation
);
1728 // Removing relations from a set through which we are iterating causes a
1729 // ConcurrentModificationException. Therefore, we delete the targeted
1730 // relations in a second step.
1731 for (NameRelationship relation
: removeRelations
) {
1732 this.removeNameRelationship(relation
);
1736 for (TaxonNameBase
<?
, ?
> name
: homotypicalGroup
.getTypifiedNames()) {
1737 if (!name
.equals(this)) {
1739 // First check whether the relationship already exists
1740 if (!this.isBasionymFor(name
)) {
1743 name
.addRelationshipFromName(this,
1744 NameRelationshipType
.BASIONYM(), null);
1751 * Removes basionym relationship between this name and
1752 * each name in its homotypic group.
1754 * @param basionymName
1757 public void removeAsGroupsBasionym() {
1759 HomotypicalGroup homotypicalGroup
= this.getHomotypicalGroup();
1761 if (homotypicalGroup
== null) {
1765 Set
<NameRelationship
> relations
= new HashSet
<NameRelationship
>();
1766 Set
<NameRelationship
> removeRelations
= new HashSet
<NameRelationship
>();
1768 for(TaxonNameBase
<?
, ?
> typifiedName
: homotypicalGroup
.getTypifiedNames()){
1770 Set
<NameRelationship
> nameRelations
= typifiedName
.getRelationsFromThisName();
1772 for(NameRelationship nameRelation
: nameRelations
){
1773 relations
.add(nameRelation
);
1777 for (NameRelationship relation
: relations
) {
1779 // If this is a basionym relation, and toName is in the homotypical group,
1780 // and fromName is basionymName, remove the relationship.
1781 if (relation
.getType().equals(NameRelationshipType
.BASIONYM()) &&
1782 relation
.getFromName().equals(this) &&
1783 relation
.getToName().getHomotypicalGroup().equals(homotypicalGroup
)) {
1784 removeRelations
.add(relation
);
1788 // Removing relations from a set through which we are iterating causes a
1789 // ConcurrentModificationException. Therefore, we delete the targeted
1790 // relations in a second step.
1791 for (NameRelationship relation
: removeRelations
) {
1792 this.removeNameRelationship(relation
);
1796 //*********************** CLONE ********************************************************/
1799 * Clones <i>this</i> taxon name. This is a shortcut that enables to create
1800 * a new instance that differs only slightly from <i>this</i> taxon name by
1801 * modifying only some of the attributes.<BR><BR>
1802 * Usages of this name in a taxon concept are <b>not</b> cloned.<BR>
1803 * <b>The name gets a newly created homotypical group</b><BR>
1804 * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1805 * {@link TaxonNameDescription Name descriptions} are cloned and not reused.<BR>
1806 * {@link TypeDesignationBase Type designations} are cloned and not reused.<BR>
1808 * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1809 * @see java.lang.Object#clone()
1812 public Object
clone() {
1813 TaxonNameBase result
;
1815 result
= (TaxonNameBase
)super.clone();
1817 //taxonBases -> empty
1818 result
.taxonBases
= new HashSet
<TaxonBase
>();
1821 if (! protectedFullTitleCache
){
1822 result
.fullTitleCache
= null;
1826 result
.descriptions
= new HashSet
<TaxonNameDescription
>();
1827 for (TaxonNameDescription taxonNameDescription
: getDescriptions()){
1828 TaxonNameDescription newDescription
= (TaxonNameDescription
)taxonNameDescription
.clone();
1829 result
.descriptions
.add(newDescription
);
1833 result
.status
= new HashSet
<NomenclaturalStatus
>();
1834 for (NomenclaturalStatus nomenclaturalStatus
: getStatus()){
1835 NomenclaturalStatus newStatus
= (NomenclaturalStatus
)nomenclaturalStatus
.clone();
1836 result
.status
.add(newStatus
);
1841 result
.relationsToThisName
= new HashSet
<NameRelationship
>();
1842 for (NameRelationship toRelationship
: getRelationsToThisName()){
1843 NameRelationship newRelationship
= (NameRelationship
)toRelationship
.clone();
1844 newRelationship
.setRelatedTo(result
);
1845 result
.relationsToThisName
.add(newRelationship
);
1849 result
.relationsFromThisName
= new HashSet
<NameRelationship
>();
1850 for (NameRelationship fromRelationship
: getRelationsFromThisName()){
1851 NameRelationship newRelationship
= (NameRelationship
)fromRelationship
.clone();
1852 newRelationship
.setRelatedFrom(result
);
1853 result
.relationsFromThisName
.add(newRelationship
);
1857 result
.typeDesignations
= new HashSet
<TypeDesignationBase
>();
1858 for (TypeDesignationBase typeDesignation
: getTypeDesignations()){
1859 TypeDesignationBase newDesignation
= (TypeDesignationBase
)typeDesignation
.clone();
1860 result
.typeDesignations
.add(newDesignation
);
1861 newDesignation
.addTypifiedName(result
);
1865 //TODO still needs to be discussed
1866 homotypicalGroup
= HomotypicalGroup
.NewInstance();
1867 homotypicalGroup
.addTypifiedName(this);
1869 //no changes to: appendedPharse, nomenclaturalReference,
1870 //nomenclaturalMicroReference, parsingProblem, problemEnds, problemStarts
1871 //protectedFullTitleCache, rank
1873 } catch (CloneNotSupportedException e
) {
1874 logger
.warn("Object does not implement cloneable");
1875 e
.printStackTrace();