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 eu
.etaxonomy
.cdm
.model
.common
.IRelated
;
14 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
15 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
16 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
17 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
18 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceBase
;
20 import org
.apache
.log4j
.Logger
;
21 import org
.hibernate
.annotations
.Cascade
;
22 import org
.hibernate
.annotations
.CascadeType
;
24 import java
.lang
.reflect
.Method
;
27 import javax
.persistence
.*;
30 * An accepted potential taxon defined by the combination of a Name and a sec reference
31 * {@link Iterable} interface is supported to iterate through taxonomic children
34 * @created 08-Nov-2007 13:06:56
37 public class Taxon
extends TaxonBase
implements Iterable
<Taxon
>, IRelated
<RelationshipBase
>{
38 static Logger logger
= Logger
.getLogger(Taxon
.class);
39 private Set
<TaxonDescription
> descriptions
= new HashSet
<TaxonDescription
>();
40 // all related synonyms
41 private Set
<SynonymRelationship
> synonymRelations
= new HashSet
<SynonymRelationship
>();
42 // all taxa relations with rel.fromTaxon==this
43 private Set
<TaxonRelationship
> relationsFromThisTaxon
= new HashSet
<TaxonRelationship
>();
44 // all taxa relations with rel.toTaxon==this
45 private Set
<TaxonRelationship
> relationsToThisTaxon
= new HashSet
<TaxonRelationship
>();
46 // shortcut to the taxonomicIncluded (parent) taxon. Managed by the taxonRelations setter
47 private Taxon taxonomicParentCache
;
49 private static Method methodDescriptionSetTaxon
;
55 * @param taxonNameBase The TaxonNameBase that belongs to the new taxon
56 * @param sec The taxon concept reference that
59 public static Taxon
NewInstance(TaxonNameBase taxonNameBase
, ReferenceBase sec
){
60 Taxon result
= new Taxon(taxonNameBase
, sec
);
64 //TODO should be private, but still produces Spring init errors
69 public Taxon(TaxonNameBase taxonNameBase
, ReferenceBase sec
){
70 super(taxonNameBase
, sec
);
74 @OneToMany(mappedBy
="taxon", fetch
= FetchType
.EAGER
)
75 @Cascade({CascadeType
.SAVE_UPDATE
})
76 public Set
<TaxonDescription
> getDescriptions() {
79 protected void setDescriptions(Set
<TaxonDescription
> descriptions
) {
80 this.descriptions
= descriptions
;
83 * Adds a description to this taxon. Set the taxon property of description to this taxon.
86 public void addDescription(TaxonDescription description
) {
88 this.invokeSetMethod(methodDescriptionSetTaxon
, description
);
89 descriptions
.add(description
);
91 public void removeDescription(TaxonDescription description
) {
93 this.invokeSetMethod(methodDescriptionSetTaxon
, null);
94 descriptions
.remove(description
);
97 private void initMethods(){
98 if (methodDescriptionSetTaxon
== null){
100 methodDescriptionSetTaxon
= TaxonDescription
.class.getDeclaredMethod("setTaxon", Taxon
.class);
101 methodDescriptionSetTaxon
.setAccessible(true);
102 } catch (Exception e
) {
104 //TODO handle exception
110 //TODO FetchType (set to Eager because lazyLoading problem in TaxEditor, try to solve problem - 14.4.08)
111 @OneToMany(mappedBy
="relatedTo", fetch
=FetchType
.EAGER
)
112 @Cascade({CascadeType
.SAVE_UPDATE
})
113 public Set
<SynonymRelationship
> getSynonymRelations() {
114 return synonymRelations
;
116 protected void setSynonymRelations(Set
<SynonymRelationship
> synonymRelations
) {
117 this.synonymRelations
= synonymRelations
;
119 protected void addSynonymRelation(SynonymRelationship synonymRelation
) {
120 this.synonymRelations
.add(synonymRelation
);
122 protected void removeSynonymRelation(SynonymRelationship synonymRelation
) {
123 synonymRelation
.setAcceptedTaxon(null);
124 Synonym synonym
= synonymRelation
.getSynonym();
125 if (synonym
!= null){
126 synonymRelation
.setSynonym(null);
127 synonym
.removeSynonymRelation(synonymRelation
);
129 this.synonymRelations
.remove(synonymRelation
);
133 @OneToMany(mappedBy
="relatedFrom", fetch
=FetchType
.EAGER
)
134 @Cascade({CascadeType
.SAVE_UPDATE
})
135 public Set
<TaxonRelationship
> getRelationsFromThisTaxon() {
136 return relationsFromThisTaxon
;
138 protected void setRelationsFromThisTaxon(
139 Set
<TaxonRelationship
> relationsFromThisTaxon
) {
140 this.relationsFromThisTaxon
= relationsFromThisTaxon
;
144 //TODO FetchType (set to Eager because lazyLoading problem in TaxEditor, try to solve problem - 14.4.08)
145 @OneToMany(mappedBy
="relatedTo", fetch
=FetchType
.EAGER
)
146 @Cascade({CascadeType
.SAVE_UPDATE
})
147 public Set
<TaxonRelationship
> getRelationsToThisTaxon() {
148 return relationsToThisTaxon
;
150 protected void setRelationsToThisTaxon(Set
<TaxonRelationship
> relationsToThisTaxon
) {
151 this.relationsToThisTaxon
= relationsToThisTaxon
;
155 // used by hibernate only...
156 private Taxon
getTaxonomicParentCache() {
157 return taxonomicParentCache
;
159 private void setTaxonomicParentCache(Taxon taxonomicParent
) {
160 this.taxonomicParentCache
= taxonomicParent
;
164 public Set
<TaxonRelationship
> getTaxonRelations() {
165 Set
<TaxonRelationship
> rels
= new HashSet
<TaxonRelationship
>();
166 rels
.addAll(getRelationsToThisTaxon());
167 rels
.addAll(getRelationsFromThisTaxon());
170 public void removeTaxonRelation(TaxonRelationship rel
) {
171 this.relationsToThisTaxon
.remove(rel
);
172 this.relationsFromThisTaxon
.remove(rel
);
173 // check if this removes the taxonomical parent. If so, also remove shortcut to the higher taxon
174 if (rel
.getType().equals(TaxonRelationshipType
.TAXONOMICALLY_INCLUDED_IN()) && rel
.getFromTaxon().equals(this)){
175 this.setTaxonomicParentCache(null);
177 // TODO: remove in related taxon too?
179 public void addTaxonRelation(TaxonRelationship rel
) {
180 if (rel
!=null && rel
.getType()!=null && !getTaxonRelations().contains(rel
)){
181 if (rel
.getFromTaxon().equals(this)){
182 relationsFromThisTaxon
.add(rel
);
183 // also add relation to other taxon object
184 Taxon toTaxon
=rel
.getToTaxon();
186 toTaxon
.addTaxonRelation(rel
);
188 // check if this sets the taxonomical parent. If so, remember a shortcut to this taxon
189 if (rel
.getType().equals(TaxonRelationshipType
.TAXONOMICALLY_INCLUDED_IN()) && toTaxon
!=null ){
190 this.setTaxonomicParentCache(rel
.getToTaxon());
192 }else if (rel
.getToTaxon().equals(this)){
193 relationsToThisTaxon
.add(rel
);
198 * @see eu.etaxonomy.cdm.model.common.IRelated#addRelationship(eu.etaxonomy.cdm.model.common.RelationshipBase)
200 public void addRelationship(RelationshipBase rel
){
201 if (rel
instanceof TaxonRelationship
){
202 addTaxonRelation((TaxonRelationship
)rel
);
203 }else if (rel
instanceof SynonymRelationship
){
204 addSynonymRelation((SynonymRelationship
)rel
);
206 throw new ClassCastException("Wrong Relationsship type for Taxon.addRelationship");
211 public void addTaxonRelation(Taxon toTaxon
, TaxonRelationshipType type
, ReferenceBase citation
, String microcitation
) {
212 TaxonRelationship rel
= new TaxonRelationship(this, toTaxon
, type
, citation
, microcitation
);
214 public void addMisappliedName(Taxon toTaxon
, ReferenceBase citation
, String microcitation
) {
215 addTaxonRelation(toTaxon
, TaxonRelationshipType
.MISAPPLIEDNAMEFOR(), citation
, microcitation
);
220 public void addTaxonomicChild(Taxon child
, ReferenceBase citation
, String microcitation
){
222 throw new NullPointerException("Child Taxon is 'null'");
224 child
.setTaxonomicParent(this, citation
, microcitation
);
232 public Taxon
getTaxonomicParent() {
233 return getTaxonomicParentCache();
238 * @param microcitation
240 public void setTaxonomicParent(Taxon parent
, ReferenceBase citation
, String microcitation
){
241 // TODO: remove previously existing parent relationship!!!
243 addTaxonRelation(parent
, TaxonRelationshipType
.TAXONOMICALLY_INCLUDED_IN(),citation
,microcitation
);
251 public Set
<Taxon
> getTaxonomicChildren() {
252 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
253 Set
<TaxonRelationship
> rels
= this.getRelationsToThisTaxon();
254 for (TaxonRelationship rel
: rels
){
255 TaxonRelationshipType tt
= rel
.getType();
256 TaxonRelationshipType incl
= TaxonRelationshipType
.TAXONOMICALLY_INCLUDED_IN();
257 if (tt
.equals(incl
)){
258 taxa
.add(rel
.getFromTaxon());
267 public boolean hasTaxonomicChildren(){
268 for (TaxonRelationship rel
: this.getRelationsToThisTaxon()){
269 if (rel
.getType().equals(TaxonRelationshipType
.TAXONOMICALLY_INCLUDED_IN())){
280 public boolean hasSynonyms(){
281 return this.getSynonymRelations().size() > 0;
289 public Set
<Taxon
> getMisappliedNames(){
290 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
291 Set
<TaxonRelationship
> rels
= this.getRelationsToThisTaxon();
292 for (TaxonRelationship rel
: rels
){
293 TaxonRelationshipType tt
= rel
.getType();
294 TaxonRelationshipType incl
= TaxonRelationshipType
.MISAPPLIEDNAMEFOR();
295 if (tt
.equals(incl
)){
296 taxa
.add(rel
.getFromTaxon());
304 * DEALING WITH SYNONYMS
307 public Set
<Synonym
> getSynonyms(){
308 Set
<Synonym
> syns
= new HashSet
<Synonym
>();
309 for (SynonymRelationship rel
: this.getSynonymRelations()){
310 syns
.add(rel
.getSynonym());
315 public Set
<Synonym
> getSynonymsSortedByType(){
316 // FIXME: need to sort synonyms according to type!!!
317 logger
.warn("getSynonymsSortedByType() not yet implemented");
318 return getSynonyms();
321 public Set
<TaxonNameBase
> getSynonymNames(){
322 Set
<TaxonNameBase
> names
= new HashSet
<TaxonNameBase
>();
323 for (SynonymRelationship rel
: this.getSynonymRelations()){
324 names
.add(rel
.getSynonym().getName());
329 * Adds a synonym as a Synonym to this Taxon using the defined synonym relationship type.<BR>
330 * If you want to add further information to this relationship use the returned SynonymRelationship.
331 * @param synonym the Synoynm to add as a synonym
332 * @param synonymType the SynonymRelationshipType between <i>this</i> taxon and the synonym (e.g. homotypic, heterotypic, proparte ...)
333 * @return The newly created synonym relationship
335 public SynonymRelationship
addSynonym(Synonym synonym
, SynonymRelationshipType synonymType
){
336 return addSynonym(synonym
, synonymType
, null, null);
338 public SynonymRelationship
addSynonym(Synonym synonym
, SynonymRelationshipType synonymType
, ReferenceBase citation
, String citationMicroReference
){
339 SynonymRelationship synonymRelationship
= new SynonymRelationship(synonym
, this, synonymType
, citation
, citationMicroReference
);
340 return synonymRelationship
;
344 * Adds a taxon name to <i>this</i> taxon as a heterotypic synonym.<BR>
345 * The new synonym gets the same concept reference as <i>this</i> taxon.
346 * @param synonymName the TaxonNameBase to add as a synonym name of the defined type.
347 * @param synonymType the SynonymRelationshipType between <i>this</i> taxon and the synonym (e.g. homotypic, heterotypic, proparte ...)
348 * @return The newly created synonym relationship
350 public SynonymRelationship
addSynonymName(TaxonNameBase synonymName
, SynonymRelationshipType synonymType
){
351 return addSynonymName(synonymName
, synonymType
, null, null);
353 public SynonymRelationship
addSynonymName(TaxonNameBase synonymName
, SynonymRelationshipType synonymType
, ReferenceBase citation
, String citationMicroReference
){
354 Synonym synonym
= Synonym
.NewInstance(synonymName
, this.getSec());
355 return addSynonym(synonym
, synonymType
, citation
, citationMicroReference
);
360 * Adds a taxon name to <i>this</i> taxon as a heterotypic synonym. <BR>
361 * The new synonym gets the same concept reference as <i>this</i> taxon.<BR>
362 * @param synonymName the TaxonNameBase to add as a heterotypic synonym name
363 * @return The newly created synonym relationship
365 public SynonymRelationship
addHeterotypicSynonymName(TaxonNameBase synonymName
){
366 return addHeterotypicSynonymName(synonymName
, null, null, null);
371 * Adds a taxon name to <i>this</i> taxon as a heterotypic synonym. <BR>
372 * The new synonym gets the same concept reference as <i>this</i> taxon.<BR>
373 * The name gets the homotypic group given as parameter <i>homotypicalGroup</i><BR>
374 * @param synonymName the TaxonNameBase to add as a heterotypic synonym name
375 * @param homotypicSynonym an existing heterotypic (to <i>this</i> taxon) synonym that has the same type (is homotypic) as the new synonym
376 * @return The newly created synonym relationship
378 public SynonymRelationship
addHeterotypicSynonymName(TaxonNameBase synonymName
, HomotypicalGroup homotypicalGroup
, ReferenceBase citation
, String microCitation
){
379 Synonym synonym
= Synonym
.NewInstance(synonymName
, this.getSec());
380 if (homotypicalGroup
!= null){
381 homotypicalGroup
.addTypifiedName(synonymName
);
383 return addSynonym(synonym
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), citation
, microCitation
);
387 * Adds a taxon name to <i>this</i> taxon as a homotypic synonym. <BR>
388 * The added name gets the same homotypic group as <i>this</i> taxon.<BR>
389 * The new synonym gets the same concept reference as <i>this</i> taxon.<BR>
390 * @param synonymName the TaxonNameBase to add as a homotypic synonym name
391 * @return The newly created synonym relationship
393 public SynonymRelationship
addHomotypicSynonymName(TaxonNameBase synonymName
, ReferenceBase citation
, String microCitation
){
394 Synonym synonym
= Synonym
.NewInstance(synonymName
, this.getSec());
395 return addHomotypicSynonym(synonym
, citation
, microCitation
);
399 * Adds a taxon to <i>this</i> taxon as a homotypic synonym. <BR>
400 * The added synonym gets the same homotypic group as <i>this</i> taxon.<BR>
401 * @param synonymName the TaxonNameBase to add as a homotypic synonym name
402 * @return The newly created synonym relationship
404 public SynonymRelationship
addHomotypicSynonym(Synonym synonym
, ReferenceBase citation
, String microCitation
){
405 if (this.getName() != null){
406 this.getName().getHomotypicalGroup().addTypifiedName(synonym
.getName());
408 SynonymRelationship synRel
= addSynonym(synonym
, SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF(), citation
, microCitation
);
413 * Deletes all synonym relationships between <this>taxon and the given synonym
416 public void removeSynonym(Synonym synonym
){
417 Set
<SynonymRelationship
> synonymRelationships
= new HashSet
<SynonymRelationship
>();
418 synonymRelationships
.addAll(this.getSynonymRelations());
419 for(SynonymRelationship synonymRelationship
: synonymRelationships
){
420 if (synonymRelationship
.getAcceptedTaxon().equals(this) && synonymRelationship
.getSynonym().equals(synonym
)){
421 this.removeSynonymRelation(synonymRelationship
);
429 * @see java.lang.Iterable#iterator()
431 public Iterator
<Taxon
> iterator() {
432 return new TaxonIterator(this.getTaxonomicChildren());
435 * inner iterator class for the iterable interface
439 private class TaxonIterator
implements Iterator
<Taxon
> {
440 private Taxon
[] items
;
442 public TaxonIterator(Set
<Taxon
> items
) {
443 // check for null being passed in etc.
444 this.items
= items
.toArray(new Taxon
[0]);
446 // interface implementation
447 public boolean hasNext() { return i
< items
.length
; }
448 public Taxon
next() { return items
[i
++]; }
449 public void remove() { throw new UnsupportedOperationException(); }
453 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(){
454 if (this.getHomotypicGroup() == null){
457 return this.getHomotypicGroup().getSynonymsInGroup(this.getSec());
462 public List
<Synonym
> getHomotypicSynonymsByHomotypicRelationship(){
463 Set
<SynonymRelationship
> synonymRelations
= this.getSynonymRelations();
464 List
<Synonym
> result
= new ArrayList
<Synonym
>();
465 for(SynonymRelationship synonymRelation
: synonymRelations
) {
466 if(synonymRelation
.getType().equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
467 result
.add(synonymRelation
.getSynonym());
474 * Returns the List of all homotypic groups synonyms of this taxon belongs too.
475 * This includes the homotypic group of <i>this</i> taxon.
479 public List
<HomotypicalGroup
> getHomotypicSynonymyGroups(){
480 List
<HomotypicalGroup
> result
= new ArrayList
<HomotypicalGroup
>();
481 result
.add(this.getHomotypicGroup());
482 for (TaxonNameBase taxonNameBase
:this.getSynonymNames()){
483 if (!result
.contains(taxonNameBase
.getHomotypicalGroup())){
484 result
.add(taxonNameBase
.getHomotypicalGroup());
487 // TODO: sort list according to date of first published name within each group
492 * Returns the List of all homotypic groups heterotypic synonyms of this taxon belongs too.
493 * This does not include the homotypic group of <i>this</i> taxon.
497 public List
<HomotypicalGroup
> getHeterotypicSynonymyGroups(){
498 List
<HomotypicalGroup
> result
= getHomotypicSynonymyGroups();
499 result
.remove(this.getHomotypicGroup());