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
) {
87 descriptionSetTaxon(description
);
88 descriptions
.add(description
);
90 public void removeDescription(TaxonDescription description
) {
91 descriptionSetTaxon(null);
92 descriptions
.remove(description
);
94 private void descriptionSetTaxon(TaxonDescription description
){
97 methodDescriptionSetTaxon
.invoke(description
, this);
98 } catch (Exception e
) {
100 //TODO handle exceptioin;
103 private void initMethods(){
104 if (methodDescriptionSetTaxon
== null){
106 methodDescriptionSetTaxon
= TaxonDescription
.class.getDeclaredMethod("setTaxon", Taxon
.class);
107 methodDescriptionSetTaxon
.setAccessible(true);
108 } catch (Exception e
) {
110 //TODO handle exception
116 //TODO FetchType (set to Eager because lazyLoading problem in TaxEditor, try to solve problem - 14.4.08)
117 @OneToMany(mappedBy
="relatedTo", fetch
=FetchType
.EAGER
)
118 @Cascade({CascadeType
.SAVE_UPDATE
})
119 public Set
<SynonymRelationship
> getSynonymRelations() {
120 return synonymRelations
;
122 protected void setSynonymRelations(Set
<SynonymRelationship
> synonymRelations
) {
123 this.synonymRelations
= synonymRelations
;
125 protected void addSynonymRelation(SynonymRelationship synonymRelation
) {
126 this.synonymRelations
.add(synonymRelation
);
128 protected void removeSynonymRelation(SynonymRelationship synonymRelation
) {
129 synonymRelation
.setAcceptedTaxon(null);
130 Synonym synonym
= synonymRelation
.getSynonym();
131 if (synonym
!= null){
132 synonymRelation
.setSynonym(null);
133 synonym
.removeSynonymRelation(synonymRelation
);
135 this.synonymRelations
.remove(synonymRelation
);
139 @OneToMany(mappedBy
="relatedFrom", fetch
=FetchType
.EAGER
)
140 @Cascade({CascadeType
.SAVE_UPDATE
})
141 public Set
<TaxonRelationship
> getRelationsFromThisTaxon() {
142 return relationsFromThisTaxon
;
144 protected void setRelationsFromThisTaxon(
145 Set
<TaxonRelationship
> relationsFromThisTaxon
) {
146 this.relationsFromThisTaxon
= relationsFromThisTaxon
;
150 //TODO FetchType (set to Eager because lazyLoading problem in TaxEditor, try to solve problem - 14.4.08)
151 @OneToMany(mappedBy
="relatedTo", fetch
=FetchType
.EAGER
)
152 @Cascade({CascadeType
.SAVE_UPDATE
})
153 public Set
<TaxonRelationship
> getRelationsToThisTaxon() {
154 return relationsToThisTaxon
;
156 protected void setRelationsToThisTaxon(Set
<TaxonRelationship
> relationsToThisTaxon
) {
157 this.relationsToThisTaxon
= relationsToThisTaxon
;
161 // used by hibernate only...
162 private Taxon
getTaxonomicParentCache() {
163 return taxonomicParentCache
;
165 private void setTaxonomicParentCache(Taxon taxonomicParent
) {
166 this.taxonomicParentCache
= taxonomicParent
;
170 public Set
<TaxonRelationship
> getTaxonRelations() {
171 Set
<TaxonRelationship
> rels
= new HashSet
<TaxonRelationship
>();
172 rels
.addAll(getRelationsToThisTaxon());
173 rels
.addAll(getRelationsFromThisTaxon());
176 public void removeTaxonRelation(TaxonRelationship rel
) {
177 this.relationsToThisTaxon
.remove(rel
);
178 this.relationsFromThisTaxon
.remove(rel
);
179 // check if this removes the taxonomical parent. If so, also remove shortcut to the higher taxon
180 if (rel
.getType().equals(TaxonRelationshipType
.TAXONOMICALLY_INCLUDED_IN()) && rel
.getFromTaxon().equals(this)){
181 this.setTaxonomicParentCache(null);
183 // TODO: remove in related taxon too?
185 public void addTaxonRelation(TaxonRelationship rel
) {
186 if (rel
!=null && rel
.getType()!=null && !getTaxonRelations().contains(rel
)){
187 if (rel
.getFromTaxon().equals(this)){
188 relationsFromThisTaxon
.add(rel
);
189 // also add relation to other taxon object
190 Taxon toTaxon
=rel
.getToTaxon();
192 toTaxon
.addTaxonRelation(rel
);
194 // check if this sets the taxonomical parent. If so, remember a shortcut to this taxon
195 if (rel
.getType().equals(TaxonRelationshipType
.TAXONOMICALLY_INCLUDED_IN()) && toTaxon
!=null ){
196 this.setTaxonomicParentCache(rel
.getToTaxon());
198 }else if (rel
.getToTaxon().equals(this)){
199 relationsToThisTaxon
.add(rel
);
204 * @see eu.etaxonomy.cdm.model.common.IRelated#addRelationship(eu.etaxonomy.cdm.model.common.RelationshipBase)
206 public void addRelationship(RelationshipBase rel
){
207 if (rel
instanceof TaxonRelationship
){
208 addTaxonRelation((TaxonRelationship
)rel
);
209 }else if (rel
instanceof SynonymRelationship
){
210 addSynonymRelation((SynonymRelationship
)rel
);
212 throw new ClassCastException("Wrong Relationsship type for Taxon.addRelationship");
217 public void addTaxonRelation(Taxon toTaxon
, TaxonRelationshipType type
, ReferenceBase citation
, String microcitation
) {
218 TaxonRelationship rel
= new TaxonRelationship(this, toTaxon
, type
, citation
, microcitation
);
220 public void addMisappliedName(Taxon toTaxon
, ReferenceBase citation
, String microcitation
) {
221 addTaxonRelation(toTaxon
, TaxonRelationshipType
.MISAPPLIEDNAMEFOR(), citation
, microcitation
);
226 public void addTaxonomicChild(Taxon child
, ReferenceBase citation
, String microcitation
){
228 throw new NullPointerException("Child Taxon is 'null'");
230 child
.setTaxonomicParent(this, citation
, microcitation
);
238 public Taxon
getTaxonomicParent() {
239 return getTaxonomicParentCache();
244 * @param microcitation
246 public void setTaxonomicParent(Taxon parent
, ReferenceBase citation
, String microcitation
){
247 // TODO: remove previously existing parent relationship!!!
249 addTaxonRelation(parent
, TaxonRelationshipType
.TAXONOMICALLY_INCLUDED_IN(),citation
,microcitation
);
257 public Set
<Taxon
> getTaxonomicChildren() {
258 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
259 Set
<TaxonRelationship
> rels
= this.getRelationsToThisTaxon();
260 for (TaxonRelationship rel
: rels
){
261 TaxonRelationshipType tt
= rel
.getType();
262 TaxonRelationshipType incl
= TaxonRelationshipType
.TAXONOMICALLY_INCLUDED_IN();
263 if (tt
.equals(incl
)){
264 taxa
.add(rel
.getFromTaxon());
273 public boolean hasTaxonomicChildren(){
274 for (TaxonRelationship rel
: this.getRelationsToThisTaxon()){
275 if (rel
.getType().equals(TaxonRelationshipType
.TAXONOMICALLY_INCLUDED_IN())){
286 public boolean hasSynonyms(){
287 return this.getSynonymRelations().size() > 0;
295 public Set
<Taxon
> getMisappliedNames(){
296 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
297 Set
<TaxonRelationship
> rels
= this.getRelationsToThisTaxon();
298 for (TaxonRelationship rel
: rels
){
299 TaxonRelationshipType tt
= rel
.getType();
300 TaxonRelationshipType incl
= TaxonRelationshipType
.MISAPPLIEDNAMEFOR();
301 if (tt
.equals(incl
)){
302 taxa
.add(rel
.getFromTaxon());
310 * DEALING WITH SYNONYMS
313 public Set
<Synonym
> getSynonyms(){
314 Set
<Synonym
> syns
= new HashSet
<Synonym
>();
315 for (SynonymRelationship rel
: this.getSynonymRelations()){
316 syns
.add(rel
.getSynonym());
321 public Set
<Synonym
> getSynonymsSortedByType(){
322 // FIXME: need to sort synonyms according to type!!!
323 logger
.warn("getSynonymsSortedByType() not yet implemented");
324 return getSynonyms();
327 public Set
<TaxonNameBase
> getSynonymNames(){
328 Set
<TaxonNameBase
> names
= new HashSet
<TaxonNameBase
>();
329 for (SynonymRelationship rel
: this.getSynonymRelations()){
330 names
.add(rel
.getSynonym().getName());
335 * Adds a synonym as a Synonym to this Taxon using the defined synonym relationship type.<BR>
336 * If you want to add further information to this relationship use the returned SynonymRelationship.
337 * @param synonym the Synoynm to add as a synonym
338 * @param synonymType the SynonymRelationshipType between <i>this</i> taxon and the synonym (e.g. homotypic, heterotypic, proparte ...)
339 * @return The newly created synonym relationship
341 public SynonymRelationship
addSynonym(Synonym synonym
, SynonymRelationshipType synonymType
){
342 return addSynonym(synonym
, synonymType
, null, null);
344 public SynonymRelationship
addSynonym(Synonym synonym
, SynonymRelationshipType synonymType
, ReferenceBase citation
, String citationMicroReference
){
345 SynonymRelationship synonymRelationship
= new SynonymRelationship(synonym
, this, synonymType
, citation
, citationMicroReference
);
346 return synonymRelationship
;
350 * Adds a taxon name to <i>this</i> taxon as a heterotypic synonym.<BR>
351 * The new synonym gets the same concept reference as <i>this</i> taxon.
352 * @param synonymName the TaxonNameBase to add as a synonym name of the defined type.
353 * @param synonymType the SynonymRelationshipType between <i>this</i> taxon and the synonym (e.g. homotypic, heterotypic, proparte ...)
354 * @return The newly created synonym relationship
356 public SynonymRelationship
addSynonymName(TaxonNameBase synonymName
, SynonymRelationshipType synonymType
){
357 return addSynonymName(synonymName
, synonymType
, null, null);
359 public SynonymRelationship
addSynonymName(TaxonNameBase synonymName
, SynonymRelationshipType synonymType
, ReferenceBase citation
, String citationMicroReference
){
360 Synonym synonym
= Synonym
.NewInstance(synonymName
, this.getSec());
361 return addSynonym(synonym
, synonymType
, citation
, citationMicroReference
);
366 * Adds a taxon name to <i>this</i> taxon as a heterotypic synonym. <BR>
367 * The new synonym gets the same concept reference as <i>this</i> taxon.<BR>
368 * @param synonymName the TaxonNameBase to add as a heterotypic synonym name
369 * @return The newly created synonym relationship
371 public SynonymRelationship
addHeterotypicSynonymName(TaxonNameBase synonymName
){
372 return addHeterotypicSynonymName(synonymName
, null);
377 * Adds a taxon name to <i>this</i> taxon as a heterotypic synonym. <BR>
378 * The new synonym gets the same concept reference as <i>this</i> taxon.<BR>
379 * The name gets the homotypic group given as parameter <i>homotypicalGroup</i><BR>
380 * @param synonymName the TaxonNameBase to add as a heterotypic synonym name
381 * @param homotypicSynonym an existing heterotypic (to <i>this</i> taxon) synonym that has the same type (is homotypic) as the new synonym
382 * @return The newly created synonym relationship
384 public SynonymRelationship
addHeterotypicSynonymName(TaxonNameBase synonymName
, HomotypicalGroup homotypicalGroup
){
385 Synonym synonym
= Synonym
.NewInstance(synonymName
, this.getSec());
386 if (homotypicalGroup
!= null){
387 synonymName
.setHomotypicalGroup(homotypicalGroup
);
389 return addSynonym(synonym
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF());
393 * Adds a taxon name to <i>this</i> taxon as a homotypic synonym. <BR>
394 * The added name gets the same homotypic group as <i>this</i> taxon.<BR>
395 * The new synonym gets the same concept reference as <i>this</i> taxon.<BR>
396 * @param synonymName the TaxonNameBase to add as a homotypic synonym name
397 * @return The newly created synonym relationship
399 public SynonymRelationship
addHomotypicSynonymName(TaxonNameBase synonymName
){
400 Synonym synonym
= Synonym
.NewInstance(synonymName
, this.getSec());
401 if (this.getName() != null){
402 synonymName
.setHomotypicalGroup(this.getName().getHomotypicalGroup());
404 SynonymRelationship synRel
= addSynonym(synonym
, SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
409 * Deletes all synonym relationships between <this>taxon and the given synonym
412 public void removeSynonym(Synonym synonym
){
413 Set
<SynonymRelationship
> synonymRelationships
= new HashSet
<SynonymRelationship
>();
414 synonymRelationships
.addAll(this.getSynonymRelations());
415 for(SynonymRelationship synonymRelationship
: synonymRelationships
){
416 if (synonymRelationship
.getAcceptedTaxon().equals(this) && synonymRelationship
.getSynonym().equals(synonym
)){
417 this.removeSynonymRelation(synonymRelationship
);
425 * @see java.lang.Iterable#iterator()
427 public Iterator
<Taxon
> iterator() {
428 return new TaxonIterator(this.getTaxonomicChildren());
431 * inner iterator class for the iterable interface
435 private class TaxonIterator
implements Iterator
<Taxon
> {
436 private Taxon
[] items
;
438 public TaxonIterator(Set
<Taxon
> items
) {
439 // check for null being passed in etc.
440 this.items
= items
.toArray(new Taxon
[0]);
442 // interface implementation
443 public boolean hasNext() { return i
< items
.length
; }
444 public Taxon
next() { return items
[i
++]; }
445 public void remove() { throw new UnsupportedOperationException(); }
449 public List
<Synonym
> getHomotypicSynonyms(){
450 if (this.getHomotypicGroup() == null){
453 return this.getHomotypicGroup().getSynonymsInGroup(this.getSec());
458 * Returns the List of all homotypic groups synonyms of this taxon belongs too.
459 * This includes the homotypic group of <i>this</i> taxon.
463 public List
<HomotypicalGroup
> getHomotypicSynonymyGroups(){
464 List
<HomotypicalGroup
> result
= new ArrayList
<HomotypicalGroup
>();
465 result
.add(this.getHomotypicGroup());
466 for (TaxonNameBase taxonNameBase
:this.getSynonymNames()){
467 if (!result
.contains(taxonNameBase
.getHomotypicalGroup())){
468 result
.add(taxonNameBase
.getHomotypicalGroup());
471 // TODO: sort list according to date of first published name within each group
476 * Returns the List of all homotypic groups heterotypic synonyms of this taxon belongs too.
477 * This does not include the homotypic group of <i>this</i> taxon.
481 public List
<HomotypicalGroup
> getHeterotypicSynonymyGroups(){
482 List
<HomotypicalGroup
> result
= getHomotypicSynonymyGroups();
483 result
.remove(this.getHomotypicGroup());