(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 descriptionSetTaxon(description);
88 descriptions.add(description);
89 }
90 public void removeDescription(TaxonDescription description) {
91 descriptionSetTaxon(null);
92 descriptions.remove(description);
93 }
94 private void descriptionSetTaxon(TaxonDescription description){
95 initMethods();
96 try {
97 methodDescriptionSetTaxon.invoke(description, this);
98 } catch (Exception e) {
99 e.printStackTrace();
100 //TODO handle exceptioin;
101 }
102 }
103 private void initMethods(){
104 if (methodDescriptionSetTaxon == null){
105 try {
106 methodDescriptionSetTaxon = TaxonDescription.class.getDeclaredMethod("setTaxon", Taxon.class);
107 methodDescriptionSetTaxon.setAccessible(true);
108 } catch (Exception e) {
109 e.printStackTrace();
110 //TODO handle exception
111 }
112 }
113 }
114
115
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;
121 }
122 protected void setSynonymRelations(Set<SynonymRelationship> synonymRelations) {
123 this.synonymRelations = synonymRelations;
124 }
125 protected void addSynonymRelation(SynonymRelationship synonymRelation) {
126 this.synonymRelations.add(synonymRelation);
127 }
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);
134 }
135 this.synonymRelations.remove(synonymRelation);
136 }
137
138
139 @OneToMany(mappedBy="relatedFrom", fetch=FetchType.EAGER)
140 @Cascade({CascadeType.SAVE_UPDATE})
141 public Set<TaxonRelationship> getRelationsFromThisTaxon() {
142 return relationsFromThisTaxon;
143 }
144 protected void setRelationsFromThisTaxon(
145 Set<TaxonRelationship> relationsFromThisTaxon) {
146 this.relationsFromThisTaxon = relationsFromThisTaxon;
147 }
148
149
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;
155 }
156 protected void setRelationsToThisTaxon(Set<TaxonRelationship> relationsToThisTaxon) {
157 this.relationsToThisTaxon = relationsToThisTaxon;
158 }
159
160 @ManyToOne
161 // used by hibernate only...
162 private Taxon getTaxonomicParentCache() {
163 return taxonomicParentCache;
164 }
165 private void setTaxonomicParentCache(Taxon taxonomicParent) {
166 this.taxonomicParentCache = taxonomicParent;
167 }
168
169 @Transient
170 public Set<TaxonRelationship> getTaxonRelations() {
171 Set<TaxonRelationship> rels = new HashSet<TaxonRelationship>();
172 rels.addAll(getRelationsToThisTaxon());
173 rels.addAll(getRelationsFromThisTaxon());
174 return rels;
175 }
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);
182 }
183 // TODO: remove in related taxon too?
184 }
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();
191 if (toTaxon!=null){
192 toTaxon.addTaxonRelation(rel);
193 }
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());
197 }
198 }else if (rel.getToTaxon().equals(this)){
199 relationsToThisTaxon.add(rel);
200 }
201 }
202 }
203 /* (non-Javadoc)
204 * @see eu.etaxonomy.cdm.model.common.IRelated#addRelationship(eu.etaxonomy.cdm.model.common.RelationshipBase)
205 */
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);
211 }else{
212 throw new ClassCastException("Wrong Relationsship type for Taxon.addRelationship");
213 }
214 }
215
216
217 public void addTaxonRelation(Taxon toTaxon, TaxonRelationshipType type, ReferenceBase citation, String microcitation) {
218 TaxonRelationship rel = new TaxonRelationship(this, toTaxon, type, citation, microcitation);
219 }
220 public void addMisappliedName(Taxon toTaxon, ReferenceBase citation, String microcitation) {
221 addTaxonRelation(toTaxon, TaxonRelationshipType.MISAPPLIEDNAMEFOR(), citation, microcitation);
222 }
223
224
225 @Transient
226 public void addTaxonomicChild(Taxon child, ReferenceBase citation, String microcitation){
227 if (child == null){
228 throw new NullPointerException("Child Taxon is 'null'");
229 }else{
230 child.setTaxonomicParent(this, citation, microcitation);
231 }
232 }
233
234 /**
235 * @return
236 */
237 @Transient
238 public Taxon getTaxonomicParent() {
239 return getTaxonomicParentCache();
240 }
241 /**
242 * @param parent
243 * @param citation
244 * @param microcitation
245 */
246 public void setTaxonomicParent(Taxon parent, ReferenceBase citation, String microcitation){
247 // TODO: remove previously existing parent relationship!!!
248 if (parent != null){
249 addTaxonRelation(parent, TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN(),citation,microcitation);
250 }
251 }
252
253 /**
254 * @return
255 */
256 @Transient
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());
265 }
266 }
267 return taxa;
268 }
269 /**
270 * @return
271 */
272 @Transient
273 public boolean hasTaxonomicChildren(){
274 for (TaxonRelationship rel: this.getRelationsToThisTaxon()){
275 if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())){
276 return true;
277 }
278 }
279 return false;
280 }
281
282 /**
283 * @return
284 */
285 @Transient
286 public boolean hasSynonyms(){
287 return this.getSynonymRelations().size() > 0;
288 }
289
290
291 /*
292 * MISAPPLIED NAMES
293 */
294 @Transient
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());
303 }
304 }
305 return taxa;
306 }
307
308
309 /*
310 * DEALING WITH SYNONYMS
311 */
312 @Transient
313 public Set<Synonym> getSynonyms(){
314 Set<Synonym> syns = new HashSet<Synonym>();
315 for (SynonymRelationship rel: this.getSynonymRelations()){
316 syns.add(rel.getSynonym());
317 }
318 return syns;
319 }
320 @Transient
321 public Set<Synonym> getSynonymsSortedByType(){
322 // FIXME: need to sort synonyms according to type!!!
323 logger.warn("getSynonymsSortedByType() not yet implemented");
324 return getSynonyms();
325 }
326 @Transient
327 public Set<TaxonNameBase> getSynonymNames(){
328 Set<TaxonNameBase> names = new HashSet<TaxonNameBase>();
329 for (SynonymRelationship rel: this.getSynonymRelations()){
330 names.add(rel.getSynonym().getName());
331 }
332 return names;
333 }
334 /**
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
340 */
341 public SynonymRelationship addSynonym(Synonym synonym, SynonymRelationshipType synonymType){
342 return addSynonym(synonym, synonymType, null, null);
343 }
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;
347 }
348
349 /**
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
355 */
356 public SynonymRelationship addSynonymName(TaxonNameBase synonymName, SynonymRelationshipType synonymType){
357 return addSynonymName(synonymName, synonymType, null, null);
358 }
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);
362 }
363
364
365 /**
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
370 */
371 public SynonymRelationship addHeterotypicSynonymName(TaxonNameBase synonymName){
372 return addHeterotypicSynonymName(synonymName, null);
373 }
374
375
376 /**
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
383 */
384 public SynonymRelationship addHeterotypicSynonymName(TaxonNameBase synonymName, HomotypicalGroup homotypicalGroup){
385 Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
386 if (homotypicalGroup != null){
387 homotypicalGroup.addTypifiedName(synonymName);
388 }
389 return addSynonym(synonym, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF());
390 }
391
392 /**
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
398 */
399 public SynonymRelationship addHomotypicSynonymName(TaxonNameBase synonymName){
400 Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
401 if (this.getName() != null){
402 this.getName().getHomotypicalGroup().addTypifiedName(synonymName);
403 }
404 SynonymRelationship synRel = addSynonym(synonym, SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF());
405 return synRel;
406 }
407
408 /**
409 * Deletes all synonym relationships between <this>taxon and the given synonym
410 * @param synonym
411 */
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);
418 }
419 }
420 }
421
422
423
424 /* (non-Javadoc)
425 * @see java.lang.Iterable#iterator()
426 */
427 public Iterator<Taxon> iterator() {
428 return new TaxonIterator(this.getTaxonomicChildren());
429 }
430 /**
431 * inner iterator class for the iterable interface
432 * @author m.doering
433 *
434 */
435 private class TaxonIterator implements Iterator<Taxon> {
436 private Taxon[] items;
437 private int i= 0;
438 public TaxonIterator(Set<Taxon> items) {
439 // check for null being passed in etc.
440 this.items= items.toArray(new Taxon[0]);
441 }
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(); }
446 }
447
448 @Transient
449 public List<Synonym> getHomotypicSynonyms(){
450 if (this.getHomotypicGroup() == null){
451 return null;
452 }else{
453 return this.getHomotypicGroup().getSynonymsInGroup(this.getSec());
454 }
455 }
456
457 /**
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.
460 * @return
461 */
462 @Transient
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());
469 }
470 }
471 // TODO: sort list according to date of first published name within each group
472 return result;
473 }
474
475 /**
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.
478 * @return
479 */
480 @Transient
481 public List<HomotypicalGroup> getHeterotypicSynonymyGroups(){
482 List<HomotypicalGroup> result = getHomotypicSynonymyGroups();
483 result.remove(this.getHomotypicGroup());
484 return result;
485 }
486
487 }