(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 java.lang.reflect.Method;
13 import java.util.HashSet;
14 import java.util.Set;
15
16 import javax.persistence.Entity;
17 import javax.persistence.FetchType;
18 import javax.persistence.Inheritance;
19 import javax.persistence.InheritanceType;
20 import javax.persistence.ManyToMany;
21 import javax.persistence.ManyToOne;
22 import javax.persistence.OneToMany;
23 import javax.persistence.Transient;
24 import javax.xml.bind.annotation.XmlAccessType;
25 import javax.xml.bind.annotation.XmlAccessorType;
26 import javax.xml.bind.annotation.XmlAnyElement;
27 import javax.xml.bind.annotation.XmlAttribute;
28 import javax.xml.bind.annotation.XmlElement;
29 import javax.xml.bind.annotation.XmlElementWrapper;
30 import javax.xml.bind.annotation.XmlIDREF;
31 import javax.xml.bind.annotation.XmlRootElement;
32 import javax.xml.bind.annotation.XmlSchemaType;
33 import javax.xml.bind.annotation.XmlTransient;
34 import javax.xml.bind.annotation.XmlType;
35
36 import org.apache.log4j.Logger;
37 import org.hibernate.annotations.Cascade;
38 import org.hibernate.annotations.CascadeType;
39 import org.hibernate.annotations.Index;
40 import org.hibernate.annotations.Table;
41 import org.hibernate.annotations.Target;
42
43 import eu.etaxonomy.cdm.model.common.IParsable;
44 import eu.etaxonomy.cdm.model.common.IReferencedEntity;
45 import eu.etaxonomy.cdm.model.common.IRelated;
46 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
47 import eu.etaxonomy.cdm.model.common.RelationshipBase;
48 import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
49 import eu.etaxonomy.cdm.model.occurrence.Specimen;
50 import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
51 import eu.etaxonomy.cdm.model.reference.ReferenceBase;
52 import eu.etaxonomy.cdm.model.reference.StrictReferenceBase;
53 import eu.etaxonomy.cdm.model.taxon.Synonym;
54 import eu.etaxonomy.cdm.model.taxon.Taxon;
55 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
56 import eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy;
57
58 /**
59 * The upmost (abstract) class for scientific taxon names regardless of any
60 * particular {@link NomenclaturalCode nomenclature code}. The scientific taxon name does not depend
61 * on the use made of it in a publication or a treatment
62 * ({@link taxon.TaxonBase taxon concept respectively potential taxon})
63 * as an {@link taxon.Taxon "accepted" respectively "correct" (taxon) name}
64 * or as a {@link taxon.Synonym synonym}.
65 * <P>
66 * This class corresponds partially to: <ul>
67 * <li> TaxonName according to the TDWG ontology
68 * <li> ScientificName and CanonicalName according to the TCS
69 * <li> ScientificName according to the ABCD schema
70 * </ul>
71 *
72 * @author m.doering
73 * @version 1.0
74 * @created 08-Nov-2007 13:06:57
75 */
76 @XmlAccessorType(XmlAccessType.FIELD)
77 @XmlType(name = "TaxonNameBase", propOrder = {
78 "appendedPhrase",
79 "nomenclaturalMicroReference",
80 "nomenclaturalReference",
81 "rank",
82 "homotypicalGroup",
83 "nameTypeDesignations",
84 "specimenTypeDesignations",
85 "relationsFromThisName",
86 "relationsToThisName",
87 "status",
88 "descriptions"
89 // "taxonBases"
90 })
91 @XmlRootElement(name = "TaxonNameBase")
92 @Entity
93 @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
94 @Table(appliesTo="TaxonNameBase", indexes = { @Index(name = "taxonNameBaseTitleCacheIndex", columnNames = { "persistentTitleCache" }) })
95 public abstract class TaxonNameBase<T extends TaxonNameBase, S extends INameCacheStrategy> extends IdentifiableEntity<TaxonNameBase> implements IReferencedEntity, IParsable, IRelated {
96
97 static Logger logger = Logger.getLogger(TaxonNameBase.class);
98
99 private static Method methodDescriptionSetTaxonName;
100
101 @XmlElementWrapper(name = "Descriptions")
102 @XmlElement(name = "Description")
103 private Set<TaxonNameDescription> descriptions = new HashSet<TaxonNameDescription>();
104
105 @XmlElement(name = "AppendedPhrase")
106 private String appendedPhrase;
107
108 @XmlElement(name = "NomenclaturalMicroReference")
109 private String nomenclaturalMicroReference;
110
111 @XmlAttribute
112 private boolean hasProblem = false;
113
114 @XmlElementWrapper(name = "TypeDesignations")
115 @XmlElement(name = "TypeDesignation")
116 private Set<TypeDesignationBase> typeDesignations = new HashSet<TypeDesignationBase>();
117
118 // List homotypical groups here in TaxonomicNames or separately?
119 // FIXME: Stack overflow if listed here.
120 @XmlElement(name = "HomotypicalGroup")
121 @XmlIDREF
122 @XmlSchemaType(name = "IDREF")
123 private HomotypicalGroup homotypicalGroup = new HomotypicalGroup();
124
125 @XmlElementWrapper(name = "RelationsFromThisName")
126 @XmlElement(name = "RelationFromThisName")
127 @XmlIDREF
128 private Set<NameRelationship> relationsFromThisName = new HashSet<NameRelationship>();
129
130 @XmlElementWrapper(name = "RelationsToThisName")
131 @XmlElement(name = "RelationToThisName")
132 @XmlIDREF
133 private Set<NameRelationship> relationsToThisName = new HashSet<NameRelationship>();
134
135 @XmlElementWrapper(name = "Statuses")
136 @XmlElement(name = "Status")
137 @XmlIDREF
138 private Set<NomenclaturalStatus> status = new HashSet<NomenclaturalStatus>();
139
140 @XmlTransient
141 //@XmlElementWrapper(name = "TaxonBases")
142 //@XmlElement(name = "TaxonBase")
143 private Set<TaxonBase> taxonBases = new HashSet<TaxonBase>();
144
145 @XmlElement(name = "Rank")
146 @XmlIDREF
147 @XmlSchemaType(name = "IDREF")
148 private Rank rank;
149
150 // FIXME: This must be an IDREF to the corresponding nomenclatural reference.
151 @XmlAnyElement
152 private INomenclaturalReference nomenclaturalReference;
153
154 static Method methodTaxonBaseSetName;
155
156 // ************* CONSTRUCTORS *************/
157 /**
158 * Class constructor: creates a new empty taxon name.
159 *
160 * @see #TaxonNameBase(Rank)
161 * @see #TaxonNameBase(HomotypicalGroup)
162 * @see #TaxonNameBase(Rank, HomotypicalGroup)
163 */
164 public TaxonNameBase() {
165 this(null, null);
166 }
167 /**
168 * Class constructor: creates a new taxon name
169 * only containing its {@link common.Rank rank}.
170 *
171 * @param rank the rank to be assigned to <i>this</i> taxon name
172 * @see #TaxonNameBase()
173 * @see #TaxonNameBase(HomotypicalGroup)
174 * @see #TaxonNameBase(Rank, HomotypicalGroup)
175 */
176 public TaxonNameBase(Rank rank) {
177 this(rank, null);
178 }
179 /**
180 * Class constructor: creates a new taxon name
181 * only containing its {@link HomotypicalGroup homotypical group}.
182 * The new taxon name will be also added to the set of taxon names
183 * belonging to this homotypical group.
184 *
185 * @param homotypicalGroup the homotypical group to which <i>this</i> taxon name belongs
186 * @see #TaxonNameBase()
187 * @see #TaxonNameBase(Rank)
188 * @see #TaxonNameBase(Rank, HomotypicalGroup)
189 */
190 public TaxonNameBase(HomotypicalGroup homotypicalGroup) {
191 this(null, homotypicalGroup);
192 }
193 /**
194 * Class constructor: creates a new taxon name
195 * only containing its {@link common.Rank rank} and
196 * its {@link HomotypicalGroup homotypical group}.
197 * The new taxon name will be also added to the set of taxon names
198 * belonging to this homotypical group.
199 *
200 * @param rank the rank to be assigned to <i>this</i> taxon name
201 * @param homotypicalGroup the homotypical group to which <i>this</i> taxon name belongs
202 * @see #TaxonNameBase()
203 * @see #TaxonNameBase(Rank)
204 * @see #TaxonNameBase(HomotypicalGroup)
205 */
206 public TaxonNameBase(Rank rank, HomotypicalGroup homotypicalGroup) {
207 super();
208 this.setRank(rank);
209 if (homotypicalGroup == null){
210 homotypicalGroup = new HomotypicalGroup();
211 }
212 homotypicalGroup.addTypifiedName(this);
213 }
214
215 //********* METHODS **************************************/
216
217 //@Index(name="TaxonNameBaseTitleCacheIndex")
218 // public String getTitleCache(){
219 // return super.getTitleCache();
220 // }
221
222 /**
223 * Returns the boolean value "false" since the components of <i>this</i> taxon name
224 * cannot follow the rules of a corresponding {@link NomenclaturalCode nomenclatural code}
225 * which is not defined for this class. The nomenclature code depends on
226 * the concrete name subclass ({@link BacterialName BacterialName},
227 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName},
228 * {@link ZoologicalName ZoologicalName} or {@link ViralName ViralName})
229 * to which a taxon name belongs.
230 *
231 * @return false
232 */
233 @Transient
234 public abstract boolean isCodeCompliant();
235
236
237 /**
238 * Returns the set of all {@link NameRelationship name relationships}
239 * in which <i>this</i> taxon name is involved. A taxon name can be both source
240 * in some name relationships or target in some others.
241 *
242 * @see #getRelationsToThisName()
243 * @see #getRelationsFromThisName()
244 * @see #addNameRelationship(NameRelationship)
245 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
246 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
247 */
248 @Transient
249 public Set<NameRelationship> getNameRelations() {
250 Set<NameRelationship> rels = new HashSet<NameRelationship>();
251 rels.addAll(getRelationsFromThisName());
252 rels.addAll(getRelationsToThisName());
253 return rels;
254 }
255 /**
256 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
257 * and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
258 * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
259 *
260 * @param toName the taxon name of the target for this new name relationship
261 * @param type the type of this new name relationship
262 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
263 * @see #getRelationsToThisName()
264 * @see #getNameRelations()
265 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
266 * @see #addNameRelationship(NameRelationship)
267 */
268 public void addRelationshipToName(TaxonNameBase toName, NameRelationshipType type, String ruleConsidered){
269 NameRelationship rel = new NameRelationship(toName, this, type, ruleConsidered);
270 }
271 /**
272 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
273 * and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
274 * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
275 *
276 * @param fromName the taxon name of the source for this new name relationship
277 * @param type the type of this new name relationship
278 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
279 * @see #getRelationsFromThisName()
280 * @see #getNameRelations()
281 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
282 * @see #addNameRelationship(NameRelationship)
283 */
284 public void addRelationshipFromName(TaxonNameBase fromName, NameRelationshipType type, String ruleConsidered){
285 NameRelationship rel = new NameRelationship(this, fromName, type, ruleConsidered);
286 }
287 /**
288 * Adds an existing {@link NameRelationship name relationship} either to the set of
289 * {@link #getRelationsToThisName() relations to <i>this</i> taxon name} or to the set of
290 * {@link #getRelationsFromThisName() relations from <i>this</i> taxon name}. If neither the
291 * source nor the target of the name relationship match with <i>this</i> taxon name
292 * no addition will be carried out.
293 *
294 * @param rel the name relationship to be added to one of <i>this</i> taxon name's name relationships sets
295 * @see #getNameRelations()
296 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
297 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
298 */
299 protected void addNameRelationship(NameRelationship rel) {
300 if (rel!=null && rel.getToName().equals(this)){
301 this.relationsToThisName.add(rel);
302 }else if(rel!=null && rel.getFromName().equals(this)){
303 this.relationsFromThisName.add(rel);
304 }else{
305 //TODO: raise error???
306 }
307 }
308 /**
309 * Removes one {@link NameRelationship name relationship} from one of both sets of
310 * {@link #getNameRelations() name relationships} in which <i>this</i> taxon name is involved.
311 * The name relationship will also be removed from one of both sets belonging
312 * to the second taxon name involved. Furthermore the fromName and toName
313 * attributes of the name relationship object will be nullified.
314 *
315 * @param nameRelation the name relationship which should be deleted from one of both sets
316 * @see #getNameRelations()
317 */
318 public void removeNameRelationship(NameRelationship nameRelation) {
319 //TODO to be implemented?
320 logger.warn("not yet fully implemented?");
321 this.relationsToThisName.remove(nameRelation);
322 this.relationsFromThisName.remove(nameRelation);
323 }
324
325
326 /**
327 * Does exactly the same as the addNameRelationship method provided that
328 * the given relationship is a name relationship.
329 *
330 * @param relation the relationship to be added to one of <i>this</i> taxon name's name relationships sets
331 * @see #addNameRelationship(NameRelationship)
332 * @see #getNameRelations()
333 * @see NameRelationship
334 * @see common.RelationshipBase
335 */
336 public void addRelationship(RelationshipBase relation) {
337 if (relation instanceof NameRelationship){
338 addNameRelationship((NameRelationship)relation);
339 if (relation.getType() != null &&
340 ( relation.getType().equals(NameRelationshipType.BASIONYM()) ||
341 relation.getType().equals(NameRelationshipType.REPLACED_SYNONYM())
342 )){
343 TaxonNameBase fromName = ((NameRelationship)relation).getFromName();
344 TaxonNameBase toName = ((NameRelationship)relation).getToName();
345 fromName.getHomotypicalGroup().merge(toName.getHomotypicalGroup());
346 }
347 }else{
348 logger.warn("Relationship not of type NameRelationship!");
349 //TODO exception handling
350 }
351 }
352
353
354 /**
355 * Returns the set of all {@link NameRelationship name relationships}
356 * in which <i>this</i> taxon name is involved as a source.
357 *
358 * @see #getNameRelations()
359 * @see #getRelationsToThisName()
360 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
361 */
362 @OneToMany(mappedBy="relatedFrom", fetch= FetchType.LAZY)
363 @Cascade({CascadeType.SAVE_UPDATE})
364 public Set<NameRelationship> getRelationsFromThisName() {
365 return relationsFromThisName;
366 }
367 private void setRelationsFromThisName(Set<NameRelationship> relationsFromThisName) {
368 this.relationsFromThisName = relationsFromThisName;
369 }
370
371 /**
372 * Returns the set of all {@link NameRelationship name relationships}
373 * in which <i>this</i> taxon name is involved as a target.
374 *
375 * @see #getNameRelations()
376 * @see #getRelationsFromThisName()
377 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
378 */
379 @OneToMany(mappedBy="relatedTo", fetch= FetchType.LAZY)
380 @Cascade({CascadeType.SAVE_UPDATE})
381 public Set<NameRelationship> getRelationsToThisName() {
382 return relationsToThisName;
383 }
384 private void setRelationsToThisName(Set<NameRelationship> relationsToThisName) {
385 this.relationsToThisName = relationsToThisName;
386 }
387
388
389 /**
390 * Returns the set of {@link NomenclaturalStatus nomenclatural status} assigned
391 * to <i>this</i> taxon name according to its corresponding nomenclature code.
392 * This includes the {@link NomenclaturalStatusType type} of the nomenclatural status
393 * and the nomenclatural code rule considered.
394 *
395 * @see NomenclaturalStatus
396 * @see NomenclaturalStatusType
397 */
398 @OneToMany(fetch= FetchType.LAZY)
399 @Cascade({CascadeType.SAVE_UPDATE})
400 public Set<NomenclaturalStatus> getStatus() {
401 return status;
402 }
403 /**
404 * @see #getStatus()
405 */
406 protected void setStatus(Set<NomenclaturalStatus> nomStatus) {
407 this.status = nomStatus;
408 }
409 /**
410 * Adds a new {@link NomenclaturalStatus nomenclatural status}
411 * to <i>this</i> taxon name's set of nomenclatural status.
412 *
413 * @param nomStatus the nomenclatural status to be added
414 * @see #getStatus()
415 */
416 public void addStatus(NomenclaturalStatus nomStatus) {
417 this.status.add(nomStatus);
418 }
419 /**
420 * Removes one element from the set of nomenclatural status of <i>this</i> taxon name.
421 * Type and ruleConsidered attributes of the nomenclatural status object
422 * will be nullified.
423 *
424 * @param nomStatus the nomenclatural status of <i>this</i> taxon name which should be deleted
425 * @see #getStatus()
426 */
427 public void removeStatus(NomenclaturalStatus nomStatus) {
428 //TODO to be implemented?
429 logger.warn("not yet fully implemented?");
430 this.status.remove(nomStatus);
431 }
432
433
434 /**
435 * Indicates whether <i>this</i> taxon name is a {@link NameRelationshipType.BASIONYM() basionym}
436 * or a {@link NameRelationshipType.REPLACED_SYNONYM() replaced synonym}
437 * of any other taxon name. Returns "true", if a basionym or a replaced
438 * synonym relationship from <i>this</i> taxon name to another taxon name exists,
439 * false otherwise (also in case <i>this</i> taxon name is the only one in the
440 * homotypical group).
441 */
442 @Transient
443 public boolean isOriginalCombination(){
444 Set<NameRelationship> relationsFromThisName = this.getRelationsFromThisName();
445 for (NameRelationship relation : relationsFromThisName) {
446 if (relation.getType().equals(NameRelationshipType.BASIONYM()) ||
447 relation.getType().equals(NameRelationshipType.REPLACED_SYNONYM())) {
448 return true;
449 }
450 }
451 return false;
452 }
453
454 /**
455 * Returns the taxon name which is the {@link NameRelationshipType.BASIONYM() basionym} of <i>this</i> taxon name.
456 * The basionym of a taxon name is its epithet-bringing synonym.
457 * For instance Pinus abies L. was published by Linnaeus and the botanist
458 * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
459 * Pinus abies L. is the basionym of the new combination Picea abies (L.) H. Karst.
460 */
461 @Transient
462 public T getBasionym(){
463 //TODO: pick the right name relationships...
464 logger.warn("get Basionym not yet implemented");
465 return null;
466 }
467 /**
468 * Assigns another taxon name as {@link NameRelationshipType.BASIONYM() basionym} of <i>this</i> taxon name.
469 * The basionym relationship will be added to <i>this</i> taxon name
470 * and to the basionym. The basionym cannot have itself a basionym.
471 * The homotypical group of <i>this</i> taxon name will be changed the basionyms homotypical group.
472 * @see #getBasionym()
473 * @see #addBasionym(TaxonNameBase, String)
474 */
475 public void addBasionym(T basionym){
476 addBasionym(basionym, null);
477 }
478 /**
479 * Assigns another taxon name as {@link NameRelationshipType.BASIONYM() basionym} of <i>this</i> taxon name
480 * and keeps the nomenclatural rule considered for it. The basionym
481 * relationship will be added to <i>this</i> taxon name and to the basionym.
482 * The basionym cannot have itself as a basionym.
483 * The homotypical group of <i>this</i> taxon name will be changed the basionyms homotypical group.
484 * @see #getBasionym()
485 * @see #setBasionym(TaxonNameBase)
486 */
487 public void addBasionym(T basionym, String ruleConsidered){
488 if (basionym != null){
489 basionym.addRelationshipToName(this, NameRelationshipType.BASIONYM(), ruleConsidered);
490 }
491 }
492
493 public void removeBasionym(){
494 //TODO implement
495 logger.warn("not yet implemented");
496 }
497
498
499
500 @Transient
501 public abstract S getCacheStrategy();
502 public abstract void setCacheStrategy(S cacheStrategy);
503
504 /**
505 * Returns the taxonomic {@link Rank rank} of <i>this</i> taxon name.
506 *
507 * @see Rank
508 */
509 @ManyToOne
510 //@Cascade({CascadeType.SAVE_UPDATE})
511 public Rank getRank(){
512 return this.rank;
513 }
514 /**
515 * @see #getRank()
516 */
517 public void setRank(Rank rank){
518 this.rank = rank;
519 }
520
521 /**
522 * Returns the {@link reference.INomenclaturalReference nomenclatural reference} of <i>this</i> taxon name.
523 * The nomenclatural reference is here meant to be the one publication
524 * <i>this</i> taxon name was originally published in while fulfilling the formal
525 * requirements as specified by the corresponding {@link NomenclaturalCode nomenclatural code}.
526 *
527 * @see reference.INomenclaturalReference
528 * @see reference.ReferenceBase
529 */
530 @ManyToOne
531 @Cascade({CascadeType.SAVE_UPDATE})
532 @Target(ReferenceBase.class)
533 public INomenclaturalReference getNomenclaturalReference(){
534 return this.nomenclaturalReference;
535 }
536 /**
537 * Assigns a nomenclatural {@link reference.INomenclaturalReference nomenclatural reference} to <i>this</i> taxon name.
538 * The corresponding {@link reference.ReferenceBase.isNomenclaturallyRelevant nomenclaturally relevant flag} will be set to true
539 * as it is obviously used for nomenclatural purposes.
540 *
541 * @see #getNomenclaturalReference()
542 */
543 public void setNomenclaturalReference(INomenclaturalReference nomenclaturalReference){
544 this.nomenclaturalReference = nomenclaturalReference;
545 }
546
547 /**
548 * Returns the appended phrase string assigned to <i>this</i> taxon name.
549 * The appended phrase is a non-atomised addition to a name. It is
550 * not ruled by a nomenclatural code.
551 */
552 public String getAppendedPhrase(){
553 return this.appendedPhrase;
554 }
555 /**
556 * @see #getAppendedPhrase()
557 */
558 public void setAppendedPhrase(String appendedPhrase){
559 this.appendedPhrase = appendedPhrase;
560 }
561
562 /**
563 * Returns the details string of the nomenclatural reference assigned
564 * to <i>this</i> taxon name. The details describe the exact localisation within
565 * the publication used as nomenclature reference. These are mostly
566 * (implicitly) pages but can also be figures or tables or any other
567 * element of a publication. A nomenclatural micro reference (details)
568 * requires the existence of a nomenclatural reference.
569 */
570 //Details of the nomenclatural reference (protologue).
571 public String getNomenclaturalMicroReference(){
572 return this.nomenclaturalMicroReference;
573 }
574 /**
575 * @see #getNomenclaturalMicroReference()
576 */
577 public void setNomenclaturalMicroReference(String nomenclaturalMicroReference){
578 this.nomenclaturalMicroReference = nomenclaturalMicroReference;
579 }
580
581 /**
582 * Returns the boolean value of the flag indicating whether the used {@link eu.etaxonomy.cdm.strategy.parser.INonViralNameParser parser}
583 * method was able to parse the taxon name string successfully (false)
584 * or not (true). The parser itself may also depend on the {@link NomenclaturalCode nomenclatural code}
585 * governing the construction of <i>this</i> taxon name.
586 *
587 * @return the boolean value of the hasProblem flag
588 * @see #getNameCache()
589 */
590 public boolean getHasProblem(){
591 return this.hasProblem;
592 }
593 /**
594 * @see #getHasProblem()
595 */
596 public void setHasProblem(boolean hasProblem){
597 this.hasProblem = hasProblem;
598 }
599 /**
600 * Returns exactly the same boolean value as the {@link #getHasProblem() getHasProblem} method.
601 *
602 * @see #getHasProblem()
603 */
604 public boolean hasProblem(){
605 return getHasProblem();
606 }
607
608
609
610 //*********************** TYPE DESIGNATION *********************************************//
611
612
613
614 /**
615 * Returns the set of {@link TypeDesignationBase type designations} assigned
616 * to <i>this</i> taxon name.
617 * @see NameTypeDesignation
618 * @see SpecimenTypeDesignation
619 */
620 @ManyToMany
621 //TODO @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE_ORPHAN})
622 @Cascade(CascadeType.SAVE_UPDATE)
623 public Set<TypeDesignationBase> getTypeDesignations() {
624 return typeDesignations;
625 }
626
627 /**
628 * @see #getNameTypeDesignations()
629 */
630 private void setTypeDesignations(Set<TypeDesignationBase> typeDesignations) {
631 this.typeDesignations = typeDesignations;
632 }
633
634 /**
635 * Removes one element from the set of {@link TypeDesignationBase type designations} assigned to the
636 * {@link HomotypicalGroup homotypical group} to which <i>this</i> taxon name belongs.
637 * The type designation itself will be nullified.
638 *
639 * @param typeDesignation the type designation which should be deleted
640 * @see #removeSpecimenTypeDesignation(SpecimenTypeDesignation)
641 * @see #removeNameTypeDesignation(NameTypeDesignation)
642 */
643 public void removeTypeDesignation(TypeDesignationBase typeDesignation) {
644 logger.warn("not yet fully implemented: nullify the specimen type designation itself?");
645 this.typeDesignations.remove(typeDesignation);
646 }
647
648
649 /**
650 * Returns the set of {@link SpecimenTypeDesignation specimen type designations} assigned
651 * indirectly to <i>this</i> taxon name through its {@link HomotypicalGroup homotypical group}.
652 * The rank of <i>this</i> taxon name is generally "species" or below.
653 * The specimen type designations include all the specimens on which
654 * the typification of this name is based (and which are common to all
655 * taxon names belonging to the homotypical group) and eventually
656 * the status of these designations.
657 *
658 * @see SpecimenTypeDesignation
659 * @see NameTypeDesignation
660 */
661 @Transient
662 public Set<SpecimenTypeDesignation> getSpecimenTypeDesignationsOfHomotypicalGroup() {
663 return this.getHomotypicalGroup().getSpecimenTypeDesignations();
664 }
665
666 //*********************** NAME TYPE DESIGNATION *********************************************//
667
668 /**
669 * Returns the set of {@link NameTypeDesignation name type designations} assigned
670 * to <i>this</i> taxon name the rank of which must be above "species".
671 * The name type designations include all the taxon names used to typify
672 * this name and eventually the rejected or conserved status
673 * of these designations.
674 *
675 * @see NameTypeDesignation
676 * @see SpecimenTypeDesignation
677 */
678 @Transient
679 public Set<NameTypeDesignation> getNameTypeDesignations() {
680 Set<NameTypeDesignation> result = new HashSet<NameTypeDesignation>();
681 for (TypeDesignationBase typeDesignation : this.typeDesignations){
682 if (typeDesignation instanceof NameTypeDesignation){
683 result.add((NameTypeDesignation)typeDesignation);
684 }
685 }
686 return result;
687 }
688
689 /**
690 * Creates and adds a new {@link NameTypeDesignation name type designation}
691 * to <i>this</i> taxon name's set of name type designations.
692 *
693 * @param typeSpecies the taxon name to be used as type of <i>this</i> taxon name
694 * @param citation the reference for this new designation
695 * @param citationMicroReference the string with the details (generally pages) within the reference
696 * @param originalNameString the taxon name string used in the reference to assert this designation
697 * @param isRejectedType the boolean status for rejected
698 * @param isConservedType the boolean status for conserved
699 * @param isLectoType the boolean status for isLectotype
700 * @param lectoCitation the reference for the lectotype
701 * @param lectoMicroCitation the String for the micro citation of a lectotype
702 * @param addToAllHomotypicNames boolean, if true the name type designation is added to all names of the homotypical
703 * group this taxon name belongs to
704 * @see #getNameTypeDesignations()
705 * @see #addTypeDesignation(Specimen, TypeDesignationStatus, ReferenceBase, String, String)
706 */
707 public void addNameTypeDesignation(TaxonNameBase typeSpecies,
708 ReferenceBase citation,
709 String citationMicroReference,
710 String originalNameString,
711 boolean isRejectedType,
712 boolean isConservedType,
713 boolean isLectoType,
714 boolean isNotDesignated,
715 boolean addToAllHomotypicNames) {
716 NameTypeDesignation nameTypeDesignation = new NameTypeDesignation(typeSpecies, citation, citationMicroReference, originalNameString, isRejectedType, isConservedType, isNotDesignated);
717 addTypeDesignation(nameTypeDesignation, addToAllHomotypicNames);
718 }
719
720 //*********************** SPECIMEN TYPE DESIGNATION *********************************************//
721
722 /**
723 * Returns the set of {@link SpecimenTypeDesignation specimen type designations}
724 * that typify <i>this</i> taxon name.
725 */
726 @Transient
727 public Set<SpecimenTypeDesignation> getSpecimenTypeDesignations() {
728 Set<SpecimenTypeDesignation> result = new HashSet<SpecimenTypeDesignation>();
729 for (TypeDesignationBase typeDesignation : this.typeDesignations){
730 if (typeDesignation instanceof SpecimenTypeDesignation){
731 result.add((SpecimenTypeDesignation)typeDesignation);
732 }
733 }
734 return result;
735 }
736
737
738 /**
739 * Adds a new {@link SpecimenTypeDesignation specimen type designation}
740 * (using its attributes as parameters) to the set of specimen type designations assigned to the
741 * {@link HomotypicalGroup homotypical group} to which <i>this</i> taxon name belongs.
742 *
743 * @param typeSpecimen the specimen to be used as a type for <i>this</i> taxon name's homotypical group
744 * @param status the specimen type designation status
745 * @param citation the reference for this new specimen type designation
746 * @param citationMicroReference the string with the details (generally pages) within the reference
747 * @param originalNameString the taxon name used in the reference to assert this designation
748 * @param addToAllHomotypicNames boolean, if true the name type designation is added to all names of the homotypical
749 * group this taxon name belongs to
750 * @see HomotypicalGroup#getSpecimenTypeDesignations()
751 * @see #addTypeDesignation(TaxonNameBase, ReferenceBase, String, String, boolean, boolean)
752 * @see TypeDesignationStatus
753 */
754 public void addSpecimenTypeDesignation(Specimen typeSpecimen,
755 TypeDesignationStatus status,
756 ReferenceBase citation,
757 String citationMicroReference,
758 String originalNameString,
759 boolean addToAllHomotypicNames) {
760 SpecimenTypeDesignation specimenTypeDesignation = new SpecimenTypeDesignation(typeSpecimen, status, citation, citationMicroReference, originalNameString);
761 addTypeDesignation(specimenTypeDesignation, addToAllHomotypicNames);
762 }
763
764 private boolean addTypeDesignation(TypeDesignationBase typeDesignation, boolean addToAllNames){
765 //at them moment typeDesignations are not persisted with the homotypical group
766 //so explicit adding to the homotypical group is not necessary.
767 if (typeDesignation != null){
768 this.typeDesignations.add(typeDesignation);
769 typeDesignation.addTypifiedName(this);
770
771 if (addToAllNames){
772 for (TaxonNameBase taxonName : this.getHomotypicalGroup().getTypifiedNames()){
773 if (taxonName != this){
774 taxonName.addTypeDesignation(typeDesignation, false);
775 }
776 }
777 }
778 }
779 return true;
780 }
781
782
783
784 //*********************** HOMOTYPICAL GROUP *********************************************//
785
786
787 /**
788 * Returns the {@link HomotypicalGroup homotypical group} to which
789 * <i>this</i> taxon name belongs. A homotypical group represents all taxon names
790 * that share the same type specimens.
791 *
792 * @see HomotypicalGroup
793 */
794 @ManyToOne
795 @Cascade({CascadeType.SAVE_UPDATE})
796 public HomotypicalGroup getHomotypicalGroup() {
797 return homotypicalGroup;
798 }
799 @Deprecated //only for bidirectional and persistence use
800 protected void setHomotypicalGroup(HomotypicalGroup newHomotypicalGroup) {
801 this.homotypicalGroup = newHomotypicalGroup;
802 }
803
804 // *************************************************************************//
805
806 /**
807 * @see #getNomenclaturalReference()
808 */
809 @Transient
810 public StrictReferenceBase getCitation(){
811 //TODO What is the purpose of this method differing from the getNomenclaturalReference method?
812 logger.warn("getCitation not yet implemented");
813 return null;
814 }
815
816 /**
817 * Returns the complete string containing the
818 * {@link reference.INomenclaturalReference#getNomenclaturalCitation() nomenclatural reference citation}
819 * (including {@link #getNomenclaturalMicroReference() details}) assigned to <i>this</i> taxon name.
820 *
821 * @return the string containing the nomenclatural reference of <i>this</i> taxon name
822 * @see reference.INomenclaturalReference#getNomenclaturalCitation()
823 * @see #getNomenclaturalReference()
824 * @see #getNomenclaturalMicroReference()
825 */
826 @Transient
827 public String getCitationString(){
828 logger.warn("getCitationString not yet implemented");
829 return null;
830 }
831
832 @Transient
833 public String[] getProblems(){
834 logger.warn("getProblems not yet implemented");
835 return null;
836 }
837
838 /**
839 * Returns the string containing the publication date (generally only year)
840 * of the nomenclatural reference for <i>this</i> taxon name, null if there is
841 * no nomenclatural reference.
842 *
843 * @return the string containing the publication date of <i>this</i> taxon name
844 * @see reference.INomenclaturalReference#getYear()
845 */
846 @Transient
847 public String getReferenceYear(){
848 if (this.getNomenclaturalReference() != null ){
849 return this.getNomenclaturalReference().getYear();
850 }else{
851 return null;
852 }
853 }
854
855 /**
856 * Returns the set of {@link taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
857 * In this context a taxon base means the use of a taxon name by a reference
858 * either as a taxon ("accepted/correct" name) or as a (junior) synonym.
859 * A taxon name can be used by several distinct references but only once
860 * within a taxonomic treatment (identified by one reference).
861 *
862 * @see taxon.TaxonBase
863 * @see #getTaxa()
864 * @see #getSynonyms()
865 */
866 @OneToMany(mappedBy="name", fetch= FetchType.LAZY)
867 public Set<TaxonBase> getTaxonBases() {
868 return this.taxonBases;
869 }
870 /**
871 * @see #getTaxonBases()
872 */
873 protected void setTaxonBases(Set<TaxonBase> taxonBases) {
874 if (taxonBases == null){
875 taxonBases = new HashSet<TaxonBase>();
876 }else{
877 this.taxonBases = taxonBases;
878 }
879 }
880 /**
881 * Adds a new {@link taxon.TaxonBase taxon base}
882 * to the set of taxon bases using <i>this</i> taxon name.
883 *
884 * @param taxonBase the taxon base to be added
885 * @see #getTaxonBases()
886 * @see #removeTaxonBase(TaxonBase)
887 */
888 //TODO protected
889 public void addTaxonBase(TaxonBase taxonBase){
890 taxonBases.add(taxonBase);
891 initMethods();
892 invokeSetMethod(methodTaxonBaseSetName, taxonBase);
893 }
894 /**
895 * Removes one element from the set of {@link taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
896 *
897 * @param taxonBase the taxon base which should be removed from the corresponding set
898 * @see #addTaxonBase(TaxonBase)
899 */
900 public void removeTaxonBase(TaxonBase taxonBase){
901 taxonBases.remove(taxonBase);
902 initMethods();
903 invokeSetMethodWithNull(methodTaxonBaseSetName, taxonBase);
904 }
905
906 private void initMethods(){
907 if (methodTaxonBaseSetName == null){
908 try {
909 methodTaxonBaseSetName = TaxonBase.class.getDeclaredMethod("setName", TaxonNameBase.class);
910 methodTaxonBaseSetName.setAccessible(true);
911 } catch (Exception e) {
912 e.printStackTrace();
913 //TODO handle exception
914 }
915 }
916 if (methodDescriptionSetTaxonName == null){
917 try {
918 methodDescriptionSetTaxonName = TaxonNameDescription.class.getDeclaredMethod("setTaxonName", TaxonNameBase.class);
919 methodDescriptionSetTaxonName.setAccessible(true);
920 } catch (Exception e) {
921 e.printStackTrace();
922 //TODO handle exception
923 }
924 }
925 }
926
927
928
929 /**
930 * Returns the set of {@link taxon.Taxon taxa} ("accepted/correct" names according to any
931 * reference) that are based on <i>this</i> taxon name. This set is a subset of
932 * the set returned by getTaxonBases().
933 *
934 * @see taxon.Taxon
935 * @see #getTaxonBases()
936 * @see #getSynonyms()
937 */
938 @Transient
939 public Set<Taxon> getTaxa(){
940 Set<Taxon> result = new HashSet<Taxon>();
941 for (TaxonBase taxonBase : this.taxonBases){
942 if (taxonBase instanceof Taxon){
943 result.add((Taxon)taxonBase);
944 }
945 }
946 return result;
947 }
948
949 /**
950 * Returns the set of {@link taxon.Synonym (junior) synonyms} (according to any
951 * reference) that are based on <i>this</i> taxon name. This set is a subset of
952 * the set returned by getTaxonBases().
953 *
954 * @see taxon.Synonym
955 * @see #getTaxonBases()
956 * @see #getTaxa()
957 */
958 @Transient
959 public Set<Synonym> getSynonyms() {
960 Set<Synonym> result = new HashSet<Synonym>();
961 for (TaxonBase taxonBase : this.taxonBases){
962 if (taxonBase instanceof Synonym){
963 result.add((Synonym)taxonBase);
964 }
965 }
966 return result;
967 }
968
969
970 // *********** DESCRIPTIONS *************************************
971
972 /**
973 * Returns the set of {@link description.TaxonNameDescription taxon name descriptions} assigned
974 * to <i>this</i> taxon name. A taxon name description is a piece of information
975 * concerning the taxon name like for instance the content of its first
976 * publication (protolog) or a picture of this publication.
977 *
978 * @see #addDescription(TaxonNameDescription)
979 * @see #removeDescription(TaxonNameDescription)
980 * @see description.TaxonNameDescription
981 */
982 @OneToMany(mappedBy="taxonName", fetch= FetchType.LAZY)
983 @Cascade({CascadeType.SAVE_UPDATE})
984 public Set<TaxonNameDescription> getDescriptions() {
985 return descriptions;
986 }
987 /**
988 * @see #getDescriptions()
989 */
990 protected void setDescriptions(Set<TaxonNameDescription> descriptions) {
991 this.descriptions = descriptions;
992 }
993 /**
994 * Adds a new {@link description.TaxonNameDescription taxon name description}
995 * to the set of taxon name descriptions assigned to <i>this</i> taxon name. The
996 * content of the {@link description.TaxonNameDescription#getTaxonName() taxonName attribute} of the
997 * taxon name description itself will be replaced with <i>this</i> taxon name.
998 *
999 * @param description the taxon name description to be added
1000 * @see #getDescriptions()
1001 * @see #removeDescription(TaxonNameDescription)
1002 */
1003 public void addDescription(TaxonNameDescription description) {
1004 initMethods();
1005 this.invokeSetMethod(methodDescriptionSetTaxonName, description);
1006 descriptions.add(description);
1007 }
1008 /**
1009 * Removes one element from the set of {@link description.TaxonNameDescription taxon name descriptions} assigned
1010 * to <i>this</i> taxon name. The content of the {@link description.TaxonNameDescription#getTaxonName() taxonName attribute}
1011 * of the description itself will be set to "null".
1012 *
1013 * @param description the taxon name description which should be removed
1014 * @see #getDescriptions()
1015 * @see #addDescription(TaxonNameDescription)
1016 * @see description.TaxonNameDescription#getTaxonName()
1017 */
1018 public void removeDescription(TaxonNameDescription description) {
1019 initMethods();
1020 this.invokeSetMethod(methodDescriptionSetTaxonName, null);
1021 descriptions.remove(description);
1022 }
1023
1024
1025
1026
1027
1028
1029 // ***********
1030 /**
1031 * Returns the boolean value indicating whether a given taxon name belongs
1032 * to the same {@link HomotypicalGroup homotypical group} as <i>this</i> taxon name (true)
1033 * or not (false). Returns "true" only if the homotypical groups of both
1034 * taxon names exist and if they are identical.
1035 *
1036 * @param homoTypicName the taxon name the homotypical group of which is to be checked
1037 * @return the boolean value of the check
1038 * @see HomotypicalGroup
1039 */
1040 public boolean isHomotypic(TaxonNameBase homoTypicName) {
1041 if (homoTypicName == null) {
1042 return false;
1043 }
1044 HomotypicalGroup homotypicGroup = homoTypicName.getHomotypicalGroup();
1045 if (homotypicGroup == null || this.getHomotypicalGroup() == null) {
1046 return false;
1047 }
1048 if (homotypicGroup.equals(this.getHomotypicalGroup())) {
1049 return true;
1050 }
1051 return false;
1052 }
1053
1054
1055
1056 //********* Rank comparison shortcuts ********************//
1057 /**
1058 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1059 * taxon name is higher than the genus rank (true) or not (false).
1060 * Suprageneric non viral names are monomials.
1061 * Returns false if rank is null.
1062 *
1063 * @see #isGenus()
1064 * @see #isInfraGeneric()
1065 * @see #isSpecies()
1066 * @see #isInfraSpecific()
1067 */
1068 @Transient
1069 public boolean isSupraGeneric() {
1070 if (rank == null){
1071 return false;
1072 }
1073 return getRank().isSupraGeneric();
1074 }
1075 /**
1076 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1077 * taxon name is the genus rank (true) or not (false). Non viral names with
1078 * genus rank are monomials. Returns false if rank is null.
1079 *
1080 * @see #isSupraGeneric()
1081 * @see #isInfraGeneric()
1082 * @see #isSpecies()
1083 * @see #isInfraSpecific()
1084 */
1085 @Transient
1086 public boolean isGenus() {
1087 if (rank == null){
1088 return false;
1089 }
1090 return getRank().isGenus();
1091 }
1092 /**
1093 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1094 * taxon name is higher than the species rank and lower than the
1095 * genus rank (true) or not (false). Infrageneric non viral names are
1096 * binomials. Returns false if rank is null.
1097 *
1098 * @see #isSupraGeneric()
1099 * @see #isGenus()
1100 * @see #isSpecies()
1101 * @see #isInfraSpecific()
1102 */
1103 @Transient
1104 public boolean isInfraGeneric() {
1105 if (rank == null){
1106 return false;
1107 }
1108 return getRank().isInfraGeneric();
1109 }
1110 /**
1111 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1112 * taxon name is the species rank (true) or not (false). Non viral names
1113 * with species rank are binomials.
1114 * Returns false if rank is null.
1115 *
1116 * @see #isSupraGeneric()
1117 * @see #isGenus()
1118 * @see #isInfraGeneric()
1119 * @see #isInfraSpecific()
1120 */
1121 @Transient
1122 public boolean isSpecies() {
1123 if (rank == null){
1124 return false;
1125 }
1126 return getRank().isSpecies();
1127 }
1128 /**
1129 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1130 * taxon name is lower than the species rank (true) or not (false).
1131 * Infraspecific non viral names are trinomials.
1132 * Returns false if rank is null.
1133 *
1134 * @see #isSupraGeneric()
1135 * @see #isGenus()
1136 * @see #isInfraGeneric()
1137 * @see #isSpecies()
1138 */
1139 @Transient
1140 public boolean isInfraSpecific() {
1141 if (rank == null){
1142 return false;
1143 }
1144 return getRank().isInfraSpecific();
1145 }
1146
1147
1148 /**
1149 * Returns null as the {@link NomenclaturalCode nomenclatural code} that governs
1150 * the construction of <i>this</i> taxon name since there is no specific
1151 * nomenclatural code defined. The real implementention takes place in the
1152 * subclasses {@link ViralName ViralName}, {@link BacterialName BacterialName},
1153 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
1154 * {@link ZoologicalName ZoologicalName}. Each taxon name is governed by one
1155 * and only one nomenclatural code.
1156 *
1157 * @return null
1158 * @see #isCodeCompliant()
1159 * @see #getHasProblem()
1160 */
1161 @Transient
1162 abstract public NomenclaturalCode getNomenclaturalCode();
1163 /* (non-Javadoc)
1164 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1165 */
1166 /**
1167 * Generates and returns the string with the scientific name of <i>this</i>
1168 * taxon name (only non viral taxon names can be generated from their
1169 * components). This string may be stored in the inherited
1170 * {@link common.IdentifiableEntity#getTitleCache() titleCache} attribute.
1171 * This method overrides the generic and inherited
1172 * IdentifiableEntity#generateTitle() method.
1173 *
1174 * @return the string with the composed name of this non viral taxon name with authorship (and maybe year)
1175 * @see common.IdentifiableEntity#generateTitle()
1176 * @see common.IdentifiableEntity#getTitleCache()
1177 */
1178 @Override
1179 public String generateTitle() {
1180 // TODO Auto-generated method stub
1181 logger.warn("not yet implemented");
1182 return null;
1183 }
1184
1185 }