(no commit message)
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / name / TaxonNameBase.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.name;
11
12 import eu.etaxonomy.cdm.model.occurrence.Specimen;
13 import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
14 import eu.etaxonomy.cdm.model.reference.ReferenceBase;
15 import eu.etaxonomy.cdm.model.reference.StrictReferenceBase;
16 import eu.etaxonomy.cdm.model.taxon.Synonym;
17 import eu.etaxonomy.cdm.model.taxon.Taxon;
18 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
19 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
20 import eu.etaxonomy.cdm.model.common.IReferencedEntity;
21 import org.apache.log4j.Logger;
22 import org.hibernate.annotations.Cascade;
23 import org.hibernate.annotations.CascadeType;
24 import org.hibernate.collection.PersistentSet;
25
26 import eu.etaxonomy.cdm.strategy.cache.INameCacheStrategy;
27
28
29 import java.util.*;
30
31 import javax.persistence.*;
32
33 /**
34 * The upmost (abstract) class for scientific taxon names regardless of any
35 * particular nomenclatural code. The scientific name including author strings and
36 * maybe year is stored in IdentifiableEntity.titleCache
37 *
38 * @author m.doering
39 * @version 1.0
40 * @created 08-Nov-2007 13:06:57
41 */
42 @Entity
43 @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
44 public abstract class TaxonNameBase<T extends TaxonNameBase> extends IdentifiableEntity<TaxonNameBase> implements IReferencedEntity {
45 static Logger logger = Logger.getLogger(TaxonNameBase.class);
46 //The scientific name without author strings and year
47 private String nameCache;
48 //Non-atomised addition to a name not ruled by a nomenclatural code
49 private String appendedPhrase;
50 //Details of the nomenclatural reference (protologue). These are mostly (implicitly) pages but can also be figures or
51 //tables or any other element of a publication. {only if a nomenclatural reference exists}
52 private String nomenclaturalMicroReference;
53 //this flag will be set to true if the parseName method was unable to successfully parse the name
54 private boolean hasProblem = false;
55 protected Set<NameTypeDesignation> nameTypeDesignations = new HashSet<NameTypeDesignation>();
56 private HomotypicalGroup homotypicalGroup = new HomotypicalGroup();
57 private Set<NameRelationship> relationsFromThisName = new HashSet<NameRelationship>();
58 private Set<NameRelationship> relationsToThisName = new HashSet<NameRelationship>();
59 private Set<NomenclaturalStatus> status = new HashSet<NomenclaturalStatus>();
60 private Set<TaxonBase> taxonBases = new HashSet<TaxonBase>();
61
62 private Rank rank;
63 //if set, the Reference.isNomenclaturallyRelevant flag should be set to true!
64 private INomenclaturalReference nomenclaturalReference;
65
66 //this flag shows if the getNameCache should return generated value(false) or the given String(true)
67 protected boolean protectedNameCache;
68
69 protected INameCacheStrategy<T> cacheStrategy;
70
71 // /**
72 // * Returns a TaxonNameBase instance
73 // * @param fullName
74 // */
75 // abstract public static TaxonNameBase PARSED_NAME(String fullName);
76
77 // ************* CONSTRUCTORS *************/
78 public TaxonNameBase() {
79 this(null, null);
80 }
81 public TaxonNameBase(Rank rank) {
82 this(rank, null);
83 }
84 public TaxonNameBase(HomotypicalGroup homotypicalGroup) {
85 this(null, homotypicalGroup);
86 }
87 public TaxonNameBase(Rank rank, HomotypicalGroup homotypicalGroup) {
88 super();
89 this.setRank(rank);
90 if (homotypicalGroup == null){
91 homotypicalGroup = new HomotypicalGroup();
92 }
93 homotypicalGroup.addTypifiedName(this);
94 }
95
96 //********* METHODS **************************************/
97
98
99
100 protected String generateNameCache(){
101 if (cacheStrategy == null){
102 logger.warn("No CacheStrategy defined for taxonName: " + this.toString());
103 return null;
104 }else{
105 return cacheStrategy.getNameCache((T)this);
106 }
107 }
108
109 public String getNameCache() {
110 if (protectedNameCache){
111 return this.nameCache;
112 }
113 // is title dirty, i.e. equal NULL?
114 if (nameCache == null){
115 this.nameCache = generateNameCache();
116 }
117 return nameCache;
118 }
119
120 public void setNameCache(String nameCache){
121 this.nameCache = nameCache;
122 this.setProtectedTitleCache(false);
123 this.setProtectedNameCache(true);
124 }
125
126 public boolean isProtectedNameCache() {
127 return protectedNameCache;
128 }
129
130 public void setProtectedNameCache(boolean protectedNameCache) {
131 this.protectedNameCache = protectedNameCache;
132 }
133
134
135
136 @Transient
137 public abstract boolean isCodeCompliant();
138
139
140 @Transient
141 public Set<NameRelationship> getNameRelations() {
142 Set<NameRelationship> rels = new HashSet<NameRelationship>();
143 rels.addAll(getRelationsFromThisName());
144 rels.addAll(getRelationsToThisName());
145 return rels;
146 }
147 /**
148 * Add a name relationship to both names involved
149 * @param rel
150 */
151 public void addRelationshipToName(TaxonNameBase toName, NameRelationshipType type, String ruleConsidered){
152 NameRelationship rel = new NameRelationship(toName, this, type, ruleConsidered);
153 }
154 public void addRelationshipFromName(TaxonNameBase fromName, NameRelationshipType type, String ruleConsidered){
155 NameRelationship rel = new NameRelationship(this, fromName, type, ruleConsidered);
156 }
157 protected void addNameRelationship(NameRelationship rel) {
158 if (rel!=null && rel.getToName().equals(this)){
159 this.relationsToThisName.add(rel);
160 }else if(rel!=null && rel.getFromName().equals(this)){
161 this.relationsFromThisName.add(rel);
162 }else{
163 //TODO: raise error???
164 }
165 }
166 public void removeNameRelationship(NameRelationship nameRelation) {
167 this.relationsToThisName.remove(nameRelation);
168 this.relationsFromThisName.remove(nameRelation);
169 }
170
171
172 @OneToMany(mappedBy="fromName", fetch= FetchType.EAGER)
173 @Cascade({CascadeType.SAVE_UPDATE})
174 public Set<NameRelationship> getRelationsFromThisName() {
175 return relationsFromThisName;
176 }
177 private void setRelationsFromThisName(Set<NameRelationship> relationsFromThisName) {
178 this.relationsFromThisName = relationsFromThisName;
179 }
180
181 @OneToMany(mappedBy="toName", fetch= FetchType.EAGER)
182 @Cascade({CascadeType.SAVE_UPDATE})
183 public Set<NameRelationship> getRelationsToThisName() {
184 return relationsToThisName;
185 }
186 private void setRelationsToThisName(Set<NameRelationship> relationsToThisName) {
187 this.relationsToThisName = relationsToThisName;
188 }
189
190
191
192 @OneToMany(fetch= FetchType.EAGER)
193 @Cascade({CascadeType.SAVE_UPDATE})
194 public Set<NomenclaturalStatus> getStatus() {
195 return status;
196 }
197 protected void setStatus(Set<NomenclaturalStatus> nomStatus) {
198 this.status = nomStatus;
199 }
200 public void addStatus(NomenclaturalStatus nomStatus) {
201 this.status.add(nomStatus);
202 }
203 public void removeStatus(NomenclaturalStatus nomStatus) {
204 this.status.remove(nomStatus);
205 }
206
207
208 /**
209 * Indicates if this taxon name has a basionym or replaced synonym relationship to any other name.
210 * @return true, if a {@link NameRelationshipType.BASIONYM()} or a {@link NameRelationshipType.REPLACED_SYNONYM()}
211 * relationship from this name to another name exists.
212 */
213 @Transient
214 public boolean isOriginalCombination(){
215 Set<NameRelationship> relationsFromThisName = this.getRelationsFromThisName();
216 for (NameRelationship relation : relationsFromThisName) {
217 if (relation.getType().equals(NameRelationshipType.BASIONYM()) ||
218 relation.getType().equals(NameRelationshipType.REPLACED_SYNONYM())) {
219 return true;
220 }
221 }
222 return false;
223 }
224
225
226 @Transient
227 public T getBasionym(){
228 //TODO: pick the right name relationships...
229 return null;
230 }
231 public void setBasionym(T basionym){
232 setBasionym(basionym, null);
233 }
234 public void setBasionym(T basionym, String ruleConsidered){
235 basionym.addRelationshipToName(this, NameRelationshipType.BASIONYM(), null);
236 }
237
238
239
240 //TODO for PROTOTYPE
241 @Transient
242 public INameCacheStrategy<T> getCacheStrategy() {
243 return cacheStrategy;
244 }
245 public void setCacheStrategy(INameCacheStrategy cacheStrategy) {
246 this.cacheStrategy = cacheStrategy;
247 }
248
249 @ManyToOne
250 //@Cascade({CascadeType.SAVE_UPDATE})
251 public Rank getRank(){
252 return this.rank;
253 }
254 public void setRank(Rank rank){
255 this.rank = rank;
256 }
257
258 @ManyToOne
259 @Cascade({CascadeType.SAVE_UPDATE})
260 public ReferenceBase getNomenclaturalReference(){
261 return (ReferenceBase) this.nomenclaturalReference;
262 }
263 public void setNomenclaturalReference(INomenclaturalReference nomenclaturalReference){
264 this.nomenclaturalReference = nomenclaturalReference;
265 }
266
267
268 public String getAppendedPhrase(){
269 return this.appendedPhrase;
270 }
271 public void setAppendedPhrase(String appendedPhrase){
272 this.appendedPhrase = appendedPhrase;
273 }
274
275 public String getNomenclaturalMicroReference(){
276 return this.nomenclaturalMicroReference;
277 }
278 public void setNomenclaturalMicroReference(String nomenclaturalMicroReference){
279 this.nomenclaturalMicroReference = nomenclaturalMicroReference;
280 }
281
282 public boolean getHasProblem(){
283 return this.hasProblem;
284 }
285 public void setHasProblem(boolean hasProblem){
286 this.hasProblem = hasProblem;
287 }
288
289
290 @OneToMany
291 //TODO @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE_ORPHAN})
292 @Cascade(CascadeType.SAVE_UPDATE)
293 public Set<NameTypeDesignation> getNameTypeDesignations() {
294 return nameTypeDesignations;
295 }
296 protected void setNameTypeDesignations(Set<NameTypeDesignation> nameTypeDesignations) {
297 this.nameTypeDesignations = nameTypeDesignations;
298 }
299
300 public void addTypeDesignation(TaxonNameBase typeSpecies, ReferenceBase citation, String citationMicroReference, String originalNameString, boolean isRejectedType, boolean isConservedType) {
301 NameTypeDesignation td = new NameTypeDesignation(this, typeSpecies, citation, citationMicroReference, originalNameString, isRejectedType, isConservedType);
302 }
303 public void addTypeDesignation(Specimen typeSpecimen, TypeDesignationStatus status, ReferenceBase citation, String citationMicroReference, String originalNameString) {
304 this.homotypicalGroup.addTypeDesignation(typeSpecimen, status, citation, citationMicroReference, originalNameString);
305 }
306 public void removeTypeDesignation(NameTypeDesignation typeDesignation) {
307 this.nameTypeDesignations.remove(typeDesignation);
308 }
309 public void removeTypeDesignation(SpecimenTypeDesignation typeDesignation) {
310 this.homotypicalGroup.removeTypeDesignation(typeDesignation);
311 }
312
313
314 @ManyToOne
315 @Cascade({CascadeType.SAVE_UPDATE})
316 public HomotypicalGroup getHomotypicalGroup() {
317 return homotypicalGroup;
318 }
319 public void setHomotypicalGroup(HomotypicalGroup newHomotypicalGroup) {
320 if(this.homotypicalGroup == newHomotypicalGroup) return;
321 if (homotypicalGroup != null) {
322 homotypicalGroup.typifiedNames.remove(this);
323 }
324 if (newHomotypicalGroup!= null) {
325 //hack for avoiding org.hibernate.LazyInitializationException: illegal access to loading collection
326 if (newHomotypicalGroup.typifiedNames instanceof PersistentSet){
327 //
328 }else{
329 newHomotypicalGroup.typifiedNames.add(this);
330 }
331 }
332 this.homotypicalGroup = newHomotypicalGroup;
333 }
334
335 @Transient
336 public StrictReferenceBase getCitation(){
337 logger.warn("getCitation not yet implemented");
338 return null;
339 }
340
341 @Transient
342 public String getCitationString(){
343 logger.warn("getCitationString not yet implemented");
344 return null;
345 }
346
347 @Transient
348 public String[] getProblems(){
349 logger.warn("getProblems not yet implemented");
350 return null;
351 }
352
353 /**
354 * returns year of according nomenclatural reference, null if nomenclatural
355 * reference does not exist
356 */
357 @Transient
358 public String getReferenceYear(){
359 if (this.getNomenclaturalReference() != null ){
360 return this.getNomenclaturalReference().getYear();
361 }else{
362 return null;
363 }
364 }
365
366 @OneToMany(mappedBy="name", fetch= FetchType.EAGER)
367 public Set<TaxonBase> getTaxonBases() {
368 return this.taxonBases;
369 }
370 protected void setTaxonBases(Set<TaxonBase> taxonBases) {
371 if (taxonBases == null){
372 taxonBases = new HashSet<TaxonBase>();
373 }else{
374 this.taxonBases = taxonBases;
375 }
376 }
377 // public void addSynonym(Synonym synonym) {
378 // synonym.setName(this);
379 // }
380 // public void removeSynonym(Synonym synonym) {
381 // synonym.setName(null);
382 // }
383
384 /**
385 * Return a set of taxa that use this name
386 * @return
387 */
388 @Transient
389 public Set<Taxon> getTaxa(){
390 Set<Taxon> result = new HashSet<Taxon>();
391 for (TaxonBase taxonBase : this.taxonBases){
392 if (taxonBase instanceof Taxon){
393 result.add((Taxon)taxonBase);
394 }
395 }
396 return result;
397 }
398
399 /**
400 * Return a set of synonyms that use this name
401 * @return
402 */
403 // TODO: implement this method via bidirectional TaxonBase-NameBase relation or use a DAO instead
404 //@OneToMany
405
406 @Transient
407 public Set<Synonym> getSynonyms() {
408 Set<Synonym> result = new HashSet<Synonym>();
409 for (TaxonBase taxonBase : this.taxonBases){
410 if (taxonBase instanceof Synonym){
411 result.add((Synonym)taxonBase);
412 }
413 }
414 return result;
415 }
416
417 @Transient
418 public Set<SpecimenTypeDesignation> getSpecimenTypeDesignations() {
419 return this.getHomotypicalGroup().getTypeDesignations();
420 }
421
422 // Rank comparison shortcuts
423 @Transient
424 public boolean isSupraGeneric() {
425 return getRank().isSupraGeneric();
426 }
427 @Transient
428 public boolean isGenus() {
429 return getRank().isGenus();
430 }
431 @Transient
432 public boolean isInfraGeneric() {
433 return getRank().isInfraGeneric();
434 }
435 @Transient
436 public boolean isSpecies() {
437 return getRank().isSpecies();
438 }
439 @Transient
440 public boolean isInfraSpecific() {
441 return getRank().isInfraSpecific();
442 }
443
444 @Transient
445 public String getNomeclaturalCodeAbbrev(){
446 return null;
447 }
448
449
450 }