(no commit message)
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / taxon / Taxon.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
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.
8 */
9
10 package eu.etaxonomy.cdm.model.taxon;
11
12
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;
19
20 import org.apache.log4j.Logger;
21 import org.hibernate.annotations.Cascade;
22 import org.hibernate.annotations.CascadeType;
23
24 import java.lang.reflect.Method;
25 import java.util.*;
26
27 import javax.persistence.*;
28
29 /**
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
32 * @author m.doering
33 * @version 1.0
34 * @created 08-Nov-2007 13:06:56
35 */
36 @Entity
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;
48
49 private static Method methodDescriptionSetTaxon;
50
51
52
53 /**
54 * Factory method
55 * @param taxonNameBase The TaxonNameBase that belongs to the new taxon
56 * @param sec The taxon concept reference that
57 * @return
58 */
59 public static Taxon NewInstance(TaxonNameBase taxonNameBase, ReferenceBase sec){
60 Taxon result = new Taxon(taxonNameBase, sec);
61 return result;
62 }
63
64 //TODO should be private, but still produces Spring init errors
65 @Deprecated
66 public Taxon(){
67 }
68
69 public Taxon(TaxonNameBase taxonNameBase, ReferenceBase sec){
70 super(taxonNameBase, sec);
71 }
72
73
74 @OneToMany(mappedBy="taxon", fetch= FetchType.EAGER)
75 @Cascade({CascadeType.SAVE_UPDATE})
76 public Set<TaxonDescription> getDescriptions() {
77 return descriptions;
78 }
79 protected void setDescriptions(Set<TaxonDescription> descriptions) {
80 this.descriptions = descriptions;
81 }
82 /**
83 * Adds a description to this taxon. Set the taxon property of description to this taxon.
84 * @param description
85 */
86 public void addDescription(TaxonDescription description) {
87 initMethods();
88 this.invokeSetMethod(methodDescriptionSetTaxon, description);
89 descriptions.add(description);
90 }
91 public void removeDescription(TaxonDescription description) {
92 initMethods();
93 this.invokeSetMethod(methodDescriptionSetTaxon, null);
94 descriptions.remove(description);
95 }
96
97 private void initMethods(){
98 if (methodDescriptionSetTaxon == null){
99 try {
100 methodDescriptionSetTaxon = TaxonDescription.class.getDeclaredMethod("setTaxon", Taxon.class);
101 methodDescriptionSetTaxon.setAccessible(true);
102 } catch (Exception e) {
103 e.printStackTrace();
104 //TODO handle exception
105 }
106 }
107 }
108
109
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;
115 }
116 protected void setSynonymRelations(Set<SynonymRelationship> synonymRelations) {
117 this.synonymRelations = synonymRelations;
118 }
119 protected void addSynonymRelation(SynonymRelationship synonymRelation) {
120 this.synonymRelations.add(synonymRelation);
121 }
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);
128 }
129 this.synonymRelations.remove(synonymRelation);
130 }
131
132
133 @OneToMany(mappedBy="relatedFrom", fetch=FetchType.EAGER)
134 @Cascade({CascadeType.SAVE_UPDATE})
135 public Set<TaxonRelationship> getRelationsFromThisTaxon() {
136 return relationsFromThisTaxon;
137 }
138 protected void setRelationsFromThisTaxon(
139 Set<TaxonRelationship> relationsFromThisTaxon) {
140 this.relationsFromThisTaxon = relationsFromThisTaxon;
141 }
142
143
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;
149 }
150 protected void setRelationsToThisTaxon(Set<TaxonRelationship> relationsToThisTaxon) {
151 this.relationsToThisTaxon = relationsToThisTaxon;
152 }
153
154 @ManyToOne
155 // used by hibernate only...
156 private Taxon getTaxonomicParentCache() {
157 return taxonomicParentCache;
158 }
159 private void setTaxonomicParentCache(Taxon taxonomicParent) {
160 this.taxonomicParentCache = taxonomicParent;
161 }
162
163 @Transient
164 public Set<TaxonRelationship> getTaxonRelations() {
165 Set<TaxonRelationship> rels = new HashSet<TaxonRelationship>();
166 rels.addAll(getRelationsToThisTaxon());
167 rels.addAll(getRelationsFromThisTaxon());
168 return rels;
169 }
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);
176 }
177 // TODO: remove in related taxon too?
178 }
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();
185 if (toTaxon!=null){
186 toTaxon.addTaxonRelation(rel);
187 }
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());
191 }
192 }else if (rel.getToTaxon().equals(this)){
193 relationsToThisTaxon.add(rel);
194 }
195 }
196 }
197 /* (non-Javadoc)
198 * @see eu.etaxonomy.cdm.model.common.IRelated#addRelationship(eu.etaxonomy.cdm.model.common.RelationshipBase)
199 */
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);
205 }else{
206 throw new ClassCastException("Wrong Relationsship type for Taxon.addRelationship");
207 }
208 }
209
210
211 public void addTaxonRelation(Taxon toTaxon, TaxonRelationshipType type, ReferenceBase citation, String microcitation) {
212 TaxonRelationship rel = new TaxonRelationship(this, toTaxon, type, citation, microcitation);
213 }
214 public void addMisappliedName(Taxon toTaxon, ReferenceBase citation, String microcitation) {
215 addTaxonRelation(toTaxon, TaxonRelationshipType.MISAPPLIEDNAMEFOR(), citation, microcitation);
216 }
217
218
219 @Transient
220 public void addTaxonomicChild(Taxon child, ReferenceBase citation, String microcitation){
221 if (child == null){
222 throw new NullPointerException("Child Taxon is 'null'");
223 }else{
224 child.setTaxonomicParent(this, citation, microcitation);
225 }
226 }
227
228 /**
229 * @return
230 */
231 @Transient
232 public Taxon getTaxonomicParent() {
233 return getTaxonomicParentCache();
234 }
235 /**
236 * @param parent
237 * @param citation
238 * @param microcitation
239 */
240 public void setTaxonomicParent(Taxon parent, ReferenceBase citation, String microcitation){
241 // TODO: remove previously existing parent relationship!!!
242 if (parent != null){
243 addTaxonRelation(parent, TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN(),citation,microcitation);
244 }
245 }
246
247 /**
248 * @return
249 */
250 @Transient
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());
259 }
260 }
261 return taxa;
262 }
263 /**
264 * @return
265 */
266 @Transient
267 public boolean hasTaxonomicChildren(){
268 for (TaxonRelationship rel: this.getRelationsToThisTaxon()){
269 if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())){
270 return true;
271 }
272 }
273 return false;
274 }
275
276 /**
277 * @return
278 */
279 @Transient
280 public boolean hasSynonyms(){
281 return this.getSynonymRelations().size() > 0;
282 }
283
284
285 /*
286 * MISAPPLIED NAMES
287 */
288 @Transient
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());
297 }
298 }
299 return taxa;
300 }
301
302
303 /*
304 * DEALING WITH SYNONYMS
305 */
306 @Transient
307 public Set<Synonym> getSynonyms(){
308 Set<Synonym> syns = new HashSet<Synonym>();
309 for (SynonymRelationship rel: this.getSynonymRelations()){
310 syns.add(rel.getSynonym());
311 }
312 return syns;
313 }
314 @Transient
315 public Set<Synonym> getSynonymsSortedByType(){
316 // FIXME: need to sort synonyms according to type!!!
317 logger.warn("getSynonymsSortedByType() not yet implemented");
318 return getSynonyms();
319 }
320 @Transient
321 public Set<TaxonNameBase> getSynonymNames(){
322 Set<TaxonNameBase> names = new HashSet<TaxonNameBase>();
323 for (SynonymRelationship rel: this.getSynonymRelations()){
324 names.add(rel.getSynonym().getName());
325 }
326 return names;
327 }
328 /**
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
334 */
335 public SynonymRelationship addSynonym(Synonym synonym, SynonymRelationshipType synonymType){
336 return addSynonym(synonym, synonymType, null, null);
337 }
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;
341 }
342
343 /**
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
349 */
350 public SynonymRelationship addSynonymName(TaxonNameBase synonymName, SynonymRelationshipType synonymType){
351 return addSynonymName(synonymName, synonymType, null, null);
352 }
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);
356 }
357
358
359 /**
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
364 */
365 public SynonymRelationship addHeterotypicSynonymName(TaxonNameBase synonymName){
366 return addHeterotypicSynonymName(synonymName, null, null, null);
367 }
368
369
370 /**
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
377 */
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);
382 }
383 return addSynonym(synonym, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), citation, microCitation);
384 }
385
386 /**
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
392 */
393 public SynonymRelationship addHomotypicSynonymName(TaxonNameBase synonymName, ReferenceBase citation, String microCitation){
394 Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
395 return addHomotypicSynonym(synonym, citation, microCitation);
396 }
397
398 /**
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
403 */
404 public SynonymRelationship addHomotypicSynonym(Synonym synonym, ReferenceBase citation, String microCitation){
405 if (this.getName() != null){
406 this.getName().getHomotypicalGroup().addTypifiedName(synonym.getName());
407 }
408 SynonymRelationship synRel = addSynonym(synonym, SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF(), citation, microCitation);
409 return synRel;
410 }
411
412 /**
413 * Deletes all synonym relationships between <this>taxon and the given synonym
414 * @param synonym
415 */
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);
422 }
423 }
424 }
425
426
427
428 /* (non-Javadoc)
429 * @see java.lang.Iterable#iterator()
430 */
431 public Iterator<Taxon> iterator() {
432 return new TaxonIterator(this.getTaxonomicChildren());
433 }
434 /**
435 * inner iterator class for the iterable interface
436 * @author m.doering
437 *
438 */
439 private class TaxonIterator implements Iterator<Taxon> {
440 private Taxon[] items;
441 private int i= 0;
442 public TaxonIterator(Set<Taxon> items) {
443 // check for null being passed in etc.
444 this.items= items.toArray(new Taxon[0]);
445 }
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(); }
450 }
451
452 @Transient
453 public List<Synonym> getHomotypicSynonymsByHomotypicGroup(){
454 if (this.getHomotypicGroup() == null){
455 return null;
456 }else{
457 return this.getHomotypicGroup().getSynonymsInGroup(this.getSec());
458 }
459 }
460
461 @Transient
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());
468 }
469 }
470 return result;
471 }
472
473 /**
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.
476 * @return
477 */
478 @Transient
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());
485 }
486 }
487 // TODO: sort list according to date of first published name within each group
488 return result;
489 }
490
491 /**
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.
494 * @return
495 */
496 @Transient
497 public List<HomotypicalGroup> getHeterotypicSynonymyGroups(){
498 List<HomotypicalGroup> result = getHomotypicSynonymyGroups();
499 result.remove(this.getHomotypicGroup());
500 return result;
501 }
502
503 }