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
.compare
.taxon
.HomotypicGroupTaxonComparator
;
53 import eu
.etaxonomy
.cdm
.compare
.taxon
.TaxonComparator
;
54 import eu
.etaxonomy
.cdm
.hibernate
.search
.GroupByTaxonClassBridge
;
55 import eu
.etaxonomy
.cdm
.hibernate
.search
.TaxonRelationshipClassBridge
;
56 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
57 import eu
.etaxonomy
.cdm
.model
.common
.IRelated
;
58 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
59 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
60 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionType
;
61 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
62 import eu
.etaxonomy
.cdm
.model
.description
.IDescribable
;
63 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
64 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
65 import eu
.etaxonomy
.cdm
.model
.name
.ITaxonNameBase
;
66 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
67 import eu
.etaxonomy
.cdm
.model
.reference
.ICdmTarget
;
68 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
69 import eu
.etaxonomy
.cdm
.strategy
.cache
.taxon
.ITaxonCacheStrategy
;
72 * The class for "accepted/correct" {@link TaxonBase taxa} (only these taxa according to
73 * the opinion of the {@link eu.etaxonomy.cdm.model.reference.Reference reference} can build a classification).
74 * An {@link java.lang.Iterable interface} is supported to iterate through taxonomic children.<BR>
75 * Splitting taxa in "accepted/correct" and {@link Synonym "synonyms"} makes it easier to handle
76 * particular relationships between ("accepted/correct") taxa on the one hand
77 * and between ("synonym") taxa and ("accepted/correct") taxa on the other.
80 * @since 08-Nov-2007 13:06:56
82 @XmlAccessorType(XmlAccessType
.FIELD
)
83 @XmlType(name
= "Taxon", propOrder
= {
86 "relationsFromThisTaxon",
87 "relationsToThisTaxon",
90 @XmlRootElement(name
= "Taxon")
92 @Indexed(index
= "eu.etaxonomy.cdm.model.taxon.TaxonBase")
96 @ClassBridge(impl
= GroupByTaxonClassBridge
.class),
97 @ClassBridge(impl
= TaxonRelationshipClassBridge
.class)
100 extends TaxonBase
<ITaxonCacheStrategy
<Taxon
>>
101 implements IRelated
<RelationshipBase
>, IDescribable
<TaxonDescription
>, ICdmTarget
{
103 private static final long serialVersionUID
= -584946869762749006L;
104 private static final Logger logger
= Logger
.getLogger(Taxon
.class);
106 private static final TaxonComparator defaultTaxonComparator
= new TaxonComparator();
108 @XmlElementWrapper(name
= "Descriptions")
109 @XmlElement(name
= "Description")
110 @OneToMany(mappedBy
="taxon", fetch
= FetchType
.LAZY
)
111 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
114 private Set
<TaxonDescription
> descriptions
= new HashSet
<>();
116 // all related synonyms
117 @XmlElementWrapper(name
= "Synonyms")
118 @XmlElement(name
= "Synonym")
120 @XmlSchemaType(name
= "IDREF")
121 @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
122 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
126 private Set
<Synonym
> synonyms
= new HashSet
<>();
128 // all taxa relations with rel.fromTaxon==this
129 @XmlElementWrapper(name
= "RelationsFromThisTaxon")
130 @XmlElement(name
= "FromThisTaxonRelationship")
131 @OneToMany(mappedBy
="relatedFrom", fetch
=FetchType
.LAZY
, orphanRemoval
=true)
132 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
136 private Set
<TaxonRelationship
> relationsFromThisTaxon
= new HashSet
<>();
138 // all taxa relations with rel.toTaxon==this
139 @XmlElementWrapper(name
= "RelationsToThisTaxon")
140 @XmlElement(name
= "ToThisTaxonRelationship")
142 @XmlSchemaType(name
= "IDREF")
143 @OneToMany(mappedBy
="relatedTo", fetch
=FetchType
.LAZY
, orphanRemoval
=true)
144 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
147 private Set
<TaxonRelationship
> relationsToThisTaxon
= new HashSet
<>();
149 @XmlAttribute(name
= "taxonStatusUnknown")
150 private boolean taxonStatusUnknown
= false;
152 * The status of this taxon is unknown it could also be some kind of synonym.
153 * @return the taxonStatusUnknown
155 public boolean isTaxonStatusUnknown() {return taxonStatusUnknown
;}
156 /** @see #isTaxonStatusUnknown()*/
157 public void setTaxonStatusUnknown(boolean taxonStatusUnknown
) {this.taxonStatusUnknown
= taxonStatusUnknown
;}
159 @XmlElementWrapper(name
= "taxonNodes")
160 @XmlElement(name
= "taxonNode")
162 @XmlSchemaType(name
= "IDREF")
163 @OneToMany(mappedBy
="taxon", fetch
=FetchType
.LAZY
)
164 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
166 private Set
<TaxonNode
> taxonNodes
= new HashSet
<>();
168 // ************************* FACTORY METHODS ********************************/
171 * @see #NewInstance(TaxonName, Reference)
176 public static Taxon
NewInstance(ITaxonNameBase taxonName
, Reference sec
){
177 return NewInstance(TaxonName
.castAndDeproxy(taxonName
), sec
);
181 * Creates a new (accepted/valid) taxon instance with
182 * the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
185 * @param taxonName the taxon name used
186 * @param sec the reference using the taxon name
187 * @see #Taxon(TaxonName, Reference)
189 public static Taxon
NewInstance(TaxonName taxonName
, Reference sec
){
190 Taxon result
= new Taxon(taxonName
, sec
);
195 * Creates a new Taxon for the given name, secundum reference and secundum detail
198 * @param secMicroReference
201 public static Taxon
NewInstance(TaxonName taxonName
, Reference sec
, String secMicroReference
){
202 Taxon result
= new Taxon(taxonName
, sec
, secMicroReference
);
207 * Creates a new taxon instance with an unknown status (accepted/synonym) and with
208 * the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
211 * @param taxonName the taxon name used
212 * @param sec the reference using the taxon name
213 * @see #Taxon(TaxonName, Reference)
215 public static Taxon
NewUnknownStatusInstance(TaxonName taxonName
, Reference sec
){
216 Taxon result
= new Taxon(taxonName
, sec
);
217 result
.setTaxonStatusUnknown(true);
220 // ************* CONSTRUCTORS *************/
222 //TODO should be private, but still produces Spring init errors
226 private Taxon(TaxonName taxonName
, Reference sec
){
227 super(taxonName
, sec
, null);
230 private Taxon(TaxonName taxonName
, Reference sec
, String secMicroReference
){
231 super(taxonName
, sec
, secMicroReference
);
234 //********* METHODS **************************************/
237 * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon descriptions}
238 * concerning <i>this</i> taxon.
240 * @see #removeDescription(TaxonDescription)
241 * @see #addDescription(TaxonDescription)
242 * @see eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
245 public Set
<TaxonDescription
> getDescriptions() {
246 if(descriptions
== null) {
247 descriptions
= new HashSet
<>();
252 public Set
<TaxonDescription
> getDescriptions(DescriptionType type
) {
253 Set
<TaxonDescription
> result
= new HashSet
<>();
254 for (TaxonDescription description
: getDescriptions()){
255 if (description
.getTypes().contains(type
)){
256 result
.add(description
);
263 * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon description} to the set
264 * of taxon descriptions assigned to <i>this</i> (accepted/correct) taxon.
265 * Due to bidirectionality the content of the {@link eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon() taxon attribute} of the
266 * taxon description itself will be replaced with <i>this</i> taxon. The taxon
267 * description will also be removed from the set of taxon descriptions
268 * assigned to its previous taxon.
270 * @param description the taxon description to be added for <i>this</i> taxon
271 * @see #getDescriptions()
272 * @see #removeDescription(TaxonDescription)
273 * @see eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
276 public void addDescription(TaxonDescription description
) {
277 if (description
.getTaxon() != null){
278 description
.getTaxon().removeDescription(description
);
280 Field field
= ReflectionUtils
.findField(TaxonDescription
.class, "taxon", Taxon
.class);
281 ReflectionUtils
.makeAccessible(field
);
282 ReflectionUtils
.setField(field
, description
, this);
283 descriptions
.add(description
);
287 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon descriptions} assigned
288 * to <i>this</i> (accepted/correct) taxon. Due to bidirectionality the content of
289 * the {@link eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon() taxon attribute} of the taxon description
290 * itself will be set to "null".
292 * @param description the taxon description which should be removed
293 * @see #getDescriptions()
294 * @see #addDescription(TaxonDescription)
295 * @see eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
298 public void removeDescription(TaxonDescription description
) {
299 //description.setTaxon(null) for not visible method
300 Field field
= ReflectionUtils
.findField(TaxonDescription
.class, "taxon", Taxon
.class);
301 ReflectionUtils
.makeAccessible(field
);
302 ReflectionUtils
.setField(field
, description
, null);
303 descriptions
.remove(description
);
306 public void removeDescription(TaxonDescription description
, boolean removeElements
){
308 Set
<DescriptionElementBase
> elements
= new HashSet
<DescriptionElementBase
>(description
.getElements());
309 for (DescriptionElementBase el
:elements
){
310 description
.getElements().remove(el
);
312 removeDescription(description
);
314 removeDescription(description
);
319 * Returns the image gallery for a taxon. If there are multiple taxon descriptions
320 * marked as image galleries an arbitrary one is chosen.
321 * If no image gallery exists, a new one is created if <code>createNewIfNotExists</code>
322 * is <code>true</code>.
323 * @param createNewIfNotExists
326 public TaxonDescription
getImageGallery(boolean createNewIfNotExists
) {
327 TaxonDescription result
= null;
328 Set
<TaxonDescription
> descriptions
= getDescriptions();
329 for (TaxonDescription description
: descriptions
){
330 if (description
.isImageGallery()){
331 result
= description
;
335 if (result
== null && createNewIfNotExists
){
336 result
= TaxonDescription
.NewInstance(this);
337 result
.setImageGallery(true);
344 public Set
<TaxonNode
> getTaxonNodes() {
347 // protected void setTaxonNodes(Set<TaxonNode> taxonNodes) {
348 // this.taxonNodes = taxonNodes;
350 protected void addTaxonNode(TaxonNode taxonNode
){
351 taxonNodes
.add(taxonNode
);
354 public boolean removeTaxonNode(TaxonNode taxonNode
){
355 if (!taxonNodes
.contains(taxonNode
)){
358 TaxonNode parent
= taxonNode
.getParent();
360 parent
.removeChildNode(taxonNode
);
362 taxonNode
.setTaxon(null);
363 return taxonNodes
.remove(taxonNode
);
367 public boolean removeTaxonNode(TaxonNode taxonNode
, boolean deleteChildren
){
368 TaxonNode parent
= taxonNode
.getParent();
369 boolean success
= true;
371 if ((!taxonNode
.getChildNodes().isEmpty() && deleteChildren
) || (taxonNode
.getChildNodes().isEmpty()) ){
375 } else if (!taxonNode
.isTopmostNode()){
377 List
<TaxonNode
> nodes
= new ArrayList
<TaxonNode
> (taxonNode
.getChildNodes());
378 for (TaxonNode childNode
: nodes
){
379 taxonNode
.getChildNodes().remove(childNode
);
380 parent
.addChildNode(childNode
, null, null);
385 } else if (taxonNode
.isTopmostNode()){
391 public boolean removeTaxonNodes(boolean deleteChildren
){
392 Iterator
<TaxonNode
> nodesIterator
= taxonNodes
.iterator();
395 boolean success
= false;
396 List
<TaxonNode
> removeNodes
= new ArrayList
<>();
397 while (nodesIterator
.hasNext()){
398 node
= nodesIterator
.next();
399 if (!deleteChildren
){
400 List
<TaxonNode
> children
= node
.getChildNodes();
401 Iterator
<TaxonNode
> childrenIterator
= children
.iterator();
402 parent
= node
.getParent();
403 while (childrenIterator
.hasNext()){
404 TaxonNode childNode
= childrenIterator
.next();
406 parent
.addChildNode(childNode
, null, null);
408 childNode
.setParent(null);
412 for (int i
= 0; i
<node
.getChildNodes().size(); i
++){
417 removeNodes
.add(node
);
419 for (int i
= 0; i
<removeNodes
.size(); i
++){
420 TaxonNode removeNode
= removeNodes
.get(i
);
421 success
= removeNode
.delete(deleteChildren
);
422 removeNode
.setTaxon(null);
423 removeTaxonNode(removeNode
);
429 public TaxonNode
getTaxonNode(Classification classification
) {
430 if (classification
== null){
433 for (TaxonNode node
: this.getTaxonNodes()){
434 if (classification
.equals(node
.getClassification())){
442 * Returns the set of all {@link Synonym synonyms}
443 * for which <i>this</i> ("accepted/valid") taxon is the accepted taxon.
445 * @see #addSynonym(Synonym, SynonymType)
446 * @see #removeSynonym(Synonym)
448 public Set
<Synonym
> getSynonyms() {
449 if(synonyms
== null) {
450 this.synonyms
= new HashSet
<>();
456 * Returns the set of all {@link TaxonRelationship taxon relationships}
457 * between two taxa in which <i>this</i> taxon is involved as a source.
459 * @see #getRelationsToThisTaxon()
460 * @see #getTaxonRelations()
462 public Set
<TaxonRelationship
> getRelationsFromThisTaxon() {
463 if(relationsFromThisTaxon
== null) {
464 this.relationsFromThisTaxon
= new HashSet
<>();
466 return relationsFromThisTaxon
;
470 * Returns the set of all {@link TaxonRelationship taxon relationships}
471 * between two taxa in which <i>this</i> taxon is involved as a target.
473 * @see #getRelationsFromThisTaxon()
474 * @see #getTaxonRelations()
476 public Set
<TaxonRelationship
> getRelationsToThisTaxon() {
477 if(relationsToThisTaxon
== null) {
478 this.relationsToThisTaxon
= new HashSet
<>();
480 return relationsToThisTaxon
;
484 * Returns the set of all {@link TaxonRelationship taxon relationships}
485 * between two taxa in which <i>this</i> taxon is involved either as a source or
488 * @see #getRelationsFromThisTaxon()
489 * @see #getRelationsToThisTaxon()
492 public Set
<TaxonRelationship
> getTaxonRelations() {
493 Set
<TaxonRelationship
> rels
= new HashSet
<>();
494 rels
.addAll(getRelationsToThisTaxon());
495 rels
.addAll(getRelationsFromThisTaxon());
500 * @see #getRelationsToThisTaxon()
502 protected void setRelationsToThisTaxon(Set
<TaxonRelationship
> relationsToThisTaxon
) {
503 this.relationsToThisTaxon
= relationsToThisTaxon
;
507 * @see #getRelationsFromThisTaxon()
509 protected void setRelationsFromThisTaxon(Set
<TaxonRelationship
> relationsFromThisTaxon
) {
510 this.relationsFromThisTaxon
= relationsFromThisTaxon
;
514 * If a relationships between <i>this</i> and the given taxon exists they will be returned.
515 * <i>This</i> taxon is involved either as a source or as a target in the relationships.
516 * The method will return <code>null</code> if no relations exist between the two taxa.
518 * @param possiblyRelatedTaxon
519 * a taxon to check for a relationship
521 * a set of <code>TaxonRelationship</code>s or <code>null</null> if none exists.
523 public Set
<TaxonRelationship
> getTaxonRelations(Taxon possiblyRelatedTaxon
){
524 Set
<TaxonRelationship
> relations
= new HashSet
<>();
526 for(TaxonRelationship relationship
: getTaxonRelations()){
527 if(relationship
.getFromTaxon().equals(possiblyRelatedTaxon
)) {
528 relations
.add(relationship
);
530 if(relationship
.getToTaxon().equals(possiblyRelatedTaxon
)) {
531 relations
.add(relationship
);
535 return relations
.size() > 0 ? relations
: null;
539 * Removes one {@link TaxonRelationship taxon relationship} from one of both sets of
540 * {@link #getTaxonRelations() taxon relationships} in which <i>this</i> taxon is involved
541 * either as a {@link #getRelationsFromThisTaxon() source} or as a {@link #getRelationsToThisTaxon() target}.
542 * The taxon relationship will also be removed from one of both sets
543 * belonging to the second taxon involved. Furthermore the inherited RelatedFrom and
544 * RelatedTo attributes of the given taxon relationship will be nullified.<P>
546 * @param rel the taxon relationship which should be removed from one
548 * @see #getTaxonRelations()
549 * @see eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom()
550 * @see eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo()
553 public void removeTaxonRelation(TaxonRelationship rel
) {
554 this.relationsToThisTaxon
.remove(rel
);
555 this.relationsFromThisTaxon
.remove(rel
);
556 Taxon fromTaxon
= rel
.getFromTaxon();
557 Taxon toTaxon
= rel
.getToTaxon();
559 //delete Relationship from other related Taxon
560 if (fromTaxon
!= this){
561 rel
.setToTaxon(null); //remove this Taxon from relationship
562 if (fromTaxon
!= null){
563 if (fromTaxon
.getTaxonRelations().contains(rel
)){
564 fromTaxon
.removeTaxonRelation(rel
);
568 if (toTaxon
!= this ){
569 rel
.setFromTaxon(null); //remove this Taxon from relationship
570 if (toTaxon
!= null){
571 if (toTaxon
.getTaxonRelations().contains(rel
)) {
572 toTaxon
.removeTaxonRelation(rel
);
579 * Adds an existing {@link TaxonRelationship taxon relationship} either to the set of
580 * {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon} or to the set of
581 * {@link #getRelationsFromThisTaxon() taxon relationships from <i>this</i> taxon}. If neither the
582 * source nor the target of the taxon relationship match with <i>this</i> taxon
583 * no addition will be carried out. The taxon relationship will also be
584 * added to the second taxon involved in the given relationship.<P>
586 * @param rel the taxon relationship to be added to one of <i>this</i> taxon's taxon relationships sets
587 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
588 * @see #getTaxonRelations()
589 * @see #getRelationsFromThisTaxon()
590 * @see #getRelationsToThisTaxon()
592 public void addTaxonRelation(TaxonRelationship rel
) {
593 if (rel
!=null && rel
.getType()!=null && !getTaxonRelations().contains(rel
) ){
594 Taxon toTaxon
=rel
.getToTaxon();
595 Taxon fromTaxon
=rel
.getFromTaxon();
596 if ( this.equals(toTaxon
) || this.equals(fromTaxon
) ){
597 if (this.equals(fromTaxon
)){
598 relationsFromThisTaxon
.add(rel
);
599 // also add relation to other taxon object
601 toTaxon
.addTaxonRelation(rel
);
603 }else if (this.equals(toTaxon
)){
604 relationsToThisTaxon
.add(rel
);
605 // also add relation to other taxon object
606 if (fromTaxon
!=null){
607 fromTaxon
.addTaxonRelation(rel
);
610 }else if (toTaxon
== null || fromTaxon
== null){
611 if (toTaxon
== null){
613 relationsToThisTaxon
.add(rel
);
614 if (fromTaxon
!= null){
615 fromTaxon
.addTaxonRelation(rel
);
617 }else if (fromTaxon
== null && toTaxon
!= null){
619 relationsFromThisTaxon
.add(rel
);
620 toTaxon
.addTaxonRelation(rel
);
627 @Deprecated //for inner use by RelationshipBase only
628 public void addRelationship(RelationshipBase rel
){
629 if (rel
instanceof TaxonRelationship
){
630 addTaxonRelation((TaxonRelationship
)rel
);
632 throw new ClassCastException("Wrong Relationsship type for Taxon.addRelationship");
637 * Creates a new {@link TaxonRelationship taxon relationship} instance where <i>this</i> taxon
638 * plays the source role and adds it to the set of
639 * {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to <i>this</i> taxon.
640 * The taxon relationship will also be added to the set of taxon
641 * relationships to the second taxon involved in the created relationship.<P>
643 * @param toTaxon the taxon which plays the target role in the new taxon relationship
644 * @param type the taxon relationship type for the new taxon relationship
645 * @param citation the reference source for the new taxon relationship
646 * @param microcitation the string with the details describing the exact localisation within the reference
648 * @see #addTaxonRelation(TaxonRelationship)
649 * @see #getTaxonRelations()
650 * @see #getRelationsFromThisTaxon()
651 * @see #getRelationsToThisTaxon()
653 public TaxonRelationship
addTaxonRelation(Taxon toTaxon
, TaxonRelationshipType type
, Reference citation
, String microcitation
) {
654 return new TaxonRelationship(this, toTaxon
, type
, citation
, microcitation
);
658 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
659 * "misapplied name for") instance where <i>this</i> taxon plays the target role
660 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
661 * The taxon relationship will also be added to the set of taxon
662 * relationships to the other (misapplied name) taxon involved in the created relationship.
664 * @param misappliedNameTaxon the taxon which plays the source role in the new taxon relationship
665 * @param citation the reference source for the new taxon relationship
666 * @param microcitation the string with the details describing the exact localisation within the reference
668 * @see #getMisappliedNames()
669 * @see #addProParteMisappliedName(Taxon, Reference, String)
670 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
671 * @see #addTaxonRelation(TaxonRelationship)
672 * @see #getTaxonRelations()
673 * @see #getRelationsFromThisTaxon()
674 * @see #getRelationsToThisTaxon()
676 public TaxonRelationship
addMisappliedName(Taxon misappliedNameTaxon
, Reference citation
, String microcitation
) {
677 return misappliedNameTaxon
.addTaxonRelation(this, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), citation
, microcitation
);
680 // public void removeMisappliedName(Taxon misappliedNameTaxon){
681 // Set<TaxonRelationship> taxRels = this.getTaxonRelations();
682 // for (TaxonRelationship taxRel : taxRels ){
683 // if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())
684 // && taxRel.getFromTaxon().equals(misappliedNameTaxon)){
685 // this.removeTaxonRelation(taxRel);
691 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
692 * "pro parte misapplied name for") instance where <i>this</i> taxon plays the target role
693 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
694 * The taxon relationship will also be added to the set of taxon
695 * relationships to the other (pro parte misapplied name) taxon involved in the created relationship.
697 * @param proParteMisappliedNameTaxon the taxon which plays the source role in the new taxon relationship
698 * @param citation the reference source for the new taxon relationship
699 * @param microcitation the string with the details describing the exact localisation within the reference
701 * @see #addMisappliedName(Taxon, Reference, String)
702 * @see #getMisappliedNames()
703 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
704 * @see #addTaxonRelation(TaxonRelationship)
705 * @see #getTaxonRelations()
706 * @see #getRelationsFromThisTaxon()
707 * @see #getRelationsToThisTaxon()
709 public TaxonRelationship
addProParteMisappliedName(Taxon proParteMisappliedNameTaxon
, Reference citation
, String microcitation
) {
710 return proParteMisappliedNameTaxon
.addTaxonRelation(this, TaxonRelationshipType
.PRO_PARTE_MISAPPLIED_NAME_FOR(), citation
, microcitation
);
714 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
715 * "partial misapplied name for") instance where <i>this</i> taxon plays the target role
716 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
717 * The taxon relationship will also be added to the set of taxon
718 * relationships to the other (pro parte misapplied name) taxon involved in the created relationship.
720 * @param partialMisappliedNameTaxon the taxon which plays the source role in the new taxon relationship
721 * @param citation the reference source for the new taxon relationship
722 * @param microcitation the string with the details describing the exact localization within the reference
724 * @see #addMisappliedName(Taxon, Reference, String)
725 * @see #addProParteMisappliedName(Taxon, Reference, String)
726 * @see #getMisappliedNames()
727 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
728 * @see #addTaxonRelation(TaxonRelationship)
729 * @see #getTaxonRelations()
730 * @see #getRelationsFromThisTaxon()
731 * @see #getRelationsToThisTaxon()
733 public TaxonRelationship
addPartialMisappliedName(Taxon partialMisappliedNameTaxon
, Reference citation
, String microcitation
) {
734 return partialMisappliedNameTaxon
.addTaxonRelation(this, TaxonRelationshipType
.PARTIAL_MISAPPLIED_NAME_FOR(), citation
, microcitation
);
738 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
739 * "pro parte synonym for") instance where <i>this</i> taxon plays the target role
740 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
741 * The taxon relationship will also be added to the set of taxon
742 * relationships to the other (pro parte synonym) taxon involved in the created relationship.
744 * @param proParteTaxon the taxon which plays the source role in the new taxon relationship
745 * @param citation the reference source for the new taxon relationship
746 * @param microcitation the string with the details describing the exact localisation within the reference
748 * @see #getMisappliedNames()
749 * @see #addProParteMisappliedName(Taxon, Reference, String)
750 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
751 * @see #addTaxonRelation(TaxonRelationship)
752 * @see #getTaxonRelations()
753 * @see #getRelationsFromThisTaxon()
754 * @see #getRelationsToThisTaxon()
756 public TaxonRelationship
addProparteSynonym(Taxon proParteTaxon
, Reference citation
, String microcitation
) {
757 return proParteTaxon
.addTaxonRelation(this, TaxonRelationshipType
.PRO_PARTE_SYNONYM_FOR(), citation
, microcitation
);
761 * Creates a new {@link TaxonRelationship taxon relationship} instance with
762 * {@link TaxonRelationshipType taxon relationship type} {@link TaxonRelationshipType#PARTIAL_SYNONYM_FOR()
763 * partial synonym for} where <i>this</i> taxon plays the target role
764 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
765 * The taxon relationship will also be added to the set of taxon
766 * relationships to the other (partial synonym) taxon involved in the created relationship.
768 * @param partialTaxon the taxon which plays the source role in the new taxon relationship
769 * @param citation the reference source for the new taxon relationship
770 * @param microcitation the string with the details describing the exact localisation within the reference
772 * @see #addProparteSynonym(Taxon, Reference, String)
773 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
774 * @see #addTaxonRelation(TaxonRelationship)
775 * @see #getTaxonRelations()
776 * @see #getRelationsFromThisTaxon()
777 * @see #getRelationsToThisTaxon()
779 public TaxonRelationship
addPartialSynonym(Taxon partialTaxon
, Reference citation
, String microcitation
) {
780 return partialTaxon
.addTaxonRelation(this, TaxonRelationshipType
.PARTIAL_SYNONYM_FOR(), citation
, microcitation
);
784 * TODO update documentation
785 * Removes one {@link TaxonRelationship taxon relationship} with {@link TaxonRelationshipType taxon relationship type}
786 * taxonRelType and with the given child taxon playing the
787 * source role from the set of {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging
788 * to <i>this</i> taxon. The taxon relationship will also be removed from the set
789 * of {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to the other side taxon.
790 * Furthermore, the inherited RelatedFrom and RelatedTo attributes of the
791 * taxon relationship will be nullified.<P>
793 * @param taxon the taxon which plays the source role in the taxon relationship
794 * @param taxonRelType the taxon relationship type
796 public void removeTaxon(Taxon taxon
, TaxonRelationshipType taxonRelType
){
797 Set
<TaxonRelationship
> taxRels
= this.getTaxonRelations();
798 for (TaxonRelationship taxRel
: taxRels
){
799 if (taxRel
.getType().equals(taxonRelType
)
800 && taxRel
.getFromTaxon().equals(taxon
)){
801 this.removeTaxonRelation(taxRel
);
807 * Returns the boolean value indicating whether <i>this</i> taxon is a misapplication
808 * (misapplied name) for at least one other taxon.
810 // TODO cache as for #hasTaxonomicChildren
812 public boolean isMisapplication(){
813 return computeMisapliedNameRelations() > 0;
817 * Counts the number of misapplied name relationships (including pro parte and partial
818 * misapplied names) where this taxon represents the
819 * misapplied name for another taxon.
822 private int computeMisapliedNameRelations(){
824 for (TaxonRelationship rel
: this.getRelationsFromThisTaxon()){
825 if (rel
.getType().isAnyMisappliedName()){
833 * Returns the boolean value indicating whether <i>this</i> taxon is a misapplication
834 * (misapplied name) for at least one other taxon.
836 // TODO cache as for #hasTaxonomicChildren
838 public boolean isProparteSynonym(){
839 return computeProparteSynonymRelations() > 0;
843 * Counts the number of misapplied name relationships (including pro parte misapplied
844 * names) where this taxon represents the
845 * misapplied name for another taxon.
848 private int computeProparteSynonymRelations(){
850 for (TaxonRelationship rel
: this.getRelationsFromThisTaxon()){
851 if (rel
.getType().isAnySynonym()){
859 * Returns the boolean value indicating whether <i>this</i> taxon is a related
860 * concept for at least one other taxon.
863 public boolean isRelatedConcept(){
864 return computeConceptRelations() > 0;
868 * Counts the number of concept relationships where this taxon represents the
869 * related concept for another taxon.
872 private int computeConceptRelations(){
874 for (TaxonRelationship rel
: this.getRelationsFromThisTaxon()){
875 TaxonRelationshipType type
= rel
.getType();
876 if (type
.isConceptRelationship()){
884 * Returns the boolean value indicating whether <i>this</i> taxon has at least one
885 * {@link Synonym synonym} (true) or not (false). If true the {@link #getSynonyms() set of synonyms}
886 * belonging to <i>this</i> ("accepted/valid") taxon is not empty .
888 * @see #getSynonyms()
889 * @see #getSynonymNames()
890 * @see #removeSynonym(Synonym)
893 public boolean hasSynonyms(){
894 return this.getSynonyms().size() > 0;
898 * Returns the boolean value indicating whether <i>this</i> taxon is at least
899 * involved in one {@link #getTaxonRelations() taxon relationship} between
900 * two taxa (true), either as a source or as a target, or not (false).
902 * @see #getTaxonRelations()
903 * @see #getRelationsToThisTaxon()
904 * @see #getRelationsFromThisTaxon()
905 * @see #removeTaxonRelation(TaxonRelationship)
906 * @see TaxonRelationship
908 public boolean hasTaxonRelationships(){
909 return this.getTaxonRelations().size() > 0;
916 * Returns the set of taxa playing the source role in {@link TaxonRelationship taxon relationships}
917 * (with {@link TaxonRelationshipType taxon relationship type} "misapplied name for") where
918 * <i>this</i> taxon plays the target role. A misapplied name is a taxon the
919 * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} of which has been erroneously used
920 * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
921 * as the one meant by <i>this</i> ("accepted/correct") taxon.
923 * @see #getTaxonRelations()
924 * @see #getRelationsToThisTaxon()
925 * @see #addMisappliedName(Taxon, Reference, String)
926 * @param includeNonCongruent if <code>true</code> also those taxa are returned that are related
927 * via a non congruent relationship like {@link TaxonRelationshipType#PRO_PARTE_MISAPPLIED_NAME_FOR()
928 * pro parte misapplied name}
931 public Set
<Taxon
> getMisappliedNames(boolean includeNonCongruent
){
932 Set
<Taxon
> taxa
= new HashSet
<>();
933 Set
<TaxonRelationship
> rels
= this.getRelationsToThisTaxon();
934 for (TaxonRelationship rel
: rels
){
935 TaxonRelationshipType relType
= rel
.getType();
936 if ( (includeNonCongruent
&& relType
.isAnyMisappliedName())
937 || relType
.equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR())){
938 taxa
.add(rel
.getFromTaxon());
945 * Returns the set of misapplied name relationships in which this taxon
946 * plays the role of the correctly accepted taxon (target). A misapplied name is a taxon the
947 * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} of which has been erroneously used
948 * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
949 * as the one meant by <i>this</i> ("accepted/correct") taxon.
952 public Set
<TaxonRelationship
> getMisappliedNameRelations(){
953 Set
<TaxonRelationship
> result
= new HashSet
<>();
954 Set
<TaxonRelationship
> rels
= this.getRelationsToThisTaxon();
955 for (TaxonRelationship rel
: rels
){
956 TaxonRelationshipType relType
= rel
.getType();
957 if (relType
.isAnyMisappliedName()){
965 * Returns the set of taxa playing the target role in {@link TaxonRelationship taxon relationships}
966 * (with {@link TaxonRelationshipType taxon relationship type} "misapplied name for"
967 * or "pro parte misapplied name for") where
968 * <i>this</i> taxon plays the source role. A misapplied name is a taxon the
969 * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} of which has been erroneously used
970 * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
971 * as the one meant by <i>this</i> ("accepted/correct") taxon.
973 * @param includeNonCongruent if <code>true</code> also those taxa are returned that are related
974 * via a non congruent relationship like {@link TaxonRelationshipType#PRO_PARTE_MISAPPLIED_NAME_FOR()
975 * pro parte misapplied name}
977 * @see #getTaxonRelations()
978 * @see #getRelationsToThisTaxon()
979 * @see #addMisappliedName(Taxon, Reference, String)
980 * @see #addProParteMisappliedName(Taxon, Reference, String)
983 public Set
<Taxon
> getTaxaForMisappliedName(boolean includeNonCongruent
){
984 Set
<Taxon
> taxa
= new HashSet
<>();
985 Set
<TaxonRelationship
> rels
= this.getRelationsFromThisTaxon();
986 for (TaxonRelationship rel
: rels
){
987 TaxonRelationshipType relType
= rel
.getType();
988 if ( (includeNonCongruent
&& relType
.isAnyMisappliedName())
989 || relType
.equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR())){
990 taxa
.add(rel
.getToTaxon());
997 * Returns the set of pro parte or partial synonym relationships in which this taxon
998 * plays the role of the "correctly" accepted taxon (target).
1000 * @see #getProParteAndPartialSynonyms()
1001 * @see #getMisappliedNameRelations()
1004 public Set
<TaxonRelationship
> getProParteAndPartialSynonymRelations(){
1005 Set
<TaxonRelationship
> result
= new HashSet
<>();
1006 Set
<TaxonRelationship
> rels
= this.getRelationsToThisTaxon();
1007 for (TaxonRelationship rel
: rels
){
1008 TaxonRelationshipType relType
= rel
.getType();
1009 if (relType
.isAnySynonym()){
1017 * Returns the set of pro parte or partial synonyms in which this taxon
1018 * plays the role of the "correctly" accepted taxon (target).
1020 * @see #getProParteAndPartialSynonymRelations()
1021 * @see #getMisappliedNames(boolean)
1024 public Set
<Taxon
> getProParteAndPartialSynonyms(){
1025 Set
<Taxon
> synonyms
= new HashSet
<>();
1026 Set
<TaxonRelationship
> rels
= this.getProParteAndPartialSynonymRelations();
1027 for (TaxonRelationship rel
: rels
){
1028 synonyms
.add(rel
.getFromTaxon());
1034 * Returns the set of all {@link TaxonName taxon names} used as {@link Synonym synonyms}
1035 * of <i>this</i> ("accepted/valid") taxon.
1037 * @see #getSynonyms()
1038 * @see #getSynonymsSortedByType()
1039 * @see #addSynonymName(TaxonName, SynonymType)
1040 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1041 * @see #removeSynonym(Synonym)
1044 public Set
<TaxonName
> getSynonymNames(){
1045 Set
<TaxonName
> names
= new HashSet
<>();
1046 for (Synonym syn
: this.getSynonyms()){
1047 names
.add(syn
.getName());
1053 * Might be public in future. For the moment protected to ensure that
1054 * synonym type is always set after refactoring.
1058 protected void addSynonym(Synonym synonym
){
1059 if (! this.equals(synonym
.getAcceptedTaxon())){
1060 synonym
.setAcceptedTaxon(this);
1062 if (!synonyms
.contains(synonym
)){
1063 synonyms
.add(synonym
);
1068 * Adds the given {@link Synonym synonym} to <code>this</code> taxon
1069 * and changes the {@link SynonymType
1070 * synonym type} before.
1072 * @param synonym the synonym to be added
1073 * @param synonymType the synonym type of the synonym to be added. If not <code>null</code>
1074 * and if the synonym already has a type the existing type will be overwritten.<BR>
1075 * If synonymType is {@link SynonymType#HOMOTYPIC_SYNONYM_OF()}
1076 * the homotypic group of the synonym is changed to that of <code>this</code> taxon.<BR>
1077 * To explicitly set the type to <code>null</code> use {@link Synonym#setType(SynonymType)}
1078 * @see #addSynonym(Synonym)
1079 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1080 * @see #addSynonymName(TaxonName, SynonymType)
1081 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1082 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1083 * @see #addHeterotypicSynonymName(TaxonName)
1084 * @see #addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1085 * @see #getSynonyms()
1086 * @see #removeSynonym(Synonym)
1087 * @see Synonym#getAcceptedTaxon()
1089 public void addSynonym(Synonym synonym
, SynonymType synonymType
){
1090 synonym
.setType(synonymType
); //must be set before as otherwise merging of homotypical groups may not work correctly in Synonym.checkHomotypic()
1091 addSynonym(synonym
);
1095 * Adds the given {@link Synonym synonym} with the given {@link SynonymType
1096 * synonym relationship type}
1098 * @param synonym the synonym to be added
1099 * @param synonymType the synonym type of the synonym to be added. If not null
1100 * and if the synonym already has a type the existing type will be overwritten.
1101 // * @param citation the reference source for the new synonym relationship
1102 // * @param microcitation the string with the details describing the exact localization within the reference
1103 * @see #addSynonym(Synonym)
1104 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1105 * @see #addSynonymName(TaxonName, SynonymType)
1106 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1107 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1108 * @see #addHeterotypicSynonymName(TaxonName)
1109 * @see #addHeterotypicSynonymName(TaxonName, HomotypicalGroup, Reference, String)
1110 * @see #getSynonyms()
1111 * @see #removeSynonym(Synonym)
1112 * @see Synonym#getAcceptedTaxon()
1114 private void addSynonym(Synonym synonym
, SynonymType synonymType
, Reference newSecReference
, String newSecMicroReference
){
1115 if (newSecReference
!= null){
1116 synonym
.setSec(newSecReference
);
1118 if (newSecMicroReference
!= null){
1119 synonym
.setSecMicroReference(newSecMicroReference
);
1121 addSynonym(synonym
, synonymType
);
1126 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the
1127 * given {@link TaxonName synonym name} and with the given
1128 * {@link SynonymType synonym type}. If the later is
1129 * {@link SynonymType#HOMOTYPIC_SYNONYM_OF() homotypic synonym}
1130 * the name will be added to the same {@link HomotypicalGroup homotypical group}
1131 * as the <code>this</code> accepted taxon.<BR>
1132 * The secundum reference of the new synonym is taken from <code>this</code> taxon.
1133 * A secundum detail is not set.
1135 * @param synonymName the taxon name to be used as a synonym to be added
1136 * to <i>this</i> taxon's set of synonyms
1137 * @param synonymType the synonym type of the synonym
1138 * relationship to be added
1139 * @return the created synonym
1140 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1141 * @see #addSynonym(Synonym, SynonymType)
1142 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1143 * @see #addHomotypicSynonym(Synonym, Reference, String)
1144 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1145 * @see #addHeterotypicSynonymName(TaxonName)
1146 * @see #addHeterotypicSynonymName(TaxonName, HomotypicalGroup, Reference, String)
1147 * @see #getSynonyms()
1148 * @see #removeSynonym(Synonym)
1150 public Synonym
addSynonymName(TaxonName synonymName
, SynonymType synonymType
){
1151 return addSynonymName(synonymName
, null, null, synonymType
);
1155 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the
1156 * given {@link TaxonName synonym name} and with the given
1157 * {@link SynonymType synonym type}. If the later is
1158 * {@link SynonymType#HOMOTYPIC_SYNONYM_OF() homotypic synonym}
1159 * the name will be added to the same {@link HomotypicalGroup homotypical group}
1160 * as the <code>this</code> accepted taxon.<BR>
1162 * If secReference is not <code>null</code>, the new synonym will have this as
1163 * secundum reference. Otherwise <code>this</code> taxons sec reference is taken
1164 * as secundum reference for the synonym. SecDetail will be the secMicroReference of the
1167 * @param synonymName the taxon name to be used as a synonym to be added
1168 * to <i>this</i> taxon's set of synonyms
1169 * @param secReference the secundum reference for the new synonym (if <code>null</code>
1170 * <code>this</code> taxon's secundum reference is taken.
1171 * @param secMicroReference the secundum micro reference of the new synonym
1172 * @param synonymType the synonym type of the synonym to be added
1174 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1175 * @see #addSynonym(Synonym, SynonymType)
1176 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1177 * @see #addHomotypicSynonym(Synonym, Reference, String)
1178 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1179 * @see #addHeterotypicSynonymName(TaxonName)
1180 * @see #addHeterotypicSynonymName(TaxonName, HomotypicalGroup, Reference, String)
1181 * @see #getSynonyms()
1182 * @see #removeSynonym(Synonym)
1184 public Synonym
addSynonymName(TaxonName synonymName
, Reference secReference
, String secMicroReference
, SynonymType synonymType
){
1185 Synonym synonym
= Synonym
.NewInstance(synonymName
, this.getSec()); //default sec
1186 synonym
.setPublish(this.isPublish());
1187 addSynonym(synonym
, synonymType
, secReference
, secMicroReference
);
1192 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the given
1193 * {@link TaxonName synonym name}. The synonym will have the synonym type
1194 * {@link SynonymType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of"}.<BR>
1195 * The secundum reference is taken from <code>this</code> taxon.
1196 * No secMicroReference will be set for the new synonym.<BR>
1197 * The synonym will keep it's old homotypical group.<BR>
1199 * @param synonymName the taxon name to be used as an heterotypic synonym
1200 * to be added to <i>this</i> taxon's set of synonyms
1201 * @return the created synonym
1202 * @see #addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1203 * @see #addSynonymName(TaxonName, SynonymType)
1204 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1205 * @see #addSynonym(Synonym, SynonymType)
1206 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1207 * @see #addHomotypicSynonym(Synonym, Reference, String)
1208 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1209 * @see #getSynonyms()
1210 * @see #removeSynonym(Synonym)
1212 public Synonym
addHeterotypicSynonymName(TaxonName synonymName
){
1213 return addHeterotypicSynonymName(synonymName
, null, null, null);
1217 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the given
1218 * {@link TaxonName synonym name}. The synonym will have the synonym type
1219 * {@link SynonymType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of"}.<BR>
1221 * If secReference is not <code>null</code>, the new synonym will have this as
1222 * secundum reference. Otherwise <code>this</code> taxons sec reference is taken
1223 * as secundum reference for the synonym. SecDetail will be the secMicroReference of the
1225 * Furthermore the taxon name used as synonym will be added
1226 * to the given {@link name.HomotypicalGroup homotypical group} (if not <code>null</code>).<BR>
1228 * @param synonymName the taxon name to be used as an heterotypic synonym
1229 * to be added to <i>this</i> taxon's set of synonyms
1230 * @param secReference the secundum reference for the new synonym
1231 * @param secDetail the secundum detil for the new synonym
1232 * @param homotypicalGroup the homotypical group to which the taxon name
1233 * of the synonym will be added. If <code>null</code>
1234 * the homotypical group of synonymName is not changed
1235 * @return the created synonym
1236 * @see #addHeterotypicSynonymName(TaxonName)
1237 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1238 * @see #addSynonymName(TaxonName, SynonymType)
1239 * @see #addSynonym(Synonym, SynonymType)
1240 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1241 * @see #addHomotypicSynonym(Synonym, Reference, String)
1242 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1243 * @see #getSynonyms()
1244 * @see #removeSynonym(Synonym)
1246 public Synonym
addHeterotypicSynonymName(TaxonName synonymName
, Reference secReference
, String secDetail
, HomotypicalGroup homotypicalGroup
){
1247 Synonym synonym
= Synonym
.NewInstance(synonymName
, this.getSec());
1248 if (homotypicalGroup
!= null){
1249 homotypicalGroup
.addTypifiedName(synonymName
);
1251 synonym
.setPublish(this.isPublish());
1253 addSynonym(synonym
, SynonymType
.HETEROTYPIC_SYNONYM_OF(), secReference
, secDetail
);
1258 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the given
1259 * {@link TaxonName synonym name}. The synonym will have the synonym type
1260 * {@link SynonymType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of"}.<BR>
1261 * The secundum reference is taken from <code>this</code> taxon.
1262 * No secMicroReference will be set for the new synonym.<BR>
1263 * The synonym's homotypic group will be changed to <code>this</code> taxon's group.<BR>
1265 * @param synonymName the taxon name to be used as an homotypic synonym
1266 * to be added to <i>this</i> taxon's set of synonyms
1267 * @return the created synonym
1268 * @see #addHomotypicSynonym(Synonym, Reference, String)
1269 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1270 * @see #addSynonymName(TaxonName, SynonymType)
1271 * @see #addSynonym(Synonym, SynonymType)
1272 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1273 * @see #addHeterotypicSynonymName(TaxonName)
1274 * @see #addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1275 * @see #getSynonyms()
1276 * @see #removeSynonym(Synonym)
1278 public Synonym
addHomotypicSynonymName(TaxonName synonymName
){
1279 Synonym synonym
= Synonym
.NewInstance(synonymName
, this.getSec());
1280 synonym
.setPublish(this.isPublish());
1281 addHomotypicSynonym(synonym
);
1286 * Adds the given {@link Synonym synonym} to <code>this</code> taxon,
1287 * with the {@link SynonymType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of"
1288 * relationship type} and returns it.
1289 * Furthermore the {@link TaxonName taxon name}
1290 * used as synonym will be added to the same {@link HomotypicalGroup homotypic group}
1291 * to which the taxon name of <i>this</i> taxon belongs.<BR>
1293 * @param synonym the synonym added to <i>this</i> taxon's synonym set
1294 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1295 * @see #addSynonym(Synonym, SynonymType)
1296 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1297 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1298 * @see #addSynonymName(TaxonName, SynonymType)
1299 * @see #addHeterotypicSynonymName(TaxonName)
1300 * @see #addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1301 * @see #getSynonyms()
1302 * @see #removeSynonym(Synonym)
1304 public void addHomotypicSynonym(Synonym synonym
){
1305 if (!this.getSynonyms().contains(synonym
)){
1306 addSynonym(synonym
, SynonymType
.HOMOTYPIC_SYNONYM_OF());
1308 logger
.warn("Tried to add a synonym to an accepted taxon that already is a synonym of this taxon.");
1314 * Like {@link #removeSynonym(Synonym, boolean)} with <code>removeSynonymNameFromHomotypicalGroup</code> set to true.
1315 * @see #removeSynonym(Synonym, boolean)
1317 public void removeSynonym(Synonym synonym
){
1318 removeSynonym(synonym
, true);
1323 * Removes one element from the set of {@link Synonym synonyms} assigned
1324 * to <i>this</i> (accepted/valid) taxon.
1326 * @param synonym the synonym to be removed
1327 * @param removeSynonymNameFromHomotypicalGroup
1328 * if <code>true</code> the synonym name will also be deleted from its homotypical group if the
1329 * group contains other names
1330 * @see #getSynonyms()
1331 * @see #removeSynonym(Synonym)
1333 public void removeSynonym(Synonym synonym
, boolean removeSynonymNameFromHomotypicalGroup
) {
1334 if (synonym
!= null && this.equals(synonym
.getAcceptedTaxon())){
1335 if(removeSynonymNameFromHomotypicalGroup
){
1336 HomotypicalGroup synHG
= synonym
.getName().getHomotypicalGroup();
1337 if (synHG
.getTypifiedNames().size() > 1){
1338 synHG
.removeTypifiedName(synonym
.getName(), false);
1341 this.synonyms
.remove(synonym
);
1342 synonym
.setAcceptedTaxon(null);
1347 * @see #getHomotypicSynonymsByHomotypicGroup(TaxonComparator)
1350 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(){
1351 return getHomotypicSynonymsByHomotypicGroup(null);
1355 * Retrieves the ordered list (depending on the date of publication) of
1356 * homotypic {@link Synonym synonyms} (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1357 * as for <i>this</i> taxon) under the condition that the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon names}
1358 * of these synonyms and the taxon name of <i>this</i> taxon belong to the
1359 * same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group}.
1361 * @param comparator the taxon comparator to use, if <code>null</code> the default comparator is taken.
1362 * @return the ordered list of homotypic synonyms
1363 * @see #getHomotypicSynonymsByHomotypicSynonymType()
1364 * @see #getSynonyms()
1365 * @see #getHomotypicSynonymyGroups()
1366 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup
1367 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup#getSynonymsInGroup(Reference)
1370 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(TaxonComparator comparator
){
1371 if (this.getHomotypicGroup() == null){
1373 }else if (comparator
== null){
1374 return this.getSynonymsInGroup(this.getHomotypicGroup());
1376 return this.getSynonymsInGroup(this.getHomotypicGroup(), comparator
);
1381 * Retrieves the list of homotypic {@link Synonym synonyms}
1382 * (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1383 * as for <i>this</i> taxon) under the condition that these synonyms and
1384 * <i>this</i> taxon are involved in {@link SynonymRelationship synonym relationships} with an
1385 * "is homotypic synonym of" {@link SynonymType#HOMOTYPIC_SYNONYM_OF() synonym relationship type}.
1387 * @return the ordered list of homotypic synonyms
1388 * @see #getHomotypicSynonymsByHomotypicGroup()
1389 * @see #getSynonyms()
1390 * @see #getHomotypicSynonymyGroups()
1392 * @deprecated as the method currently returns data not matching the original description of the method
1393 * as an ordered list (according to date of publication) of synonyms with same secundum as <i>this</i> taxon.<BR>
1394 * In future this method will either be removed or semantics may change.
1398 public List
<Synonym
> getHomotypicSynonymsByHomotypicSynonymType(){
1399 Set
<Synonym
> synonyms
= this.getSynonyms();
1400 List
<Synonym
> result
= new ArrayList
<>();
1401 for(Synonym synonym
: synonyms
) {
1402 if(synonym
.getType().equals(SynonymType
.HOMOTYPIC_SYNONYM_OF())){
1403 result
.add(synonym
);
1410 * Returns the ordered list of all {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups} {@link Synonym synonyms} of
1411 * <i>this</i> taxon belong to. {@link eu.etaxonomy.cdm.model.name.TaxonName Taxon names} of homotypic synonyms
1412 * belong to the same homotypical group as the taxon name of <i>this</i>
1413 * taxon. Taxon names of heterotypic synonyms belong to at least one other
1414 * homotypical group. <BR>
1415 * The list returned is ordered according to the date of publication of the
1416 * first published name within each homotypical group.
1418 * @see #getHeterotypicSynonymyGroups()
1419 * @see #getSynonyms()
1420 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup
1423 public List
<HomotypicalGroup
> getHomotypicSynonymyGroups(){
1424 List
<HomotypicalGroup
> result
= new ArrayList
<>();
1425 HomotypicalGroup myGroup
= this.getHomotypicGroup();
1426 if (myGroup
!= null){ //if taxon has no name HG might be null
1427 result
.add(myGroup
);
1429 for (TaxonName taxonName
:this.getSynonymNames()){
1430 if (taxonName
!= null) {
1431 if (!result
.contains(taxonName
.getHomotypicalGroup())){
1432 result
.add(taxonName
.getHomotypicalGroup());
1441 * <BR>Also returns <code>false</code> if it is a misapplied name or has a similar concept relationship that
1442 * is similar to synonym relationship (shows up in the synonymy of applications)
1446 public boolean isOrphaned() {
1448 if(taxonNodes
== null || taxonNodes
.isEmpty()) {
1449 if(getRelationsFromThisTaxon().isEmpty()) {
1452 for (TaxonRelationship rel
: getRelationsFromThisTaxon()){
1453 if (rel
.getType() != null && ! rel
.getType().isConceptRelationship()){
1454 return false; //a synonym relationship type similar relationship exists => not orphaned
1457 return true; //all relations are real concept relations and therefore not relevant
1465 * Returns the ordered list of all
1466 * {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups}
1467 * that contain {@link Synonym synonyms} that are heterotypic to <i>this</i> taxon.<BR>
1469 * {@link eu.etaxonomy.cdm.model.name.TaxonName Taxon names} of heterotypic synonyms
1470 * belong to a homotypical group which cannot be the homotypical group to which the
1471 * taxon name of <i>this</i> taxon belongs.
1472 * This method returns the same
1473 * list as the {@link #getHomotypicSynonymyGroups() getHomotypicSynonymyGroups} method
1474 * but without the homotypical group to which the taxon name of <i>this</i> taxon
1476 * The list returned is <B>ordered</B> according to the rules defined for
1477 * the {@link HomotypicGroupTaxonComparator} which includes 1) grouping of
1478 * basionym groups, 2) replaced synonym relationships, 3) publication date,
1479 * 4) ranks and 5) alphabetical order.
1481 * @see #getHeterotypicSynonymyGroups()
1482 * @see #getSynonyms()
1483 * @see SynonymType#HETEROTYPIC_SYNONYM_OF()
1484 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup
1487 public List
<HomotypicalGroup
> getHeterotypicSynonymyGroups(){
1488 List
<HomotypicalGroup
> list
= getHomotypicSynonymyGroups();
1489 //remove homotypic group
1490 list
.remove(this.getHomotypicGroup());
1492 Map
<Synonym
, HomotypicalGroup
> map
= new HashMap
<>();
1493 for (HomotypicalGroup homotypicalGroup
: list
){
1494 List
<Synonym
> synonymList
= getSynonymsInGroup(homotypicalGroup
);
1495 if (synonymList
.size() > 0){
1496 //select the first synonym in the group
1497 map
.put(synonymList
.get(0), homotypicalGroup
);
1500 List
<Synonym
> keyList
= new ArrayList
<>();
1501 keyList
.addAll(map
.keySet());
1502 //order by first synonym
1503 Collections
.sort(keyList
, defaultTaxonComparator
);
1505 List
<HomotypicalGroup
> result
= new ArrayList
<>();
1506 for(Synonym synonym
: keyList
){
1507 //"replace" synonyms by homotypic groups
1508 result
.add(map
.get(synonym
));
1515 * Retrieves the ordered list (depending on the rules defined for
1516 * the {@link HomotypicGroupTaxonComparator}) of
1517 * {@link taxon.Synonym synonyms} (according to a given reference)
1518 * the {@link TaxonName taxon names} of which belong to the homotypical group.
1519 * If other names are part of the group that are not considered synonyms of
1520 * <i>this</i> taxon, then they will not be included in
1523 * @param homotypicGroup
1524 * @see #getHeterotypicSynonymyGroups()
1525 * @see TaxonName#getSynonyms()
1526 * @see TaxonName#getTaxa()
1527 * @see taxon.Synonym
1530 public List
<Synonym
> getSynonymsInGroup(HomotypicalGroup homotypicGroup
){
1531 return getSynonymsInGroup(homotypicGroup
, new HomotypicGroupTaxonComparator(this));
1535 * @param homotypicGroup
1538 * @see #getSynonymsInGroup(HomotypicalGroup)
1539 * @see #getHeterotypicSynonymyGroups()
1542 public List
<Synonym
> getSynonymsInGroup(HomotypicalGroup homotypicGroup
, TaxonComparator comparator
){
1543 List
<Synonym
> result
= new ArrayList
<>();
1544 if (homotypicGroup
== null){
1545 return result
; //always empty
1548 for (Synonym synonym
: this.getSynonyms()){
1549 if (homotypicGroup
.equals(synonym
.getHomotypicGroup())){
1550 result
.add(synonym
);
1554 Collections
.sort(result
, comparator
);
1559 * @see #getSynonymsGroups()
1562 public List
<Taxon
> getAllMisappliedNames(){
1563 List
<Taxon
> result
= new ArrayList
<>();
1565 for (TaxonRelationship rel
: this.getRelationsToThisTaxon()){
1566 if (rel
.getType().isAnyMisappliedName() ){
1567 result
.add(rel
.getFromTaxon());
1570 sortBySimpleTitleCacheComparator(result
);
1576 * @see #getSynonymsGroups()
1579 public List
<Taxon
> getAllProParteSynonyms(){
1580 List
<Taxon
> result
= new ArrayList
<>();
1582 for (TaxonRelationship rel
: this.getRelationsToThisTaxon()){
1583 if (rel
.getType().isAnySynonym()){
1584 result
.add(rel
.getFromTaxon());
1587 sortBySimpleTitleCacheComparator(result
);
1592 // * @see #getSynonymsGroups()
1595 // public List<Taxon> getProParteSynonyms(){
1596 // List<Taxon> result = new ArrayList<>();
1598 // for (TaxonRelationship rel : this.getRelationsToThisTaxon()){
1599 // if (rel.getType().isProParte()){
1600 // result.add(rel.getFromTaxon());
1603 // sortBySimpleTitleCacheComparator(result);
1608 // * @see #getSynonymsGroups()
1611 // public List<Taxon> getPartialSynonyms(){
1612 // List<Taxon> result = new ArrayList<>();
1614 // for (TaxonRelationship rel : this.getRelationsToThisTaxon()){
1615 // if (rel.getType().isPartial()){
1616 // result.add(rel.getFromTaxon());
1619 // sortBySimpleTitleCacheComparator(result);
1622 private void sortBySimpleTitleCacheComparator(List
<Taxon
> result
) {
1624 Comparator
<Taxon
> taxonComparator
= new Comparator
<Taxon
>(){
1627 public int compare(Taxon o1
, Taxon o2
) {
1629 if (o1
.getTitleCache() == o2
.getTitleCache()){
1632 if (o1
.getTitleCache() == null){
1635 if (o2
.getTitleCache() == null){
1638 return o1
.getTitleCache().compareTo(o2
.getTitleCache());
1642 Collections
.sort(result
, taxonComparator
);
1646 * Returns the image gallery description. If no image gallery exists, a new one is created using the
1647 * defined title and adds the string "-Image Gallery" to the title.</BR>
1648 * If multiple image galleries exist an arbitrary one is choosen.
1652 public TaxonDescription
getOrCreateImageGallery(String title
){
1653 return getOrCreateImageGallery(title
, true, false);
1657 * Returns the image gallery description. If no image gallery exists, a new one is created using the
1658 * defined title.</BR>
1659 * If onlyTitle == true we look only for an image gallery with this title, create a new one otherwise.
1660 * If multiple image galleries exist that match the conditions an arbitrary one is choosen.
1663 * @param if true, the String "Image Gallery
1667 public TaxonDescription
getOrCreateImageGallery(String title
, boolean addImageGalleryToTitle
, boolean onlyTitle
){
1668 TaxonDescription result
= null;
1669 String titleCache
= (title
== null) ?
"Image Gallery" : title
;
1670 if (title
!= null && addImageGalleryToTitle
){
1671 titleCache
= titleCache
+ "-Image Gallery";
1673 Set
<TaxonDescription
> descriptionSet
= this.getDescriptions();
1674 for (TaxonDescription desc
: descriptionSet
){
1675 if (desc
.isImageGallery()){
1676 if (onlyTitle
&& ! titleCache
.equals(desc
.getTitleCache())){
1680 if (onlyTitle
&& titleCache
.equals(desc
.getTitleCache())){
1685 if (result
== null){
1686 result
= TaxonDescription
.NewInstance();
1687 result
.setTitleCache(titleCache
, true);
1688 this.addDescription(result
);
1689 result
.setImageGallery(true);
1694 public void clearDescriptions() {
1695 this.descriptions
= new HashSet
<>();
1699 * Compiles all description items attached to this taxon having the given feature
1700 * and being of the given class. If feature or clazz is null no according filter
1703 public <T
extends DescriptionElementBase
> Set
<T
> getDescriptionItems(Feature feature
, Class
<T
> clazz
) {
1704 Set
<T
> result
= new HashSet
<>();
1705 Set
<TaxonDescription
> descriptions
= this.getDescriptions();
1706 for (TaxonDescription description
: descriptions
) {
1707 for (DescriptionElementBase deb
: description
.getElements()) {
1708 if (clazz
== null || deb
.isInstanceOf(clazz
)) {
1709 if (feature
== null || feature
.equals(deb
.getFeature())) {
1710 T matchingDeb
= CdmBase
.deproxy(deb
, clazz
);
1711 result
.add(matchingDeb
);
1719 //*********************** CLONE ********************************************************/
1722 * Clones <i>this</i> taxon. This is a shortcut that enables to create
1723 * a new instance that differs only slightly from <i>this</i> taxon by
1724 * modifying only some of the attributes.<BR><BR>
1725 * The TaxonNodes are not cloned, the list is empty.<BR>
1726 * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1727 * The taxon relationships and synonym relationships are cloned <BR>
1729 * @see eu.etaxonomy.cdm.model.taxon.TaxonBase#clone()
1730 * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1731 * @see java.lang.Object#clone()
1734 public Taxon
clone() {
1735 return clone(true, true, true, true);
1738 public Taxon
clone(boolean withSynonyms
, boolean withTaxonRelations
, boolean withDescriptions
,
1739 boolean withMedia
) {
1742 result
= (Taxon
)super.clone();
1744 result
.setRelationsFromThisTaxon(new HashSet
<>());
1745 result
.setRelationsToThisTaxon(new HashSet
<>());
1747 if (withTaxonRelations
|| withSynonyms
){
1748 for (TaxonRelationship fromRelationship
: this.getRelationsFromThisTaxon()){
1749 boolean isSynonymRelation
= fromRelationship
.getType() != null &&
1750 fromRelationship
.getType().isAnySynonymOrMisappliedName();
1751 if (isSynonymRelation
&& withSynonyms
|| !isSynonymRelation
&& withTaxonRelations
){
1752 TaxonRelationship newRelationship
= fromRelationship
.clone();
1753 newRelationship
.setRelatedFrom(result
);
1754 result
.relationsFromThisTaxon
.add(newRelationship
);
1758 for (TaxonRelationship toRelationship
: this.getRelationsToThisTaxon()){
1759 boolean isSynonymRelation
= toRelationship
.getType() != null &&
1760 toRelationship
.getType().isAnySynonymOrMisappliedName();
1761 if (isSynonymRelation
&& withSynonyms
|| !isSynonymRelation
&& withTaxonRelations
){
1762 TaxonRelationship newRelationship
= toRelationship
.clone();
1763 newRelationship
.setRelatedTo(result
);
1764 result
.relationsToThisTaxon
.add(newRelationship
);
1769 //clone synonyms (is this wanted or should we remove synonyms
1770 result
.synonyms
= new HashSet
<>();
1772 for (Synonym synonym
: this.getSynonyms()){
1773 Synonym newSyn
= synonym
.clone();
1774 newSyn
.setAcceptedTaxon(result
);
1778 result
.descriptions
= new HashSet
<>();
1779 for (TaxonDescription description
: this.getDescriptions()){
1780 if (description
.isImageGallery() && withMedia
||
1781 !description
.isImageGallery() && withDescriptions
){
1782 TaxonDescription newDescription
= description
.clone();
1783 result
.addDescription(newDescription
);
1787 result
.taxonNodes
= new HashSet
<>();
1789 /*for (TaxonNode taxonNode : this.getTaxonNodes()){
1790 TaxonNode newTaxonNode = (TaxonNode)taxonNode.clone();
1791 newTaxonNode.setTaxon(result);
1792 result.addTaxonNode(newTaxonNode);