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
.taxon
;
13 import java
.lang
.reflect
.Field
;
14 import java
.util
.ArrayList
;
15 import java
.util
.Collections
;
16 import java
.util
.Comparator
;
17 import java
.util
.HashMap
;
18 import java
.util
.HashSet
;
19 import java
.util
.Iterator
;
20 import java
.util
.List
;
24 import javax
.persistence
.Entity
;
25 import javax
.persistence
.FetchType
;
26 import javax
.persistence
.OneToMany
;
27 import javax
.persistence
.Transient
;
28 import javax
.validation
.Valid
;
29 import javax
.validation
.constraints
.NotNull
;
30 import javax
.xml
.bind
.annotation
.XmlAccessType
;
31 import javax
.xml
.bind
.annotation
.XmlAccessorType
;
32 import javax
.xml
.bind
.annotation
.XmlAttribute
;
33 import javax
.xml
.bind
.annotation
.XmlElement
;
34 import javax
.xml
.bind
.annotation
.XmlElementWrapper
;
35 import javax
.xml
.bind
.annotation
.XmlIDREF
;
36 import javax
.xml
.bind
.annotation
.XmlRootElement
;
37 import javax
.xml
.bind
.annotation
.XmlSchemaType
;
38 import javax
.xml
.bind
.annotation
.XmlType
;
40 import org
.apache
.log4j
.Logger
;
41 import org
.hibernate
.annotations
.Cascade
;
42 import org
.hibernate
.annotations
.CascadeType
;
43 import org
.hibernate
.envers
.Audited
;
44 import org
.hibernate
.search
.annotations
.ClassBridge
;
45 import org
.hibernate
.search
.annotations
.ClassBridges
;
46 import org
.hibernate
.search
.annotations
.ContainedIn
;
47 import org
.hibernate
.search
.annotations
.Indexed
;
48 import org
.hibernate
.search
.annotations
.IndexedEmbedded
;
49 import org
.springframework
.beans
.factory
.annotation
.Configurable
;
50 import org
.springframework
.util
.ReflectionUtils
;
52 import eu
.etaxonomy
.cdm
.hibernate
.search
.GroupByTaxonClassBridge
;
53 import eu
.etaxonomy
.cdm
.hibernate
.search
.TaxonRelationshipClassBridge
;
54 import eu
.etaxonomy
.cdm
.model
.common
.IRelated
;
55 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
56 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
57 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionType
;
58 import eu
.etaxonomy
.cdm
.model
.description
.IDescribable
;
59 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
60 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
61 import eu
.etaxonomy
.cdm
.model
.name
.ITaxonNameBase
;
62 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
63 import eu
.etaxonomy
.cdm
.model
.reference
.ICdmTarget
;
64 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
65 import eu
.etaxonomy
.cdm
.strategy
.cache
.taxon
.ITaxonCacheStrategy
;
66 import eu
.etaxonomy
.cdm
.strategy
.cache
.taxon
.TaxonBaseDefaultCacheStrategy
;
69 * The class for "accepted/correct" {@link TaxonBase taxa} (only these taxa according to
70 * the opinion of the {@link eu.etaxonomy.cdm.model.reference.Reference reference} can build a classification).
71 * An {@link java.lang.Iterable interface} is supported to iterate through taxonomic children.<BR>
72 * Splitting taxa in "accepted/correct" and {@link Synonym "synonyms"} makes it easier to handle
73 * particular relationships between ("accepted/correct") taxa on the one hand
74 * and between ("synonym") taxa and ("accepted/correct") taxa on the other.
77 * @since 08-Nov-2007 13:06:56
79 @XmlAccessorType(XmlAccessType
.FIELD
)
80 @XmlType(name
= "Taxon", propOrder
= {
83 "relationsFromThisTaxon",
84 "relationsToThisTaxon",
87 @XmlRootElement(name
= "Taxon")
89 @Indexed(index
= "eu.etaxonomy.cdm.model.taxon.TaxonBase")
93 @ClassBridge(impl
= GroupByTaxonClassBridge
.class),
94 @ClassBridge(impl
= TaxonRelationshipClassBridge
.class)
97 extends TaxonBase
<ITaxonCacheStrategy
<Taxon
>>
98 implements IRelated
<RelationshipBase
>, IDescribable
<TaxonDescription
>, ICdmTarget
{
100 private static final long serialVersionUID
= -584946869762749006L;
101 private static final Logger logger
= Logger
.getLogger(Taxon
.class);
103 private static final TaxonComparator defaultTaxonComparator
= new TaxonComparator();
105 @XmlElementWrapper(name
= "Descriptions")
106 @XmlElement(name
= "Description")
107 @OneToMany(mappedBy
="taxon", fetch
= FetchType
.LAZY
)
108 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
111 private Set
<TaxonDescription
> descriptions
= new HashSet
<>();
113 // all related synonyms
114 @XmlElementWrapper(name
= "Synonyms")
115 @XmlElement(name
= "Synonym")
117 @XmlSchemaType(name
= "IDREF")
118 @OneToMany(mappedBy
="acceptedTaxon", fetch
=FetchType
.LAZY
, orphanRemoval
=false) //we allow synonyms to stay on their own for dirty data and for intermediate states during e.g. imports
119 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
123 private Set
<Synonym
> synonyms
= new HashSet
<>();
125 // all taxa relations with rel.fromTaxon==this
126 @XmlElementWrapper(name
= "RelationsFromThisTaxon")
127 @XmlElement(name
= "FromThisTaxonRelationship")
128 @OneToMany(mappedBy
="relatedFrom", fetch
=FetchType
.LAZY
, orphanRemoval
=true)
129 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
133 private Set
<TaxonRelationship
> relationsFromThisTaxon
= new HashSet
<>();
135 // all taxa relations with rel.toTaxon==this
136 @XmlElementWrapper(name
= "RelationsToThisTaxon")
137 @XmlElement(name
= "ToThisTaxonRelationship")
139 @XmlSchemaType(name
= "IDREF")
140 @OneToMany(mappedBy
="relatedTo", fetch
=FetchType
.LAZY
, orphanRemoval
=true)
141 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
144 private Set
<TaxonRelationship
> relationsToThisTaxon
= new HashSet
<>();
146 @XmlAttribute(name
= "taxonStatusUnknown")
147 private boolean taxonStatusUnknown
= false;
149 * The status of this taxon is unknown it could also be some kind of synonym.
150 * @return the taxonStatusUnknown
152 public boolean isTaxonStatusUnknown() {return taxonStatusUnknown
;}
153 /** @see #isTaxonStatusUnknown()*/
154 public void setTaxonStatusUnknown(boolean taxonStatusUnknown
) {this.taxonStatusUnknown
= taxonStatusUnknown
;}
156 @XmlElementWrapper(name
= "taxonNodes")
157 @XmlElement(name
= "taxonNode")
159 @XmlSchemaType(name
= "IDREF")
160 @OneToMany(mappedBy
="taxon", fetch
=FetchType
.LAZY
)
161 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
163 private Set
<TaxonNode
> taxonNodes
= new HashSet
<>();
165 // ************************* FACTORY METHODS ********************************/
168 * @see #NewInstance(TaxonName, Reference)
173 public static Taxon
NewInstance(ITaxonNameBase taxonName
, Reference sec
){
174 return NewInstance(TaxonName
.castAndDeproxy(taxonName
), sec
);
178 * Creates a new (accepted/valid) taxon instance with
179 * the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
182 * @param taxonName the taxon name used
183 * @param sec the reference using the taxon name
184 * @see #Taxon(TaxonName, Reference)
186 public static Taxon
NewInstance(TaxonName taxonName
, Reference sec
){
187 Taxon result
= new Taxon(taxonName
, sec
);
192 * Creates a new Taxon for the given name, secundum reference and secundum detail
195 * @param secMicroReference
198 public static Taxon
NewInstance(TaxonName taxonName
, Reference sec
, String secMicroReference
){
199 Taxon result
= new Taxon(taxonName
, sec
, secMicroReference
);
204 * Creates a new taxon instance with an unknown status (accepted/synonym) and with
205 * the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
208 * @param taxonName the taxon name used
209 * @param sec the reference using the taxon name
210 * @see #Taxon(TaxonName, Reference)
212 public static Taxon
NewUnknownStatusInstance(TaxonName taxonName
, Reference sec
){
213 Taxon result
= new Taxon(taxonName
, sec
);
214 result
.setTaxonStatusUnknown(true);
217 // ************* CONSTRUCTORS *************/
219 //TODO should be private, but still produces Spring init errors
222 this.cacheStrategy
= new TaxonBaseDefaultCacheStrategy
<>();
225 private Taxon(TaxonName taxonName
, Reference sec
){
226 super(taxonName
, sec
, null);
227 this.cacheStrategy
= new TaxonBaseDefaultCacheStrategy
<>();
230 private Taxon(TaxonName taxonName
, Reference sec
, String secMicroReference
){
231 super(taxonName
, sec
, secMicroReference
);
232 this.cacheStrategy
= new TaxonBaseDefaultCacheStrategy
<Taxon
>();
235 //********* METHODS **************************************/
238 * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon descriptions}
239 * concerning <i>this</i> taxon.
241 * @see #removeDescription(TaxonDescription)
242 * @see #addDescription(TaxonDescription)
243 * @see eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
246 public Set
<TaxonDescription
> getDescriptions() {
247 if(descriptions
== null) {
248 descriptions
= new HashSet
<>();
253 public Set
<TaxonDescription
> getDescriptions(DescriptionType type
) {
254 Set
<TaxonDescription
> result
= new HashSet
<>();
255 for (TaxonDescription description
: getDescriptions()){
256 if (description
.getTypes().contains(type
)){
257 result
.add(description
);
264 * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon description} to the set
265 * of taxon descriptions assigned to <i>this</i> (accepted/correct) taxon.
266 * Due to bidirectionality the content of the {@link eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon() taxon attribute} of the
267 * taxon description itself will be replaced with <i>this</i> taxon. The taxon
268 * description will also be removed from the set of taxon descriptions
269 * assigned to its previous taxon.
271 * @param description the taxon description to be added for <i>this</i> taxon
272 * @see #getDescriptions()
273 * @see #removeDescription(TaxonDescription)
274 * @see eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
277 public void addDescription(TaxonDescription description
) {
278 if (description
.getTaxon() != null){
279 description
.getTaxon().removeDescription(description
);
281 Field field
= ReflectionUtils
.findField(TaxonDescription
.class, "taxon", Taxon
.class);
282 ReflectionUtils
.makeAccessible(field
);
283 ReflectionUtils
.setField(field
, description
, this);
284 descriptions
.add(description
);
289 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon descriptions} assigned
290 * to <i>this</i> (accepted/correct) taxon. Due to bidirectionality the content of
291 * the {@link eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon() taxon attribute} of the taxon description
292 * itself will be set to "null".
294 * @param description the taxon description which should be removed
295 * @see #getDescriptions()
296 * @see #addDescription(TaxonDescription)
297 * @see eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
300 public void removeDescription(TaxonDescription description
) {
301 //description.setTaxon(null) for not visible method
302 Field field
= ReflectionUtils
.findField(TaxonDescription
.class, "taxon", Taxon
.class);
303 ReflectionUtils
.makeAccessible(field
);
304 ReflectionUtils
.setField(field
, description
, null);
305 descriptions
.remove(description
);
308 public void removeDescription(TaxonDescription description
, boolean removeElements
){
310 Set
<DescriptionElementBase
> elements
= new HashSet
<DescriptionElementBase
>(description
.getElements());
311 for (DescriptionElementBase el
:elements
){
312 description
.getElements().remove(el
);
314 removeDescription(description
);
316 removeDescription(description
);
321 * Returns the image gallery for a taxon. If there are multiple taxon descriptions
322 * marked as image galleries an arbitrary one is chosen.
323 * If no image gallery exists, a new one is created if <code>createNewIfNotExists</code>
324 * is <code>true</code>.
325 * @param createNewIfNotExists
328 public TaxonDescription
getImageGallery(boolean createNewIfNotExists
) {
329 TaxonDescription result
= null;
330 Set
<TaxonDescription
> descriptions
= getDescriptions();
331 for (TaxonDescription description
: descriptions
){
332 if (description
.isImageGallery()){
333 result
= description
;
337 if (result
== null && createNewIfNotExists
){
338 result
= TaxonDescription
.NewInstance(this);
339 result
.setImageGallery(true);
346 public Set
<TaxonNode
> getTaxonNodes() {
349 // protected void setTaxonNodes(Set<TaxonNode> taxonNodes) {
350 // this.taxonNodes = taxonNodes;
352 protected void addTaxonNode(TaxonNode taxonNode
){
353 taxonNodes
.add(taxonNode
);
356 public boolean removeTaxonNode(TaxonNode taxonNode
){
357 if (!taxonNodes
.contains(taxonNode
)){
360 TaxonNode parent
= taxonNode
.getParent();
362 parent
.removeChildNode(taxonNode
);
364 taxonNode
.setTaxon(null);
365 return taxonNodes
.remove(taxonNode
);
369 public boolean removeTaxonNode(TaxonNode taxonNode
, boolean deleteChildren
){
370 TaxonNode parent
= taxonNode
.getParent();
371 boolean success
= true;
373 if ((!taxonNode
.getChildNodes().isEmpty() && deleteChildren
) || (taxonNode
.getChildNodes().isEmpty()) ){
377 } else if (!taxonNode
.isTopmostNode()){
379 List
<TaxonNode
> nodes
= new ArrayList
<TaxonNode
> (taxonNode
.getChildNodes());
380 for (TaxonNode childNode
: nodes
){
381 taxonNode
.getChildNodes().remove(childNode
);
382 parent
.addChildNode(childNode
, null, null);
387 } else if (taxonNode
.isTopmostNode()){
393 public boolean removeTaxonNodes(boolean deleteChildren
){
394 Iterator
<TaxonNode
> nodesIterator
= taxonNodes
.iterator();
397 boolean success
= false;
398 List
<TaxonNode
> removeNodes
= new ArrayList
<>();
399 while (nodesIterator
.hasNext()){
400 node
= nodesIterator
.next();
401 if (!deleteChildren
){
402 List
<TaxonNode
> children
= node
.getChildNodes();
403 Iterator
<TaxonNode
> childrenIterator
= children
.iterator();
404 parent
= node
.getParent();
405 while (childrenIterator
.hasNext()){
406 TaxonNode childNode
= childrenIterator
.next();
408 parent
.addChildNode(childNode
, null, null);
410 childNode
.setParent(null);
414 for (int i
= 0; i
<node
.getChildNodes().size(); i
++){
419 removeNodes
.add(node
);
421 for (int i
= 0; i
<removeNodes
.size(); i
++){
422 TaxonNode removeNode
= removeNodes
.get(i
);
423 success
= removeNode
.delete(deleteChildren
);
424 removeNode
.setTaxon(null);
425 removeTaxonNode(removeNode
);
431 public TaxonNode
getTaxonNode(Classification classification
) {
432 if (classification
== null){
435 for (TaxonNode node
: this.getTaxonNodes()){
436 if (classification
.equals(node
.getClassification())){
444 * Returns the set of all {@link Synonym synonyms}
445 * for which <i>this</i> ("accepted/valid") taxon is the accepted taxon.
447 * @see #addSynonym(Synonym, SynonymType)
448 * @see #removeSynonym(Synonym)
450 public Set
<Synonym
> getSynonyms() {
451 if(synonyms
== null) {
452 this.synonyms
= new HashSet
<>();
458 * Returns the set of all {@link TaxonRelationship taxon relationships}
459 * between two taxa in which <i>this</i> taxon is involved as a source.
461 * @see #getRelationsToThisTaxon()
462 * @see #getTaxonRelations()
464 public Set
<TaxonRelationship
> getRelationsFromThisTaxon() {
465 if(relationsFromThisTaxon
== null) {
466 this.relationsFromThisTaxon
= new HashSet
<>();
468 return relationsFromThisTaxon
;
472 * Returns the set of all {@link TaxonRelationship taxon relationships}
473 * between two taxa in which <i>this</i> taxon is involved as a target.
475 * @see #getRelationsFromThisTaxon()
476 * @see #getTaxonRelations()
478 public Set
<TaxonRelationship
> getRelationsToThisTaxon() {
479 if(relationsToThisTaxon
== null) {
480 this.relationsToThisTaxon
= new HashSet
<>();
482 return relationsToThisTaxon
;
486 * Returns the set of all {@link TaxonRelationship taxon relationships}
487 * between two taxa in which <i>this</i> taxon is involved either as a source or
490 * @see #getRelationsFromThisTaxon()
491 * @see #getRelationsToThisTaxon()
494 public Set
<TaxonRelationship
> getTaxonRelations() {
495 Set
<TaxonRelationship
> rels
= new HashSet
<>();
496 rels
.addAll(getRelationsToThisTaxon());
497 rels
.addAll(getRelationsFromThisTaxon());
502 * @see #getRelationsToThisTaxon()
504 protected void setRelationsToThisTaxon(Set
<TaxonRelationship
> relationsToThisTaxon
) {
505 this.relationsToThisTaxon
= relationsToThisTaxon
;
509 * @see #getRelationsFromThisTaxon()
511 protected void setRelationsFromThisTaxon(Set
<TaxonRelationship
> relationsFromThisTaxon
) {
512 this.relationsFromThisTaxon
= relationsFromThisTaxon
;
516 * If a relationships between <i>this</i> and the given taxon exists they will be returned.
517 * <i>This</i> taxon is involved either as a source or as a target in the relationships.
518 * The method will return <code>null</code> if no relations exist between the two taxa.
520 * @param possiblyRelatedTaxon
521 * a taxon to check for a relationship
523 * a set of <code>TaxonRelationship</code>s or <code>null</null> if none exists.
525 public Set
<TaxonRelationship
> getTaxonRelations(Taxon possiblyRelatedTaxon
){
526 Set
<TaxonRelationship
> relations
= new HashSet
<>();
528 for(TaxonRelationship relationship
: getTaxonRelations()){
529 if(relationship
.getFromTaxon().equals(possiblyRelatedTaxon
)) {
530 relations
.add(relationship
);
532 if(relationship
.getToTaxon().equals(possiblyRelatedTaxon
)) {
533 relations
.add(relationship
);
537 return relations
.size() > 0 ? relations
: null;
541 * Removes one {@link TaxonRelationship taxon relationship} from one of both sets of
542 * {@link #getTaxonRelations() taxon relationships} in which <i>this</i> taxon is involved
543 * either as a {@link #getRelationsFromThisTaxon() source} or as a {@link #getRelationsToThisTaxon() target}.
544 * The taxon relationship will also be removed from one of both sets
545 * belonging to the second taxon involved. Furthermore the inherited RelatedFrom and
546 * RelatedTo attributes of the given taxon relationship will be nullified.<P>
548 * @param rel the taxon relationship which should be removed from one
550 * @see #getTaxonRelations()
551 * @see eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom()
552 * @see eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo()
555 public void removeTaxonRelation(TaxonRelationship rel
) {
556 this.relationsToThisTaxon
.remove(rel
);
557 this.relationsFromThisTaxon
.remove(rel
);
558 Taxon fromTaxon
= rel
.getFromTaxon();
559 Taxon toTaxon
= rel
.getToTaxon();
561 //delete Relationship from other related Taxon
562 if (fromTaxon
!= this){
563 rel
.setToTaxon(null); //remove this Taxon from relationship
564 if (fromTaxon
!= null){
565 if (fromTaxon
.getTaxonRelations().contains(rel
)){
566 fromTaxon
.removeTaxonRelation(rel
);
570 if (toTaxon
!= this ){
571 rel
.setFromTaxon(null); //remove this Taxon from relationship
572 if (toTaxon
!= null){
573 if (toTaxon
.getTaxonRelations().contains(rel
)) {
574 toTaxon
.removeTaxonRelation(rel
);
581 * Adds an existing {@link TaxonRelationship taxon relationship} either to the set of
582 * {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon} or to the set of
583 * {@link #getRelationsFromThisTaxon() taxon relationships from <i>this</i> taxon}. If neither the
584 * source nor the target of the taxon relationship match with <i>this</i> taxon
585 * no addition will be carried out. The taxon relationship will also be
586 * added to the second taxon involved in the given relationship.<P>
588 * @param rel the taxon relationship to be added to one of <i>this</i> taxon's taxon relationships sets
589 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
590 * @see #getTaxonRelations()
591 * @see #getRelationsFromThisTaxon()
592 * @see #getRelationsToThisTaxon()
594 public void addTaxonRelation(TaxonRelationship rel
) {
595 if (rel
!=null && rel
.getType()!=null && !getTaxonRelations().contains(rel
) ){
596 Taxon toTaxon
=rel
.getToTaxon();
597 Taxon fromTaxon
=rel
.getFromTaxon();
598 if ( this.equals(toTaxon
) || this.equals(fromTaxon
) ){
599 if (this.equals(fromTaxon
)){
600 relationsFromThisTaxon
.add(rel
);
601 // also add relation to other taxon object
603 toTaxon
.addTaxonRelation(rel
);
605 }else if (this.equals(toTaxon
)){
606 relationsToThisTaxon
.add(rel
);
607 // also add relation to other taxon object
608 if (fromTaxon
!=null){
609 fromTaxon
.addTaxonRelation(rel
);
612 }else if (toTaxon
== null || fromTaxon
== null){
613 if (toTaxon
== null){
615 relationsToThisTaxon
.add(rel
);
616 if (fromTaxon
!= null){
617 fromTaxon
.addTaxonRelation(rel
);
619 }else if (fromTaxon
== null && toTaxon
!= null){
621 relationsFromThisTaxon
.add(rel
);
622 toTaxon
.addTaxonRelation(rel
);
629 @Deprecated //for inner use by RelationshipBase only
630 public void addRelationship(RelationshipBase rel
){
631 if (rel
instanceof TaxonRelationship
){
632 addTaxonRelation((TaxonRelationship
)rel
);
634 throw new ClassCastException("Wrong Relationsship type for Taxon.addRelationship");
639 * Creates a new {@link TaxonRelationship taxon relationship} instance where <i>this</i> taxon
640 * plays the source role and adds it to the set of
641 * {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to <i>this</i> taxon.
642 * The taxon relationship will also be added to the set of taxon
643 * relationships to the second taxon involved in the created relationship.<P>
645 * @param toTaxon the taxon which plays the target role in the new taxon relationship
646 * @param type the taxon relationship type for the new taxon relationship
647 * @param citation the reference source for the new taxon relationship
648 * @param microcitation the string with the details describing the exact localisation within the reference
650 * @see #addTaxonRelation(TaxonRelationship)
651 * @see #getTaxonRelations()
652 * @see #getRelationsFromThisTaxon()
653 * @see #getRelationsToThisTaxon()
655 public TaxonRelationship
addTaxonRelation(Taxon toTaxon
, TaxonRelationshipType type
, Reference citation
, String microcitation
) {
656 return new TaxonRelationship(this, toTaxon
, type
, citation
, microcitation
);
660 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
661 * "misapplied name for") instance where <i>this</i> taxon plays the target role
662 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
663 * The taxon relationship will also be added to the set of taxon
664 * relationships to the other (misapplied name) taxon involved in the created relationship.
666 * @param misappliedNameTaxon the taxon which plays the source role in the new taxon relationship
667 * @param citation the reference source for the new taxon relationship
668 * @param microcitation the string with the details describing the exact localisation within the reference
670 * @see #getMisappliedNames()
671 * @see #addProParteMisappliedName(Taxon, Reference, String)
672 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
673 * @see #addTaxonRelation(TaxonRelationship)
674 * @see #getTaxonRelations()
675 * @see #getRelationsFromThisTaxon()
676 * @see #getRelationsToThisTaxon()
678 public TaxonRelationship
addMisappliedName(Taxon misappliedNameTaxon
, Reference citation
, String microcitation
) {
679 return misappliedNameTaxon
.addTaxonRelation(this, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), citation
, microcitation
);
682 // public void removeMisappliedName(Taxon misappliedNameTaxon){
683 // Set<TaxonRelationship> taxRels = this.getTaxonRelations();
684 // for (TaxonRelationship taxRel : taxRels ){
685 // if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())
686 // && taxRel.getFromTaxon().equals(misappliedNameTaxon)){
687 // this.removeTaxonRelation(taxRel);
693 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
694 * "pro parte misapplied name for") instance where <i>this</i> taxon plays the target role
695 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
696 * The taxon relationship will also be added to the set of taxon
697 * relationships to the other (pro parte misapplied name) taxon involved in the created relationship.
699 * @param proParteMisappliedNameTaxon the taxon which plays the source role in the new taxon relationship
700 * @param citation the reference source for the new taxon relationship
701 * @param microcitation the string with the details describing the exact localisation within the reference
703 * @see #addMisappliedName(Taxon, Reference, String)
704 * @see #getMisappliedNames()
705 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
706 * @see #addTaxonRelation(TaxonRelationship)
707 * @see #getTaxonRelations()
708 * @see #getRelationsFromThisTaxon()
709 * @see #getRelationsToThisTaxon()
711 public TaxonRelationship
addProParteMisappliedName(Taxon proParteMisappliedNameTaxon
, Reference citation
, String microcitation
) {
712 return proParteMisappliedNameTaxon
.addTaxonRelation(this, TaxonRelationshipType
.PRO_PARTE_MISAPPLIED_NAME_FOR(), citation
, microcitation
);
716 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
717 * "partial misapplied name for") instance where <i>this</i> taxon plays the target role
718 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
719 * The taxon relationship will also be added to the set of taxon
720 * relationships to the other (pro parte misapplied name) taxon involved in the created relationship.
722 * @param partialMisappliedNameTaxon the taxon which plays the source role in the new taxon relationship
723 * @param citation the reference source for the new taxon relationship
724 * @param microcitation the string with the details describing the exact localization within the reference
726 * @see #addMisappliedName(Taxon, Reference, String)
727 * @see #addProParteMisappliedName(Taxon, Reference, String)
728 * @see #getMisappliedNames()
729 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
730 * @see #addTaxonRelation(TaxonRelationship)
731 * @see #getTaxonRelations()
732 * @see #getRelationsFromThisTaxon()
733 * @see #getRelationsToThisTaxon()
735 public TaxonRelationship
addPartialMisappliedName(Taxon partialMisappliedNameTaxon
, Reference citation
, String microcitation
) {
736 return partialMisappliedNameTaxon
.addTaxonRelation(this, TaxonRelationshipType
.PARTIAL_MISAPPLIED_NAME_FOR(), citation
, microcitation
);
740 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
741 * "pro parte synonym for") instance where <i>this</i> taxon plays the target role
742 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
743 * The taxon relationship will also be added to the set of taxon
744 * relationships to the other (pro parte synonym) taxon involved in the created relationship.
746 * @param proParteTaxon the taxon which plays the source role in the new taxon relationship
747 * @param citation the reference source for the new taxon relationship
748 * @param microcitation the string with the details describing the exact localisation within the reference
750 * @see #getMisappliedNames()
751 * @see #addProParteMisappliedName(Taxon, Reference, String)
752 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
753 * @see #addTaxonRelation(TaxonRelationship)
754 * @see #getTaxonRelations()
755 * @see #getRelationsFromThisTaxon()
756 * @see #getRelationsToThisTaxon()
758 public TaxonRelationship
addProparteSynonym(Taxon proParteTaxon
, Reference citation
, String microcitation
) {
759 return proParteTaxon
.addTaxonRelation(this, TaxonRelationshipType
.PRO_PARTE_SYNONYM_FOR(), citation
, microcitation
);
763 * Creates a new {@link TaxonRelationship taxon relationship} instance with
764 * {@link TaxonRelationshipType taxon relationship type} {@link TaxonRelationshipType#PARTIAL_SYNONYM_FOR()
765 * partial synonym for} where <i>this</i> taxon plays the target role
766 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
767 * The taxon relationship will also be added to the set of taxon
768 * relationships to the other (partial synonym) taxon involved in the created relationship.
770 * @param partialTaxon the taxon which plays the source role in the new taxon relationship
771 * @param citation the reference source for the new taxon relationship
772 * @param microcitation the string with the details describing the exact localisation within the reference
774 * @see #addProparteSynonym(Taxon, Reference, String)
775 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
776 * @see #addTaxonRelation(TaxonRelationship)
777 * @see #getTaxonRelations()
778 * @see #getRelationsFromThisTaxon()
779 * @see #getRelationsToThisTaxon()
781 public TaxonRelationship
addPartialSynonym(Taxon partialTaxon
, Reference citation
, String microcitation
) {
782 return partialTaxon
.addTaxonRelation(this, TaxonRelationshipType
.PARTIAL_SYNONYM_FOR(), citation
, microcitation
);
786 * TODO update documentation
787 * Removes one {@link TaxonRelationship taxon relationship} with {@link TaxonRelationshipType taxon relationship type}
788 * taxonRelType and with the given child taxon playing the
789 * source role from the set of {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging
790 * to <i>this</i> taxon. The taxon relationship will also be removed from the set
791 * of {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to the other side taxon.
792 * Furthermore, the inherited RelatedFrom and RelatedTo attributes of the
793 * taxon relationship will be nullified.<P>
795 * @param taxon the taxon which plays the source role in the taxon relationship
796 * @param taxonRelType the taxon relationship type
798 public void removeTaxon(Taxon taxon
, TaxonRelationshipType taxonRelType
){
799 Set
<TaxonRelationship
> taxRels
= this.getTaxonRelations();
800 for (TaxonRelationship taxRel
: taxRels
){
801 if (taxRel
.getType().equals(taxonRelType
)
802 && taxRel
.getFromTaxon().equals(taxon
)){
803 this.removeTaxonRelation(taxRel
);
809 * Returns the boolean value indicating whether <i>this</i> taxon is a misapplication
810 * (misapplied name) for at least one other taxon.
812 // TODO cache as for #hasTaxonomicChildren
814 public boolean isMisapplication(){
815 return computeMisapliedNameRelations() > 0;
819 * Counts the number of misapplied name relationships (including pro parte and partial
820 * misapplied names) where this taxon represents the
821 * misapplied name for another taxon.
824 private int computeMisapliedNameRelations(){
826 for (TaxonRelationship rel
: this.getRelationsFromThisTaxon()){
827 if (rel
.getType().isAnyMisappliedName()){
835 * Returns the boolean value indicating whether <i>this</i> taxon is a misapplication
836 * (misapplied name) for at least one other taxon.
838 // TODO cache as for #hasTaxonomicChildren
840 public boolean isProparteSynonym(){
841 return computeProparteSynonymRelations() > 0;
845 * Counts the number of misapplied name relationships (including pro parte misapplied
846 * names) where this taxon represents the
847 * misapplied name for another taxon.
850 private int computeProparteSynonymRelations(){
852 for (TaxonRelationship rel
: this.getRelationsFromThisTaxon()){
853 if (rel
.getType().isAnySynonym()){
861 * Returns the boolean value indicating whether <i>this</i> taxon is a invalid designation
862 * for at least one other taxon.
864 // TODO cache as for #hasTaxonomicChildren
866 public boolean isInvalidDesignation(){
867 return computeInvalidDesignationRelations() > 0;
871 * Counts the number of invalid designation relationships where this taxon represents the
872 * invalid designation for another taxon.
875 private int computeInvalidDesignationRelations(){
877 for (TaxonRelationship rel
: this.getRelationsFromThisTaxon()){
878 if (rel
.getType().isInvalidDesignation()){
886 * Returns the boolean value indicating whether <i>this</i> taxon is a related
887 * concept for at least one other taxon.
890 public boolean isRelatedConcept(){
891 return computeConceptRelations() > 0;
895 * Counts the number of concept relationships where this taxon represents the
896 * related concept for another taxon.
899 private int computeConceptRelations(){
901 for (TaxonRelationship rel
: this.getRelationsFromThisTaxon()){
902 TaxonRelationshipType type
= rel
.getType();
903 if (type
.isConceptRelationship()){
911 * Returns the boolean value indicating whether <i>this</i> taxon has at least one
912 * {@link Synonym synonym} (true) or not (false). If true the {@link #getSynonyms() set of synonyms}
913 * belonging to <i>this</i> ("accepted/valid") taxon is not empty .
915 * @see #getSynonyms()
916 * @see #getSynonymNames()
917 * @see #removeSynonym(Synonym)
920 public boolean hasSynonyms(){
921 return this.getSynonyms().size() > 0;
925 * Returns the boolean value indicating whether <i>this</i> taxon is at least
926 * involved in one {@link #getTaxonRelations() taxon relationship} between
927 * two taxa (true), either as a source or as a target, or not (false).
929 * @see #getTaxonRelations()
930 * @see #getRelationsToThisTaxon()
931 * @see #getRelationsFromThisTaxon()
932 * @see #removeTaxonRelation(TaxonRelationship)
933 * @see TaxonRelationship
935 public boolean hasTaxonRelationships(){
936 return this.getTaxonRelations().size() > 0;
943 * Returns the set of taxa playing the source role in {@link TaxonRelationship taxon relationships}
944 * (with {@link TaxonRelationshipType taxon relationship type} "misapplied name for") where
945 * <i>this</i> taxon plays the target role. A misapplied name is a taxon the
946 * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} of which has been erroneously used
947 * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
948 * as the one meant by <i>this</i> ("accepted/correct") taxon.
950 * @see #getTaxonRelations()
951 * @see #getRelationsToThisTaxon()
952 * @see #addMisappliedName(Taxon, Reference, String)
953 * @param includeNonCongruent if <code>true</code> also those taxa are returned that are related
954 * via a non congruent relationship like {@link TaxonRelationshipType#PRO_PARTE_MISAPPLIED_NAME_FOR()
955 * pro parte misapplied name}
958 public Set
<Taxon
> getMisappliedNames(boolean includeNonCongruent
){
959 Set
<Taxon
> taxa
= new HashSet
<>();
960 Set
<TaxonRelationship
> rels
= this.getRelationsToThisTaxon();
961 for (TaxonRelationship rel
: rels
){
962 TaxonRelationshipType relType
= rel
.getType();
963 if ( (includeNonCongruent
&& relType
.isAnyMisappliedName())
964 || relType
.equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR())){
965 taxa
.add(rel
.getFromTaxon());
972 * Returns the set of misapplied name relationships in which this taxon
973 * plays the role of the correctly accepted taxon (target). A misapplied name is a taxon the
974 * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} of which has been erroneously used
975 * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
976 * as the one meant by <i>this</i> ("accepted/correct") taxon.
979 public Set
<TaxonRelationship
> getMisappliedNameRelations(){
980 Set
<TaxonRelationship
> result
= new HashSet
<>();
981 Set
<TaxonRelationship
> rels
= this.getRelationsToThisTaxon();
982 for (TaxonRelationship rel
: rels
){
983 TaxonRelationshipType relType
= rel
.getType();
984 if (relType
.isAnyMisappliedName()){
992 * Returns the set of taxa playing the target role in {@link TaxonRelationship taxon relationships}
993 * (with {@link TaxonRelationshipType taxon relationship type} "misapplied name for"
994 * or "pro parte misapplied name for") where
995 * <i>this</i> taxon plays the source role. A misapplied name is a taxon the
996 * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} of which has been erroneously used
997 * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
998 * as the one meant by <i>this</i> ("accepted/correct") taxon.
1000 * @param includeNonCongruent if <code>true</code> also those taxa are returned that are related
1001 * via a non congruent relationship like {@link TaxonRelationshipType#PRO_PARTE_MISAPPLIED_NAME_FOR()
1002 * pro parte misapplied name}
1004 * @see #getTaxonRelations()
1005 * @see #getRelationsToThisTaxon()
1006 * @see #addMisappliedName(Taxon, Reference, String)
1007 * @see #addProParteMisappliedName(Taxon, Reference, String)
1010 public Set
<Taxon
> getTaxaForMisappliedName(boolean includeNonCongruent
){
1011 Set
<Taxon
> taxa
= new HashSet
<>();
1012 Set
<TaxonRelationship
> rels
= this.getRelationsFromThisTaxon();
1013 for (TaxonRelationship rel
: rels
){
1014 TaxonRelationshipType relType
= rel
.getType();
1015 if ( (includeNonCongruent
&& relType
.isAnyMisappliedName())
1016 || relType
.equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR())){
1017 taxa
.add(rel
.getToTaxon());
1024 * Returns the set of pro parte or partial synonym relationships in which this taxon
1025 * plays the role of the "correctly" accepted taxon (target).
1027 * @see #getProParteAndPartialSynonyms()
1028 * @see #getMisappliedNameRelations()
1031 public Set
<TaxonRelationship
> getProParteAndPartialSynonymRelations(){
1032 Set
<TaxonRelationship
> result
= new HashSet
<>();
1033 Set
<TaxonRelationship
> rels
= this.getRelationsToThisTaxon();
1034 for (TaxonRelationship rel
: rels
){
1035 TaxonRelationshipType relType
= rel
.getType();
1036 if (relType
.isAnySynonym()){
1044 * Returns the set of pro parte or partial synonyms in which this taxon
1045 * plays the role of the "correctly" accepted taxon (target).
1047 * @see #getProParteAndPartialSynonymRelations()
1048 * @see #getMisappliedNames(boolean)
1051 public Set
<Taxon
> getProParteAndPartialSynonyms(){
1052 Set
<Taxon
> synonyms
= new HashSet
<>();
1053 Set
<TaxonRelationship
> rels
= this.getProParteAndPartialSynonymRelations();
1054 for (TaxonRelationship rel
: rels
){
1055 synonyms
.add(rel
.getFromTaxon());
1061 * Returns the set of all {@link TaxonName taxon names} used as {@link Synonym synonyms}
1062 * of <i>this</i> ("accepted/valid") taxon.
1064 * @see #getSynonyms()
1065 * @see #getSynonymsSortedByType()
1066 * @see #addSynonymName(TaxonName, SynonymType)
1067 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1068 * @see #removeSynonym(Synonym)
1071 public Set
<TaxonName
> getSynonymNames(){
1072 Set
<TaxonName
> names
= new HashSet
<>();
1073 for (Synonym syn
: this.getSynonyms()){
1074 names
.add(syn
.getName());
1080 * Might be public in future. For the moment protected to ensure that
1081 * synonym type is always set after refactoring.
1085 protected void addSynonym(Synonym synonym
){
1086 if (! this.equals(synonym
.getAcceptedTaxon())){
1087 synonym
.setAcceptedTaxon(this);
1089 if (!synonyms
.contains(synonym
)){
1090 synonyms
.add(synonym
);
1095 * Adds the given {@link Synonym synonym} to <code>this</code> taxon
1096 * and changes the {@link SynonymType
1097 * synonym type} before.
1099 * @param synonym the synonym to be added
1100 * @param synonymType the synonym type of the synonym to be added. If not <code>null</code>
1101 * and if the synonym already has a type the existing type will be overwritten.<BR>
1102 * If synonymType is {@link SynonymType#HOMOTYPIC_SYNONYM_OF()}
1103 * the homotypic group of the synonym is changed to that of <code>this</code> taxon.<BR>
1104 * To explicitly set the type to <code>null</code> use {@link Synonym#setType(SynonymType)}
1105 * @see #addSynonym(Synonym)
1106 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1107 * @see #addSynonymName(TaxonName, SynonymType)
1108 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1109 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1110 * @see #addHeterotypicSynonymName(TaxonName)
1111 * @see #addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1112 * @see #getSynonyms()
1113 * @see #removeSynonym(Synonym)
1114 * @see Synonym#getAcceptedTaxon()
1116 public void addSynonym(Synonym synonym
, SynonymType synonymType
){
1117 synonym
.setType(synonymType
); //must be set before as otherwise merging of homotypical groups may not work correctly in Synonym.checkHomotypic()
1118 addSynonym(synonym
);
1122 * Adds the given {@link Synonym synonym} with the given {@link SynonymType
1123 * synonym relationship type}
1125 * @param synonym the synonym to be added
1126 * @param synonymType the synonym type of the synonym to be added. If not null
1127 * and if the synonym already has a type the existing type will be overwritten.
1128 // * @param citation the reference source for the new synonym relationship
1129 // * @param microcitation the string with the details describing the exact localization within the reference
1130 * @see #addSynonym(Synonym)
1131 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1132 * @see #addSynonymName(TaxonName, SynonymType)
1133 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1134 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1135 * @see #addHeterotypicSynonymName(TaxonName)
1136 * @see #addHeterotypicSynonymName(TaxonName, HomotypicalGroup, Reference, String)
1137 * @see #getSynonyms()
1138 * @see #removeSynonym(Synonym)
1139 * @see Synonym#getAcceptedTaxon()
1141 private void addSynonym(Synonym synonym
, SynonymType synonymType
, Reference newSecReference
, String newSecMicroReference
){
1142 if (newSecReference
!= null){
1143 synonym
.setSec(newSecReference
);
1145 if (newSecMicroReference
!= null){
1146 synonym
.setSecMicroReference(newSecMicroReference
);
1148 addSynonym(synonym
, synonymType
);
1153 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the
1154 * given {@link TaxonName synonym name} and with the given
1155 * {@link SynonymType synonym type}. If the later is
1156 * {@link SynonymType#HOMOTYPIC_SYNONYM_OF() homotypic synonym}
1157 * the name will be added to the same {@link HomotypicalGroup homotypical group}
1158 * as the <code>this</code> accepted taxon.<BR>
1159 * The secundum reference of the new synonym is taken from <code>this</code> taxon.
1160 * A secundum detail is not set.
1162 * @param synonymName the taxon name to be used as a synonym to be added
1163 * to <i>this</i> taxon's set of synonyms
1164 * @param synonymType the synonym type of the synonym
1165 * relationship to be added
1166 * @return the created synonym
1167 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1168 * @see #addSynonym(Synonym, SynonymType)
1169 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1170 * @see #addHomotypicSynonym(Synonym, Reference, String)
1171 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1172 * @see #addHeterotypicSynonymName(TaxonName)
1173 * @see #addHeterotypicSynonymName(TaxonName, HomotypicalGroup, Reference, String)
1174 * @see #getSynonyms()
1175 * @see #removeSynonym(Synonym)
1177 public Synonym
addSynonymName(TaxonName synonymName
, SynonymType synonymType
){
1178 return addSynonymName(synonymName
, null, null, synonymType
);
1182 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the
1183 * given {@link TaxonName synonym name} and with the given
1184 * {@link SynonymType synonym type}. If the later is
1185 * {@link SynonymType#HOMOTYPIC_SYNONYM_OF() homotypic synonym}
1186 * the name will be added to the same {@link HomotypicalGroup homotypical group}
1187 * as the <code>this</code> accepted taxon.<BR>
1189 * If secReference is not <code>null</code>, the new synonym will have this as
1190 * secundum reference. Otherwise <code>this</code> taxons sec reference is taken
1191 * as secundum reference for the synonym. SecDetail will be the secMicroReference of the
1194 * @param synonymName the taxon name to be used as a synonym to be added
1195 * to <i>this</i> taxon's set of synonyms
1196 * @param secReference the secundum reference for the new synonym (if <code>null</code>
1197 * <code>this</code> taxon's secundum reference is taken.
1198 * @param secMicroReference the secundum micro reference of the new synonym
1199 * @param synonymType the synonym type of the synonym to be added
1201 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1202 * @see #addSynonym(Synonym, SynonymType)
1203 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1204 * @see #addHomotypicSynonym(Synonym, Reference, String)
1205 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1206 * @see #addHeterotypicSynonymName(TaxonName)
1207 * @see #addHeterotypicSynonymName(TaxonName, HomotypicalGroup, Reference, String)
1208 * @see #getSynonyms()
1209 * @see #removeSynonym(Synonym)
1211 public Synonym
addSynonymName(TaxonName synonymName
, Reference secReference
, String secMicroReference
, SynonymType synonymType
){
1212 Synonym synonym
= Synonym
.NewInstance(synonymName
, this.getSec()); //default sec
1213 addSynonym(synonym
, synonymType
, secReference
, secMicroReference
);
1218 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the given
1219 * {@link TaxonName synonym name}. The synonym will have the synonym type
1220 * {@link SynonymType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of"}.<BR>
1221 * The secundum reference is taken from <code>this</code> taxon.
1222 * No secMicroReference will be set for the new synonym.<BR>
1223 * The synonym will keep it's old homotypical group.<BR>
1225 * @param synonymName the taxon name to be used as an heterotypic synonym
1226 * to be added to <i>this</i> taxon's set of synonyms
1227 * @return the created synonym
1228 * @see #addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1229 * @see #addSynonymName(TaxonName, SynonymType)
1230 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1231 * @see #addSynonym(Synonym, SynonymType)
1232 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1233 * @see #addHomotypicSynonym(Synonym, Reference, String)
1234 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1235 * @see #getSynonyms()
1236 * @see #removeSynonym(Synonym)
1238 public Synonym
addHeterotypicSynonymName(TaxonName synonymName
){
1239 return addHeterotypicSynonymName(synonymName
, null, null, null);
1243 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the given
1244 * {@link TaxonName synonym name}. The synonym will have the synonym type
1245 * {@link SynonymType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of"}.<BR>
1247 * If secReference is not <code>null</code>, the new synonym will have this as
1248 * secundum reference. Otherwise <code>this</code> taxons sec reference is taken
1249 * as secundum reference for the synonym. SecDetail will be the secMicroReference of the
1251 * Furthermore the taxon name used as synonym will be added
1252 * to the given {@link name.HomotypicalGroup homotypical group} (if not <code>null</code>).<BR>
1254 * @param synonymName the taxon name to be used as an heterotypic synonym
1255 * to be added to <i>this</i> taxon's set of synonyms
1256 * @param secReference the secundum reference for the new synonym
1257 * @param secDetail the secundum detil for the new synonym
1258 * @param homotypicalGroup the homotypical group to which the taxon name
1259 * of the synonym will be added. If <code>null</code>
1260 * the homotypical group of synonymName is not changed
1261 * @return the created synonym
1262 * @see #addHeterotypicSynonymName(TaxonName)
1263 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1264 * @see #addSynonymName(TaxonName, SynonymType)
1265 * @see #addSynonym(Synonym, SynonymType)
1266 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1267 * @see #addHomotypicSynonym(Synonym, Reference, String)
1268 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1269 * @see #getSynonyms()
1270 * @see #removeSynonym(Synonym)
1272 public Synonym
addHeterotypicSynonymName(TaxonName synonymName
, Reference secReference
, String secDetail
, HomotypicalGroup homotypicalGroup
){
1273 Synonym synonym
= Synonym
.NewInstance(synonymName
, this.getSec());
1274 if (homotypicalGroup
!= null){
1275 homotypicalGroup
.addTypifiedName(synonymName
);
1277 addSynonym(synonym
, SynonymType
.HETEROTYPIC_SYNONYM_OF(), secReference
, secDetail
);
1282 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the given
1283 * {@link TaxonName synonym name}. The synonym will have the synonym type
1284 * {@link SynonymType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of"}.<BR>
1285 * The secundum reference is taken from <code>this</code> taxon.
1286 * No secMicroReference will be set for the new synonym.<BR>
1287 * The synonym's homotypic group will be changed to <code>this</code> taxon's group.<BR>
1289 * @param synonymName the taxon name to be used as an homotypic synonym
1290 * to be added to <i>this</i> taxon's set of synonyms
1291 * @return the created synonym
1292 * @see #addHomotypicSynonym(Synonym, Reference, String)
1293 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1294 * @see #addSynonymName(TaxonName, SynonymType)
1295 * @see #addSynonym(Synonym, SynonymType)
1296 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1297 * @see #addHeterotypicSynonymName(TaxonName)
1298 * @see #addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1299 * @see #getSynonyms()
1300 * @see #removeSynonym(Synonym)
1302 public Synonym
addHomotypicSynonymName(TaxonName synonymName
){
1303 Synonym synonym
= Synonym
.NewInstance(synonymName
, this.getSec());
1304 addHomotypicSynonym(synonym
);
1309 * Adds the given {@link Synonym synonym} to <code>this</code> taxon,
1310 * with the {@link SynonymType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of"
1311 * relationship type} and returns it.
1312 * Furthermore the {@link TaxonName taxon name}
1313 * used as synonym will be added to the same {@link HomotypicalGroup homotypic group}
1314 * to which the taxon name of <i>this</i> taxon belongs.<BR>
1316 * @param synonym the synonym added to <i>this</i> taxon's synonym set
1317 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1318 * @see #addSynonym(Synonym, SynonymType)
1319 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1320 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1321 * @see #addSynonymName(TaxonName, SynonymType)
1322 * @see #addHeterotypicSynonymName(TaxonName)
1323 * @see #addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1324 * @see #getSynonyms()
1325 * @see #removeSynonym(Synonym)
1327 public void addHomotypicSynonym(Synonym synonym
){
1328 if (!this.getSynonyms().contains(synonym
)){
1329 addSynonym(synonym
, SynonymType
.HOMOTYPIC_SYNONYM_OF());
1331 logger
.warn("Tried to add a synonym to an accepted taxon that already is a synonym of this taxon.");
1337 * Like {@link #removeSynonym(Synonym, boolean)} with <code>removeSynonymNameFromHomotypicalGroup</code> set to true.
1338 * @see #removeSynonym(Synonym, boolean)
1340 public void removeSynonym(Synonym synonym
){
1341 removeSynonym(synonym
, true);
1346 * Removes one element from the set of {@link Synonym synonyms} assigned
1347 * to <i>this</i> (accepted/valid) taxon.
1349 * @param synonym the synonym to be removed
1350 * @param removeSynonymNameFromHomotypicalGroup
1351 * if <code>true</code> the synonym name will also be deleted from its homotypical group if the
1352 * group contains other names
1353 * @see #getSynonyms()
1354 * @see #removeSynonym(Synonym)
1356 public void removeSynonym(Synonym synonym
, boolean removeSynonymNameFromHomotypicalGroup
) {
1357 if (synonym
!= null && this.equals(synonym
.getAcceptedTaxon())){
1358 if(removeSynonymNameFromHomotypicalGroup
){
1359 HomotypicalGroup synHG
= synonym
.getName().getHomotypicalGroup();
1360 if (synHG
.getTypifiedNames().size() > 1){
1361 synHG
.removeTypifiedName(synonym
.getName(), false);
1364 this.synonyms
.remove(synonym
);
1365 synonym
.setAcceptedTaxon(null);
1370 * @see #getHomotypicSynonymsByHomotypicGroup(TaxonComparator)
1373 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(){
1374 return getHomotypicSynonymsByHomotypicGroup(null);
1378 * Retrieves the ordered list (depending on the date of publication) of
1379 * homotypic {@link Synonym synonyms} (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1380 * as for <i>this</i> taxon) under the condition that the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon names}
1381 * of these synonyms and the taxon name of <i>this</i> taxon belong to the
1382 * same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group}.
1384 * @param comparator the taxon comparator to use, if <code>null</code> the default comparator is taken.
1385 * @return the ordered list of homotypic synonyms
1386 * @see #getHomotypicSynonymsByHomotypicSynonymType()
1387 * @see #getSynonyms()
1388 * @see #getHomotypicSynonymyGroups()
1389 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup
1390 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup#getSynonymsInGroup(Reference)
1393 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(TaxonComparator comparator
){
1394 if (this.getHomotypicGroup() == null){
1396 }else if (comparator
== null){
1397 return this.getSynonymsInGroup(this.getHomotypicGroup());
1399 return this.getSynonymsInGroup(this.getHomotypicGroup(), comparator
);
1404 * Retrieves the list of homotypic {@link Synonym synonyms}
1405 * (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1406 * as for <i>this</i> taxon) under the condition that these synonyms and
1407 * <i>this</i> taxon are involved in {@link SynonymRelationship synonym relationships} with an
1408 * "is homotypic synonym of" {@link SynonymType#HOMOTYPIC_SYNONYM_OF() synonym relationship type}.
1410 * @return the ordered list of homotypic synonyms
1411 * @see #getHomotypicSynonymsByHomotypicGroup()
1412 * @see #getSynonyms()
1413 * @see #getHomotypicSynonymyGroups()
1415 * @deprecated as the method currently returns data not matching the original description of the method
1416 * as an ordered list (according to date of publication) of synonyms with same secundum as <i>this</i> taxon.
1417 * In future this method will either be removed or semantics may change.
1421 public List
<Synonym
> getHomotypicSynonymsByHomotypicSynonymType(){
1422 Set
<Synonym
> synonyms
= this.getSynonyms();
1423 List
<Synonym
> result
= new ArrayList
<>();
1424 for(Synonym synonym
: synonyms
) {
1425 if(synonym
.getType().equals(SynonymType
.HOMOTYPIC_SYNONYM_OF())){
1426 result
.add(synonym
);
1433 * Returns the ordered list of all {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups} {@link Synonym synonyms} of
1434 * <i>this</i> taxon belong to. {@link eu.etaxonomy.cdm.model.name.TaxonName Taxon names} of homotypic synonyms
1435 * belong to the same homotypical group as the taxon name of <i>this</i>
1436 * taxon. Taxon names of heterotypic synonyms belong to at least one other
1437 * homotypical group. <BR>
1438 * The list returned is ordered according to the date of publication of the
1439 * first published name within each homotypical group.
1441 * @see #getHeterotypicSynonymyGroups()
1442 * @see #getSynonyms()
1443 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup
1446 public List
<HomotypicalGroup
> getHomotypicSynonymyGroups(){
1447 List
<HomotypicalGroup
> result
= new ArrayList
<>();
1448 HomotypicalGroup myGroup
= this.getHomotypicGroup();
1449 if (myGroup
!= null){ //if taxon has no name HG might be null
1450 result
.add(myGroup
);
1452 for (TaxonName taxonName
:this.getSynonymNames()){
1453 if (taxonName
!= null) {
1454 if (!result
.contains(taxonName
.getHomotypicalGroup())){
1455 result
.add(taxonName
.getHomotypicalGroup());
1464 * <BR>Also returns <code>false</code> if it is a misapplied name or has a similar concept relationship that
1465 * is similar to synonym relationship (shows up in the synonymy of applications)
1469 public boolean isOrphaned() {
1471 if(taxonNodes
== null || taxonNodes
.isEmpty()) {
1472 if(getRelationsFromThisTaxon().isEmpty()) {
1475 for (TaxonRelationship rel
: getRelationsFromThisTaxon()){
1476 if (rel
.getType() != null && ! rel
.getType().isConceptRelationship()){
1477 return false; //a synonym relationship type similar relationship exists => not orphaned
1480 return true; //all relations are real concept relations and therefore not relevant
1488 * Returns the ordered list of all
1489 * {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups}
1490 * that contain {@link Synonym synonyms} that are heterotypic to <i>this</i> taxon.<BR>
1492 * {@link eu.etaxonomy.cdm.model.name.TaxonName Taxon names} of heterotypic synonyms
1493 * belong to a homotypical group which cannot be the homotypical group to which the
1494 * taxon name of <i>this</i> taxon belongs.
1495 * This method returns the same
1496 * list as the {@link #getHomotypicSynonymyGroups() getHomotypicSynonymyGroups} method
1497 * but without the homotypical group to which the taxon name of <i>this</i> taxon
1499 * The list returned is <B>ordered</B> according to the rules defined for
1500 * the {@link HomotypicGroupTaxonComparator} which includes 1) grouping of
1501 * basionym groups, 2) replaced synonym relationships, 3) publication date,
1502 * 4) ranks and 5) alphabetical order.
1504 * @see #getHeterotypicSynonymyGroups()
1505 * @see #getSynonyms()
1506 * @see SynonymType#HETEROTYPIC_SYNONYM_OF()
1507 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup
1510 public List
<HomotypicalGroup
> getHeterotypicSynonymyGroups(){
1511 List
<HomotypicalGroup
> list
= getHomotypicSynonymyGroups();
1512 //remove homotypic group
1513 list
.remove(this.getHomotypicGroup());
1515 Map
<Synonym
, HomotypicalGroup
> map
= new HashMap
<>();
1516 for (HomotypicalGroup homotypicalGroup
: list
){
1517 List
<Synonym
> synonymList
= getSynonymsInGroup(homotypicalGroup
);
1518 if (synonymList
.size() > 0){
1519 //select the first synonym in the group
1520 map
.put(synonymList
.get(0), homotypicalGroup
);
1523 List
<Synonym
> keyList
= new ArrayList
<>();
1524 keyList
.addAll(map
.keySet());
1525 //order by first synonym
1526 Collections
.sort(keyList
, defaultTaxonComparator
);
1528 List
<HomotypicalGroup
> result
= new ArrayList
<>();
1529 for(Synonym synonym
: keyList
){
1530 //"replace" synonyms by homotypic groups
1531 result
.add(map
.get(synonym
));
1538 * Retrieves the ordered list (depending on the rules defined for
1539 * the {@link HomotypicGroupTaxonComparator}) of
1540 * {@link taxon.Synonym synonyms} (according to a given reference)
1541 * the {@link TaxonName taxon names} of which belong to the homotypical group.
1542 * If other names are part of the group that are not considered synonyms of
1543 * <i>this</i> taxon, then they will not be included in
1546 * @param homotypicGroup
1547 * @see #getHeterotypicSynonymyGroups()
1548 * @see TaxonName#getSynonyms()
1549 * @see TaxonName#getTaxa()
1550 * @see taxon.Synonym
1553 public List
<Synonym
> getSynonymsInGroup(HomotypicalGroup homotypicGroup
){
1554 return getSynonymsInGroup(homotypicGroup
, new HomotypicGroupTaxonComparator(this));
1558 * @param homotypicGroup
1561 * @see #getSynonymsInGroup(HomotypicalGroup)
1562 * @see #getHeterotypicSynonymyGroups()
1565 public List
<Synonym
> getSynonymsInGroup(HomotypicalGroup homotypicGroup
, TaxonComparator comparator
){
1566 List
<Synonym
> result
= new ArrayList
<>();
1567 if (homotypicGroup
== null){
1568 return result
; //always empty
1571 for (Synonym synonym
: this.getSynonyms()){
1572 if (homotypicGroup
.equals(synonym
.getHomotypicGroup())){
1573 result
.add(synonym
);
1577 Collections
.sort(result
, comparator
);
1582 * @see #getSynonymsGroups()
1585 public List
<Taxon
> getAllMisappliedNames(){
1586 List
<Taxon
> result
= new ArrayList
<>();
1588 for (TaxonRelationship rel
: this.getRelationsToThisTaxon()){
1589 if (rel
.getType().isAnyMisappliedName() ){
1590 result
.add(rel
.getFromTaxon());
1593 sortBySimpleTitleCacheComparator(result
);
1598 * @see #getSynonymsGroups()
1601 public List
<Taxon
> getInvalidDesignations(){
1602 List
<Taxon
> result
= new ArrayList
<>();
1603 for (TaxonRelationship rel
: this.getRelationsToThisTaxon()){
1604 if (rel
.getType().isInvalidDesignation()){
1605 result
.add(rel
.getFromTaxon());
1608 sortBySimpleTitleCacheComparator(result
);
1613 * @see #getSynonymsGroups()
1616 public List
<Taxon
> getAllProParteSynonyms(){
1617 List
<Taxon
> result
= new ArrayList
<>();
1619 for (TaxonRelationship rel
: this.getRelationsToThisTaxon()){
1620 if (rel
.getType().isAnySynonym()){
1621 result
.add(rel
.getFromTaxon());
1624 sortBySimpleTitleCacheComparator(result
);
1628 private void sortBySimpleTitleCacheComparator(List
<Taxon
> result
) {
1630 Comparator
<Taxon
> taxonComparator
= new Comparator
<Taxon
>(){
1633 public int compare(Taxon o1
, Taxon o2
) {
1635 if (o1
.getTitleCache() == o2
.getTitleCache()){
1638 if (o1
.getTitleCache() == null){
1641 if (o2
.getTitleCache() == null){
1644 return o1
.getTitleCache().compareTo(o2
.getTitleCache());
1648 Collections
.sort(result
, taxonComparator
);
1652 * Returns the image gallery description. If no image gallery exists, a new one is created using the
1653 * defined title and adds the string "-Image Gallery" to the title.</BR>
1654 * If multiple image galleries exist an arbitrary one is choosen.
1658 public TaxonDescription
getOrCreateImageGallery(String title
){
1659 return getOrCreateImageGallery(title
, true, false);
1663 * Returns the image gallery description. If no image gallery exists, a new one is created using the
1664 * defined title.</BR>
1665 * If onlyTitle == true we look only for an image gallery with this title, create a new one otherwise.
1666 * If multiple image galleries exist that match the conditions an arbitrary one is choosen.
1669 * @param if true, the String "Image Gallery
1673 public TaxonDescription
getOrCreateImageGallery(String title
, boolean addImageGalleryToTitle
, boolean onlyTitle
){
1674 TaxonDescription result
= null;
1675 String titleCache
= (title
== null) ?
"Image Gallery" : title
;
1676 if (title
!= null && addImageGalleryToTitle
){
1677 titleCache
= titleCache
+ "-Image Gallery";
1679 Set
<TaxonDescription
> descriptionSet
= this.getDescriptions();
1680 for (TaxonDescription desc
: descriptionSet
){
1681 if (desc
.isImageGallery()){
1682 if (onlyTitle
&& ! titleCache
.equals(desc
.getTitleCache())){
1686 if (onlyTitle
&& titleCache
.equals(desc
.getTitleCache())){
1691 if (result
== null){
1692 result
= TaxonDescription
.NewInstance();
1693 result
.setTitleCache(titleCache
, true);
1694 this.addDescription(result
);
1695 result
.setImageGallery(true);
1702 * Clones <i>this</i> taxon. This is a shortcut that enables to create
1703 * a new instance that differs only slightly from <i>this</i> taxon by
1704 * modifying only some of the attributes.<BR><BR>
1705 * The TaxonNodes are not cloned, the list is empty.<BR>
1706 * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1707 * The taxon relationships and synonym relationships are cloned <BR>
1709 * @see eu.etaxonomy.cdm.model.taxon.TaxonBase#clone()
1710 * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1711 * @see java.lang.Object#clone()
1714 public Object
clone() {
1716 result
= (Taxon
)super.clone();
1718 result
.setRelationsFromThisTaxon(new HashSet
<>());
1720 for (TaxonRelationship fromRelationship
: this.getRelationsFromThisTaxon()){
1721 TaxonRelationship newRelationship
= (TaxonRelationship
)fromRelationship
.clone();
1722 newRelationship
.setRelatedFrom(result
);
1723 result
.relationsFromThisTaxon
.add(newRelationship
);
1726 result
.setRelationsToThisTaxon(new HashSet
<>());
1727 for (TaxonRelationship toRelationship
: this.getRelationsToThisTaxon()){
1728 TaxonRelationship newRelationship
= (TaxonRelationship
)toRelationship
.clone();
1729 newRelationship
.setRelatedTo(result
);
1730 result
.relationsToThisTaxon
.add(newRelationship
);
1733 //clone synonyms (is this wanted or should we remove synonyms
1734 result
.synonyms
= new HashSet
<>();
1735 for (Synonym synonym
: this.getSynonyms()){
1736 Synonym newSyn
= (Synonym
)synonym
.clone();
1737 newSyn
.setAcceptedTaxon(result
);
1740 result
.descriptions
= new HashSet
<>();
1741 for (TaxonDescription description
: this.getDescriptions()){
1742 TaxonDescription newDescription
= (TaxonDescription
)description
.clone();
1743 result
.addDescription(newDescription
);
1746 result
.taxonNodes
= new HashSet
<>();
1748 /*for (TaxonNode taxonNode : this.getTaxonNodes()){
1749 TaxonNode newTaxonNode = (TaxonNode)taxonNode.clone();
1750 newTaxonNode.setTaxon(result);
1751 result.addTaxonNode(newTaxonNode);
1758 public void clearDescriptions() {
1759 this.descriptions
= new HashSet
<>();