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.description.DescriptionBase;
|
14
|
import eu.etaxonomy.cdm.model.description.TaxonDescription;
|
15
|
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
|
16
|
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
|
17
|
import eu.etaxonomy.cdm.model.reference.ReferenceBase;
|
18
|
|
19
|
import org.apache.log4j.Logger;
|
20
|
import org.hibernate.annotations.Cascade;
|
21
|
import org.hibernate.annotations.CascadeType;
|
22
|
|
23
|
import java.util.*;
|
24
|
|
25
|
import javax.persistence.*;
|
26
|
|
27
|
/**
|
28
|
* An accepted potential taxon defined by the combination of a Name and a sec reference
|
29
|
* {@link Iterable} interface is supported to iterate through taxonomic children
|
30
|
* @author m.doering
|
31
|
* @version 1.0
|
32
|
* @created 08-Nov-2007 13:06:56
|
33
|
*/
|
34
|
@Entity
|
35
|
public class Taxon extends TaxonBase implements Iterable<Taxon>{
|
36
|
static Logger logger = Logger.getLogger(Taxon.class);
|
37
|
private Set<TaxonDescription> descriptions = new HashSet();
|
38
|
// all related synonyms
|
39
|
private Set<SynonymRelationship> synonymRelations = new HashSet();
|
40
|
// all taxa relations with rel.fromTaxon==this
|
41
|
private Set<TaxonRelationship> relationsFromThisTaxon = new HashSet();
|
42
|
// all taxa relations with rel.toTaxon==this
|
43
|
private Set<TaxonRelationship> relationsToThisTaxon = new HashSet();
|
44
|
// shortcut to the taxonomicIncluded (parent) taxon. Managed by the taxonRelations setter
|
45
|
private Taxon taxonomicParentCache;
|
46
|
|
47
|
|
48
|
public static Taxon NewInstance(TaxonNameBase taxonName, ReferenceBase sec){
|
49
|
Taxon result = new Taxon();
|
50
|
result.setName(taxonName);
|
51
|
result.setSec(sec);
|
52
|
return result;
|
53
|
}
|
54
|
|
55
|
//TODO should be private, but still produces Spring init errors
|
56
|
public Taxon(){
|
57
|
}
|
58
|
|
59
|
|
60
|
@OneToMany(mappedBy="taxon")
|
61
|
@Cascade({CascadeType.SAVE_UPDATE})
|
62
|
public Set<TaxonDescription> getDescriptions() {
|
63
|
return descriptions;
|
64
|
}
|
65
|
protected void setDescriptions(Set<TaxonDescription> descriptions) {
|
66
|
this.descriptions = descriptions;
|
67
|
}
|
68
|
public void addDescriptions(TaxonDescription description) {
|
69
|
this.descriptions.add(description);
|
70
|
}
|
71
|
public void removeDescriptions(DescriptionBase description) {
|
72
|
this.descriptions.remove(description);
|
73
|
}
|
74
|
|
75
|
|
76
|
@OneToMany(mappedBy="acceptedTaxon")
|
77
|
@Cascade({CascadeType.SAVE_UPDATE})
|
78
|
public Set<SynonymRelationship> getSynonymRelations() {
|
79
|
return synonymRelations;
|
80
|
}
|
81
|
protected void setSynonymRelations(Set<SynonymRelationship> synonymRelations) {
|
82
|
this.synonymRelations = synonymRelations;
|
83
|
}
|
84
|
public void addSynonymRelation(SynonymRelationship synonymRelation) {
|
85
|
this.synonymRelations.add(synonymRelation);
|
86
|
}
|
87
|
public void removeSynonymRelation(SynonymRelationship synonymRelation) {
|
88
|
this.synonymRelations.remove(synonymRelation);
|
89
|
}
|
90
|
|
91
|
|
92
|
@OneToMany(mappedBy="fromTaxon")
|
93
|
@Cascade({CascadeType.SAVE_UPDATE})
|
94
|
public Set<TaxonRelationship> getRelationsFromThisTaxon() {
|
95
|
return relationsFromThisTaxon;
|
96
|
}
|
97
|
protected void setRelationsFromThisTaxon(
|
98
|
Set<TaxonRelationship> relationsFromThisTaxon) {
|
99
|
this.relationsFromThisTaxon = relationsFromThisTaxon;
|
100
|
}
|
101
|
|
102
|
|
103
|
@OneToMany(mappedBy="toTaxon")
|
104
|
@Cascade({CascadeType.SAVE_UPDATE})
|
105
|
public Set<TaxonRelationship> getRelationsToThisTaxon() {
|
106
|
return relationsToThisTaxon;
|
107
|
}
|
108
|
protected void setRelationsToThisTaxon(Set<TaxonRelationship> relationsToThisTaxon) {
|
109
|
this.relationsToThisTaxon = relationsToThisTaxon;
|
110
|
}
|
111
|
|
112
|
@ManyToOne
|
113
|
// used by hibernate only...
|
114
|
private Taxon getTaxonomicParentCache() {
|
115
|
return taxonomicParentCache;
|
116
|
}
|
117
|
private void setTaxonomicParentCache(Taxon taxonomicParent) {
|
118
|
this.taxonomicParentCache = taxonomicParent;
|
119
|
}
|
120
|
|
121
|
@Transient
|
122
|
public Set<TaxonRelationship> getTaxonRelations() {
|
123
|
Set<TaxonRelationship> rels = new HashSet();
|
124
|
rels.addAll(getRelationsToThisTaxon());
|
125
|
rels.addAll(getRelationsFromThisTaxon());
|
126
|
return rels;
|
127
|
}
|
128
|
public void removeTaxonRelation(TaxonRelationship rel) {
|
129
|
this.relationsToThisTaxon.remove(rel);
|
130
|
this.relationsFromThisTaxon.remove(rel);
|
131
|
// check if this removes the taxonomical parent. If so, also remove shortcut to the higher taxon
|
132
|
if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && rel.getFromTaxon().equals(this)){
|
133
|
this.setTaxonomicParentCache(null);
|
134
|
}
|
135
|
// TODO: remove in related taxon too?
|
136
|
}
|
137
|
public void addTaxonRelation(TaxonRelationship rel) {
|
138
|
if (rel!=null && rel.getType()!=null && !getTaxonRelations().contains(rel)){
|
139
|
if (rel.getFromTaxon().equals(this)){
|
140
|
relationsFromThisTaxon.add(rel);
|
141
|
// also add relation to other taxon object
|
142
|
Taxon toTaxon=rel.getToTaxon();
|
143
|
if (toTaxon!=null){
|
144
|
toTaxon.addTaxonRelation(rel);
|
145
|
}
|
146
|
// check if this sets the taxonomical parent. If so, remember a shortcut to this taxon
|
147
|
if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN()) && toTaxon!=null ){
|
148
|
this.setTaxonomicParentCache(rel.getToTaxon());
|
149
|
}
|
150
|
}else if (rel.getToTaxon().equals(this)){
|
151
|
relationsToThisTaxon.add(rel);
|
152
|
}
|
153
|
}
|
154
|
}
|
155
|
public void addTaxonRelation(Taxon toTaxon, TaxonRelationshipType type, ReferenceBase citation, String microcitation) {
|
156
|
TaxonRelationship rel = new TaxonRelationship();
|
157
|
rel.setToTaxon(toTaxon);
|
158
|
rel.setFromTaxon(this);
|
159
|
rel.setType(type);
|
160
|
rel.setCitation(citation);
|
161
|
rel.setCitationMicroReference(microcitation);
|
162
|
this.addTaxonRelation(rel);
|
163
|
}
|
164
|
public void addMisappliedName(Taxon toTaxon, ReferenceBase citation, String microcitation) {
|
165
|
addTaxonRelation(toTaxon, TaxonRelationshipType.MISAPPLIEDNAMEFOR(), citation, microcitation);
|
166
|
}
|
167
|
|
168
|
|
169
|
|
170
|
|
171
|
@Override
|
172
|
public String generateTitle(){
|
173
|
return this.toString();
|
174
|
}
|
175
|
|
176
|
@Transient
|
177
|
public void addTaxonomicChild(Taxon child, ReferenceBase citation, String microcitation){
|
178
|
if (child == null){
|
179
|
throw new NullPointerException("Child Taxon is 'null'");
|
180
|
}else{
|
181
|
child.setTaxonomicParent(this, citation, microcitation);
|
182
|
}
|
183
|
}
|
184
|
|
185
|
/**
|
186
|
* @return
|
187
|
*/
|
188
|
@Transient
|
189
|
public Taxon getTaxonomicParent() {
|
190
|
return getTaxonomicParentCache();
|
191
|
}
|
192
|
/**
|
193
|
* @param parent
|
194
|
* @param citation
|
195
|
* @param microcitation
|
196
|
*/
|
197
|
public void setTaxonomicParent(Taxon parent, ReferenceBase citation, String microcitation){
|
198
|
// TODO: remove previously existing parent relationship!!!
|
199
|
if (parent != null){
|
200
|
addTaxonRelation(parent, TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN(),citation,microcitation);
|
201
|
}
|
202
|
}
|
203
|
|
204
|
/**
|
205
|
* @return
|
206
|
*/
|
207
|
@Transient
|
208
|
public Set<Taxon> getTaxonomicChildren() {
|
209
|
Set<Taxon> taxa = new HashSet<Taxon>();
|
210
|
for (TaxonRelationship rel: this.getRelationsToThisTaxon()){
|
211
|
TaxonRelationshipType tt = rel.getType();
|
212
|
TaxonRelationshipType incl = TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN();
|
213
|
if (tt.equals(incl)){
|
214
|
taxa.add(rel.getFromTaxon());
|
215
|
}
|
216
|
}
|
217
|
return taxa;
|
218
|
}
|
219
|
/**
|
220
|
* @return
|
221
|
*/
|
222
|
@Transient
|
223
|
public boolean hasTaxonomicChildren(){
|
224
|
for (TaxonRelationship rel: this.getRelationsToThisTaxon()){
|
225
|
if (rel.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())){
|
226
|
return true;
|
227
|
}
|
228
|
}
|
229
|
return false;
|
230
|
}
|
231
|
|
232
|
|
233
|
|
234
|
/*
|
235
|
* DEALING WITH SYNONYMS
|
236
|
*/
|
237
|
@Transient
|
238
|
public Set<Synonym> getSynonyms(){
|
239
|
Set<Synonym> syns = new HashSet();
|
240
|
for (SynonymRelationship rel: this.getSynonymRelations()){
|
241
|
syns.add(rel.getSynonym());
|
242
|
}
|
243
|
return syns;
|
244
|
}
|
245
|
@Transient
|
246
|
public Set<Synonym> getSynonymsSortedByType(){
|
247
|
// FIXME: need to sort synonyms according to type!!!
|
248
|
return getSynonyms();
|
249
|
}
|
250
|
@Transient
|
251
|
public Set<TaxonNameBase> getSynonymNames(){
|
252
|
Set<TaxonNameBase> names = new HashSet();
|
253
|
for (SynonymRelationship rel: this.getSynonymRelations()){
|
254
|
names.add(rel.getSynonym().getName());
|
255
|
}
|
256
|
return names;
|
257
|
}
|
258
|
/**
|
259
|
* ass a synonym to this taxon (a taxon can have multiple synonyms that should be proparte synonyms then!)
|
260
|
* The {@link SynonymRelationship} constructor immediately adds a relationship instance to both
|
261
|
* the synonym and taxon instance!
|
262
|
* @param synonym
|
263
|
* @param synonymType
|
264
|
*/
|
265
|
public void addSynonym(Synonym synonym, SynonymRelationshipType synonymType){
|
266
|
SynonymRelationship synonymRelationship = new SynonymRelationship(synonym, this, synonymType);
|
267
|
}
|
268
|
|
269
|
/* (non-Javadoc)
|
270
|
* @see java.lang.Iterable#iterator()
|
271
|
*/
|
272
|
public Iterator<Taxon> iterator() {
|
273
|
return new TaxonIterator(this.getTaxonomicChildren());
|
274
|
}
|
275
|
/**
|
276
|
* inner iterator class for the iterable interface
|
277
|
* @author markus
|
278
|
*
|
279
|
*/
|
280
|
private class TaxonIterator implements Iterator<Taxon> {
|
281
|
private Taxon[] items;
|
282
|
private int i= 0;
|
283
|
public TaxonIterator(Set<Taxon> items) {
|
284
|
// check for null being passed in etc.
|
285
|
this.items= items.toArray(new Taxon[0]);
|
286
|
}
|
287
|
// interface implementation
|
288
|
public boolean hasNext() { return i < items.length; }
|
289
|
public Taxon next() { return items[i++]; }
|
290
|
public void remove() { throw new UnsupportedOperationException(); }
|
291
|
}
|
292
|
|
293
|
@Transient
|
294
|
public HomotypicalGroup getHomotypicGroup(){
|
295
|
return this.getName().getHomotypicalGroup();
|
296
|
}
|
297
|
|
298
|
@Transient
|
299
|
public List<Synonym> getHomotypicSynonyms(){
|
300
|
return this.getHomotypicGroup().getSynonymsInGroup(this.getSec());
|
301
|
}
|
302
|
|
303
|
@Transient
|
304
|
public List<HomotypicalGroup> getHeterotypicSynonymyGroups(){
|
305
|
List<HomotypicalGroup> result = new ArrayList();
|
306
|
for (TaxonNameBase n:this.getSynonymNames()){
|
307
|
if (!result.contains(n.getHomotypicalGroup())){
|
308
|
result.add(n.getHomotypicalGroup());
|
309
|
}
|
310
|
}
|
311
|
// TODO: sort list according to date of first published name within each group
|
312
|
return result;
|
313
|
}
|
314
|
}
|