Implement correct mapping between TaxonNameBase and TypeDesignationBase and add...
[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.List;
15 import java.util.Map;
16 import java.util.Set;
17
18 import javax.persistence.Column;
19 import javax.persistence.Entity;
20 import javax.persistence.FetchType;
21 import javax.persistence.Inheritance;
22 import javax.persistence.InheritanceType;
23 import javax.persistence.JoinTable;
24 import javax.persistence.ManyToMany;
25 import javax.persistence.ManyToOne;
26 import javax.persistence.OneToMany;
27 import javax.persistence.Transient;
28 import javax.validation.Valid;
29 import javax.validation.constraints.NotNull;
30 import javax.validation.constraints.Size;
31 import javax.xml.bind.annotation.XmlAccessType;
32 import javax.xml.bind.annotation.XmlAccessorType;
33 import javax.xml.bind.annotation.XmlAttribute;
34 import javax.xml.bind.annotation.XmlElement;
35 import javax.xml.bind.annotation.XmlElementWrapper;
36 import javax.xml.bind.annotation.XmlIDREF;
37 import javax.xml.bind.annotation.XmlRootElement;
38 import javax.xml.bind.annotation.XmlSchemaType;
39 import javax.xml.bind.annotation.XmlType;
40
41 import org.apache.log4j.Logger;
42 import org.hibernate.annotations.Cascade;
43 import org.hibernate.annotations.CascadeType;
44 import org.hibernate.annotations.Index;
45 import org.hibernate.annotations.Table;
46 import org.hibernate.envers.Audited;
47 import org.hibernate.search.annotations.Field;
48 import org.hibernate.search.annotations.IndexedEmbedded;
49 import org.hibernate.validator.constraints.NotEmpty;
50 import org.springframework.util.ReflectionUtils;
51
52 import eu.etaxonomy.cdm.model.common.IParsable;
53 import eu.etaxonomy.cdm.model.common.IReferencedEntity;
54 import eu.etaxonomy.cdm.model.common.IRelated;
55 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
56 import eu.etaxonomy.cdm.model.common.RelationshipBase;
57 import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
58 import eu.etaxonomy.cdm.model.occurrence.Specimen;
59 import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
60 import eu.etaxonomy.cdm.model.reference.Reference;
61 import eu.etaxonomy.cdm.model.taxon.Synonym;
62 import eu.etaxonomy.cdm.model.taxon.Taxon;
63 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
64 import eu.etaxonomy.cdm.strategy.cache.name.CacheUpdate;
65 import eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy;
66 import eu.etaxonomy.cdm.strategy.match.IMatchable;
67 import eu.etaxonomy.cdm.strategy.match.Match;
68 import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
69 import eu.etaxonomy.cdm.strategy.match.MatchMode;
70 import eu.etaxonomy.cdm.strategy.merge.Merge;
71 import eu.etaxonomy.cdm.strategy.merge.MergeMode;
72 import eu.etaxonomy.cdm.strategy.parser.ParserProblem;
73 import eu.etaxonomy.cdm.validation.Level2;
74 import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
75
76 /**
77 * The upmost (abstract) class for scientific taxon names regardless of any
78 * particular {@link NomenclaturalCode nomenclature code}. The scientific taxon name does not depend
79 * on the use made of it in a publication or a treatment
80 * ({@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon concept respectively potential taxon})
81 * as an {@link eu.etaxonomy.cdm.model.taxon.Taxon "accepted" respectively "correct" (taxon) name}
82 * or as a {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
83 * <P>
84 * This class corresponds partially to: <ul>
85 * <li> TaxonName according to the TDWG ontology
86 * <li> ScientificName and CanonicalName according to the TCS
87 * <li> ScientificName according to the ABCD schema
88 * </ul>
89 *
90 * @author m.doering
91 * @version 1.0
92 * @created 08-Nov-2007 13:06:57
93 */
94 @XmlAccessorType(XmlAccessType.FIELD)
95 @XmlType(name = "TaxonNameBase", propOrder = {
96 "appendedPhrase",
97 "nomenclaturalMicroReference",
98 "nomenclaturalReference",
99 "rank",
100 "fullTitleCache",
101 "protectedFullTitleCache",
102 "homotypicalGroup",
103 "typeDesignations",
104 "relationsFromThisName",
105 "relationsToThisName",
106 "status",
107 "descriptions",
108 "taxonBases"
109 })
110 @XmlRootElement(name = "TaxonNameBase")
111 @Entity
112 @Audited
113 @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
114 @Table(appliesTo="TaxonNameBase", indexes = { @Index(name = "taxonNameBaseTitleCacheIndex", columnNames = { "titleCache" }) })
115 public abstract class TaxonNameBase<T extends TaxonNameBase<?,?>, S extends INameCacheStrategy> extends IdentifiableEntity<S> implements IReferencedEntity, IParsable, IRelated, IMatchable, Cloneable {
116 private static final long serialVersionUID = -4530368639601532116L;
117 private static final Logger logger = Logger.getLogger(TaxonNameBase.class);
118
119 @XmlElement(name = "FullTitleCache")
120 @Column(length=330, name="fullTitleCache")
121 @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
122 @CacheUpdate(noUpdate ="titleCache")
123 @NotEmpty(groups = Level2.class)
124 @Size(max = 330)
125 protected String fullTitleCache;
126
127 //if true titleCache will not be automatically generated/updated
128 @XmlElement(name = "ProtectedFullTitleCache")
129 @CacheUpdate(value ="fullTitleCache", noUpdate ="titleCache")
130 private boolean protectedFullTitleCache;
131
132 @XmlElementWrapper(name = "Descriptions")
133 @XmlElement(name = "Description")
134 @OneToMany(mappedBy="taxonName", fetch= FetchType.LAZY)
135 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
136 @NotNull
137 private Set<TaxonNameDescription> descriptions = new HashSet<TaxonNameDescription>();
138
139 @XmlElement(name = "AppendedPhrase")
140 @Field(index= org.hibernate.search.annotations.Index.TOKENIZED)
141 @CacheUpdate(value ="nameCache")
142 @NullOrNotEmpty
143 @Size(max = 255)
144 private String appendedPhrase;
145
146 @XmlElement(name = "NomenclaturalMicroReference")
147 @Field(index= org.hibernate.search.annotations.Index.TOKENIZED)
148 @CacheUpdate(noUpdate ="titleCache")
149 @NullOrNotEmpty
150 @Size(max = 255)
151 private String nomenclaturalMicroReference;
152
153 @XmlAttribute
154 @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
155 private int parsingProblem = 0;
156
157 @XmlAttribute
158 @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
159 private int problemStarts = -1;
160
161 @XmlAttribute
162 @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
163 private int problemEnds = -1;
164
165 @XmlElementWrapper(name = "TypeDesignations")
166 @XmlElement(name = "TypeDesignation")
167 @XmlIDREF
168 @XmlSchemaType(name = "IDREF")
169 @ManyToMany(fetch = FetchType.LAZY)
170 @JoinTable(
171 name="TaxonNameBase_TypeDesignationBase",
172 joinColumns=@javax.persistence.JoinColumn(name="TaxonNameBase_id"),
173 inverseJoinColumns=@javax.persistence.JoinColumn(name="typedesignations_id")
174 )
175 @Cascade({CascadeType.SAVE_UPDATE})
176 @NotNull
177 private Set<TypeDesignationBase> typeDesignations = new HashSet<TypeDesignationBase>();
178
179 @XmlElement(name = "HomotypicalGroup")
180 @XmlIDREF
181 @XmlSchemaType(name = "IDREF")
182 @ManyToOne(fetch = FetchType.LAZY)
183 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
184 @Match(MatchMode.IGNORE)
185 @CacheUpdate(noUpdate ="titleCache")
186 @NotNull
187 private HomotypicalGroup homotypicalGroup;
188
189 @XmlElementWrapper(name = "RelationsFromThisName")
190 @XmlElement(name = "RelationFromThisName")
191 @OneToMany(mappedBy="relatedFrom", fetch= FetchType.LAZY)
192 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE_ORPHAN})
193 @Merge(MergeMode.RELATION)
194 @NotNull
195 @Valid
196 private Set<NameRelationship> relationsFromThisName = new HashSet<NameRelationship>();
197
198 @XmlElementWrapper(name = "RelationsToThisName")
199 @XmlElement(name = "RelationToThisName")
200 @XmlIDREF
201 @XmlSchemaType(name = "IDREF")
202 @OneToMany(mappedBy="relatedTo", fetch= FetchType.LAZY)
203 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE , CascadeType.DELETE_ORPHAN })
204 @Merge(MergeMode.RELATION)
205 @NotNull
206 @Valid
207 private Set<NameRelationship> relationsToThisName = new HashSet<NameRelationship>();
208
209 @XmlElementWrapper(name = "NomenclaturalStatuses")
210 @XmlElement(name = "NomenclaturalStatus")
211 @OneToMany(fetch= FetchType.LAZY)
212 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE,CascadeType.DELETE,CascadeType.DELETE_ORPHAN})
213 @NotNull
214 private Set<NomenclaturalStatus> status = new HashSet<NomenclaturalStatus>();
215
216 @XmlElementWrapper(name = "TaxonBases")
217 @XmlElement(name = "TaxonBase")
218 @XmlIDREF
219 @XmlSchemaType(name = "IDREF")
220 @OneToMany(mappedBy="name", fetch= FetchType.LAZY)
221 @NotNull
222 private Set<TaxonBase> taxonBases = new HashSet<TaxonBase>();
223
224 @XmlElement(name = "Rank")
225 @XmlIDREF
226 @XmlSchemaType(name = "IDREF")
227 @ManyToOne(fetch = FetchType.EAGER)
228 @CacheUpdate(value ="nameCache")
229 @NotNull
230 private Rank rank;
231
232 @XmlElement(name = "NomenclaturalReference")
233 @XmlIDREF
234 @XmlSchemaType(name = "IDREF")
235 @ManyToOne(fetch = FetchType.LAZY)
236 @Cascade({CascadeType.SAVE_UPDATE})
237 @CacheUpdate(noUpdate ="titleCache")
238 @IndexedEmbedded
239 private Reference nomenclaturalReference;
240
241 // ************* CONSTRUCTORS *************/
242 /**
243 * Class constructor: creates a new empty taxon name.
244 *
245 * @see #TaxonNameBase(Rank)
246 * @see #TaxonNameBase(HomotypicalGroup)
247 * @see #TaxonNameBase(Rank, HomotypicalGroup)
248 */
249 public TaxonNameBase() {
250 super();
251 }
252 /**
253 * Class constructor: creates a new taxon name
254 * only containing its {@link Rank rank}.
255 *
256 * @param rank the rank to be assigned to <i>this</i> taxon name
257 * @see #TaxonNameBase()
258 * @see #TaxonNameBase(HomotypicalGroup)
259 * @see #TaxonNameBase(Rank, HomotypicalGroup)
260 */
261 public TaxonNameBase(Rank rank) {
262 this(rank, null);
263 }
264 /**
265 * Class constructor: creates a new taxon name
266 * only containing its {@link HomotypicalGroup homotypical group}.
267 * The new taxon name will be also added to the set of taxon names
268 * belonging to this homotypical group.
269 *
270 * @param homotypicalGroup the homotypical group to which <i>this</i> taxon name belongs
271 * @see #TaxonNameBase()
272 * @see #TaxonNameBase(Rank)
273 * @see #TaxonNameBase(Rank, HomotypicalGroup)
274 */
275 public TaxonNameBase(HomotypicalGroup homotypicalGroup) {
276 this(null, homotypicalGroup);
277 }
278 /**
279 * Class constructor: creates a new taxon name
280 * only containing its {@link Rank rank} and
281 * its {@link HomotypicalGroup homotypical group}.
282 * The new taxon name will be also added to the set of taxon names
283 * belonging to this homotypical group.
284 *
285 * @param rank the rank to be assigned to <i>this</i> taxon name
286 * @param homotypicalGroup the homotypical group to which <i>this</i> taxon name belongs
287 * @see #TaxonNameBase()
288 * @see #TaxonNameBase(Rank)
289 * @see #TaxonNameBase(HomotypicalGroup)
290 */
291 public TaxonNameBase(Rank rank, HomotypicalGroup homotypicalGroup) {
292 super();
293 this.setRank(rank);
294 if (homotypicalGroup == null){
295 homotypicalGroup = new HomotypicalGroup();
296 }
297 homotypicalGroup.addTypifiedName(this);
298 }
299
300 abstract protected Map<String, java.lang.reflect.Field> getAllFields();
301
302 //********* METHODS **************************************/
303
304 /**
305 * Returns the boolean value "false" since the components of <i>this</i> taxon name
306 * cannot follow the rules of a corresponding {@link NomenclaturalCode nomenclatural code}
307 * which is not defined for this class. The nomenclature code depends on
308 * the concrete name subclass ({@link BacterialName BacterialName},
309 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName},
310 * {@link ZoologicalName ZoologicalName} or {@link ViralName ViralName})
311 * to which a taxon name belongs.
312 *
313 * @return false
314 */
315 @Transient
316 public abstract boolean isCodeCompliant();
317
318 public abstract String generateFullTitle();
319
320
321
322 @Transient
323 public List<Object> getTaggedName(){
324 return getCacheStrategy().getTaggedTitle(this);
325 }
326
327 @Transient
328 public String getFullTitleCache(){
329 if (protectedFullTitleCache){
330 return this.fullTitleCache;
331 }
332 if (fullTitleCache == null ){
333 this.fullTitleCache = getTruncatedCache(generateFullTitle());
334 }
335 return fullTitleCache;
336 }
337
338
339 public void setFullTitleCache(String fullTitleCache){
340 setFullTitleCache(fullTitleCache, PROTECTED);
341 }
342
343 public void setFullTitleCache(String fullTitleCache, boolean protectCache){
344 fullTitleCache = getTruncatedCache(fullTitleCache);
345 this.fullTitleCache = fullTitleCache;
346 this.setProtectedFullTitleCache(protectCache);
347 }
348
349
350 public boolean isProtectedFullTitleCache() {
351 return protectedFullTitleCache;
352 }
353
354 public void setProtectedFullTitleCache(boolean protectedFullTitleCache) {
355 this.protectedFullTitleCache = protectedFullTitleCache;
356 }
357
358 /**
359 * Returns the set of all {@link NameRelationship name relationships}
360 * in which <i>this</i> taxon name is involved. A taxon name can be both source
361 * in some name relationships or target in some others.
362 *
363 * @see #getRelationsToThisName()
364 * @see #getRelationsFromThisName()
365 * @see #addNameRelationship(NameRelationship)
366 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
367 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
368 */
369 @Transient
370 public Set<NameRelationship> getNameRelations() {
371 Set<NameRelationship> rels = new HashSet<NameRelationship>();
372 rels.addAll(getRelationsFromThisName());
373 rels.addAll(getRelationsToThisName());
374 return rels;
375 }
376
377 /**
378 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
379 * and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
380 * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
381 *
382 * @param toName the taxon name of the target for this new name relationship
383 * @param type the type of this new name relationship
384 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
385 * @see #getRelationsToThisName()
386 * @see #getNameRelations()
387 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
388 * @see #addNameRelationship(NameRelationship)
389 */
390 public void addRelationshipToName(TaxonNameBase toName, NameRelationshipType type, String ruleConsidered){
391 addRelationshipToName(toName, type, null, null, ruleConsidered);
392 // NameRelationship rel = new NameRelationship(toName, this, type, ruleConsidered);
393 }
394
395 /**
396 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from <i>this</i> taxon name to another taxon name
397 * and adds it both to the set of {@link #getRelationsFromThisName() relations from <i>this</i> taxon name} and
398 * to the set of {@link #getRelationsToThisName() relations to the other taxon name}.
399 *
400 * @param toName the taxon name of the target for this new name relationship
401 * @param type the type of this new name relationship
402 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
403 * @return
404 * @see #getRelationsToThisName()
405 * @see #getNameRelations()
406 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
407 * @see #addNameRelationship(NameRelationship)
408 */
409 public NameRelationship addRelationshipToName(TaxonNameBase toName, NameRelationshipType type, Reference citation, String microCitation, String ruleConsidered){
410 if (toName == null){
411 throw new NullPointerException("Null is not allowed as name for a name relationship");
412 }
413 NameRelationship rel = new NameRelationship(toName, this, type, citation, microCitation, ruleConsidered);
414 return rel;
415 }
416
417 /**
418 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
419 * and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
420 * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
421 *
422 * @param fromName the taxon name of the source for this new name relationship
423 * @param type the type of this new name relationship
424 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
425 * @param citation the reference in which this relation was described
426 * @param microCitation the reference detail for this relation (e.g. page)
427 * @see #getRelationsFromThisName()
428 * @see #getNameRelations()
429 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
430 * @see #addNameRelationship(NameRelationship)
431 */
432 public void addRelationshipFromName(TaxonNameBase fromName, NameRelationshipType type, String ruleConsidered){
433 fromName.addRelationshipToName(this, type, null, null, ruleConsidered);
434 // NameRelationship rel = new NameRelationship(this, fromName, type, ruleConsidered);
435 }
436 /**
437 * Creates a new {@link NameRelationship#NameRelationship(TaxonNameBase, TaxonNameBase, NameRelationshipType, String) name relationship} from another taxon name to <i>this</i> taxon name
438 * and adds it both to the set of {@link #getRelationsToThisName() relations to <i>this</i> taxon name} and
439 * to the set of {@link #getRelationsFromThisName() relations from the other taxon name}.
440 *
441 * @param fromName the taxon name of the source for this new name relationship
442 * @param type the type of this new name relationship
443 * @param ruleConsidered the string which specifies the rule on which this name relationship is based
444 * @param citation the reference in which this relation was described
445 * @param microCitation the reference detail for this relation (e.g. page)
446 * @see #getRelationsFromThisName()
447 * @see #getNameRelations()
448 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
449 * @see #addNameRelationship(NameRelationship)
450 */
451 public void addRelationshipFromName(TaxonNameBase fromName, NameRelationshipType type, Reference citation, String microCitation, String ruleConsidered){
452 fromName.addRelationshipToName(this, type, citation, microCitation, ruleConsidered);
453 }
454
455 /**
456 * Adds an existing {@link NameRelationship name relationship} either to the set of
457 * {@link #getRelationsToThisName() relations to <i>this</i> taxon name} or to the set of
458 * {@link #getRelationsFromThisName() relations from <i>this</i> taxon name}. If neither the
459 * source nor the target of the name relationship match with <i>this</i> taxon name
460 * no addition will be carried out.
461 *
462 * @param rel the name relationship to be added to one of <i>this</i> taxon name's name relationships sets
463 * @see #getNameRelations()
464 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
465 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
466 */
467 protected void addNameRelationship(NameRelationship rel) {
468 if (rel!=null && rel.getToName().equals(this)){
469 this.relationsToThisName.add(rel);
470 }else if(rel!=null && rel.getFromName().equals(this)){
471 this.relationsFromThisName.add(rel);
472 }else{
473 throw new RuntimeException("NameRelationship is either null or the relationship does not reference this name");
474 }
475 }
476 /**
477 * Removes one {@link NameRelationship name relationship} from one of both sets of
478 * {@link #getNameRelations() name relationships} in which <i>this</i> taxon name is involved.
479 * The name relationship will also be removed from one of both sets belonging
480 * to the second taxon name involved. Furthermore the fromName and toName
481 * attributes of the name relationship object will be nullified.
482 *
483 * @param nameRelation the name relationship which should be deleted from one of both sets
484 * @see #getNameRelations()
485 */
486 public void removeNameRelationship(NameRelationship nameRelation) {
487
488 TaxonNameBase fromName = nameRelation.getFromName();
489 TaxonNameBase toName = nameRelation.getToName();
490
491 if (nameRelation != null) {
492 nameRelation.setToName(null);
493 nameRelation.setFromName(null);
494 }
495
496 if (fromName != null) {
497 fromName.removeNameRelationship(nameRelation);
498 }
499
500 if (toName != null) {
501 toName.removeNameRelationship(nameRelation);
502 }
503
504 this.relationsToThisName.remove(nameRelation);
505 this.relationsFromThisName.remove(nameRelation);
506 }
507
508 public void removeRelationToTaxonName(TaxonNameBase toTaxonName) {
509 Set<NameRelationship> nameRelationships = new HashSet<NameRelationship>();
510 // nameRelationships.addAll(this.getNameRelations());
511 nameRelationships.addAll(this.getRelationsFromThisName());
512 nameRelationships.addAll(this.getRelationsToThisName());
513 for(NameRelationship nameRelationship : nameRelationships) {
514 // remove name relationship from this side
515 if (nameRelationship.getFromName().equals(this) && nameRelationship.getToName().equals(toTaxonName)) {
516 this.removeNameRelationship(nameRelationship);
517 }
518 }
519 }
520
521
522 /**
523 * Does exactly the same as the addNameRelationship method provided that
524 * the given relationship is a name relationship.
525 *
526 * @param relation the relationship to be added to one of <i>this</i> taxon name's name relationships sets
527 * @see #addNameRelationship(NameRelationship)
528 * @see #getNameRelations()
529 * @see NameRelationship
530 * @see eu.etaxonomy.cdm.model.common.RelationshipBase
531 */
532 public void addRelationship(RelationshipBase relation) {
533 if (relation instanceof NameRelationship){
534 addNameRelationship((NameRelationship)relation);
535 NameRelationshipType type = (NameRelationshipType)relation.getType();
536 if (type != null && ( type.isBasionymRelation() || type.isReplacedSynonymRelation() ) ){
537 TaxonNameBase fromName = ((NameRelationship)relation).getFromName();
538 TaxonNameBase toName = ((NameRelationship)relation).getToName();
539 fromName.mergeHomotypicGroups(toName);
540 }
541 }else{
542 logger.warn("Relationship not of type NameRelationship!");
543 throw new IllegalArgumentException("Relationship not of type NameRelationship");
544 }
545 }
546
547
548 /**
549 * Returns the set of all {@link NameRelationship name relationships}
550 * in which <i>this</i> taxon name is involved as a source ("from"-side).
551 *
552 * @see #getNameRelations()
553 * @see #getRelationsToThisName()
554 * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
555 */
556 public Set<NameRelationship> getRelationsFromThisName() {
557 if(relationsFromThisName == null) {
558 this.relationsFromThisName = new HashSet<NameRelationship>();
559 }
560 return relationsFromThisName;
561 }
562
563 /**
564 * Returns the set of all {@link NameRelationship name relationships}
565 * in which <i>this</i> taxon name is involved as a target ("to"-side).
566 *
567 * @see #getNameRelations()
568 * @see #getRelationsFromThisName()
569 * @see #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
570 */
571 public Set<NameRelationship> getRelationsToThisName() {
572 if(relationsToThisName == null) {
573 this.relationsToThisName = new HashSet<NameRelationship>();
574 }
575 return relationsToThisName;
576 }
577
578 /**
579 * Returns the set of {@link NomenclaturalStatus nomenclatural status} assigned
580 * to <i>this</i> taxon name according to its corresponding nomenclature code.
581 * This includes the {@link NomenclaturalStatusType type} of the nomenclatural status
582 * and the nomenclatural code rule considered.
583 *
584 * @see NomenclaturalStatus
585 * @see NomenclaturalStatusType
586 */
587 public Set<NomenclaturalStatus> getStatus() {
588 if(status == null) {
589 this.status = new HashSet<NomenclaturalStatus>();
590 }
591 return status;
592 }
593
594 /**
595 * Adds a new {@link NomenclaturalStatus nomenclatural status}
596 * to <i>this</i> taxon name's set of nomenclatural status.
597 *
598 * @param nomStatus the nomenclatural status to be added
599 * @see #getStatus()
600 */
601 public void addStatus(NomenclaturalStatus nomStatus) {
602 this.status.add(nomStatus);
603 }
604
605 /**
606 * Removes one element from the set of nomenclatural status of <i>this</i> taxon name.
607 * Type and ruleConsidered attributes of the nomenclatural status object
608 * will be nullified.
609 *
610 * @param nomStatus the nomenclatural status of <i>this</i> taxon name which should be deleted
611 * @see #getStatus()
612 */
613 public void removeStatus(NomenclaturalStatus nomStatus) {
614 //TODO to be implemented?
615 logger.warn("not yet fully implemented?");
616 this.status.remove(nomStatus);
617 }
618
619
620 /**
621 * Indicates whether <i>this</i> taxon name is a {@link NameRelationshipType#BASIONYM() basionym}
622 * or a {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym}
623 * of any other taxon name. Returns "true", if a basionym or a replaced
624 * synonym {@link NameRelationship relationship} from <i>this</i> taxon name to another taxon name exists,
625 * false otherwise (also in case <i>this</i> taxon name is the only one in the
626 * homotypical group).
627 */
628 @Transient
629 public boolean isOriginalCombination(){
630 Set<NameRelationship> relationsFromThisName = this.getRelationsFromThisName();
631 for (NameRelationship relation : relationsFromThisName) {
632 if (relation.getType().isBasionymRelation() ||
633 relation.getType().isReplacedSynonymRelation()) {
634 return true;
635 }
636 }
637 return false;
638 }
639
640 /**
641 * Returns the taxon name which is the {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
642 * The basionym of a taxon name is its epithet-bringing synonym.
643 * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
644 * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
645 * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
646 *
647 * If more than one basionym exists one is choosen at radom.
648 *
649 * If no basionym exists null is returned.
650 */
651 @Transient
652 public TaxonNameBase getBasionym(){
653 Set<TaxonNameBase> basionyms = getBasionyms();
654 if (basionyms.size() == 0){
655 return null;
656 }else{
657 return basionyms.iterator().next();
658 }
659 }
660
661 /**
662 * Returns the set of taxon names which are the {@link NameRelationshipType#BASIONYM() basionyms} of <i>this</i> taxon name.
663 * The basionym of a taxon name is its epithet-bringing synonym.
664 * For instance <i>Pinus abies</i> L. was published by Linnaeus and the botanist
665 * Karsten transferred later <i>this</i> taxon to the genus Picea. Therefore,
666 * <i>Pinus abies</i> L. is the basionym of the new combination <i>Picea abies</i> (L.) H. Karst.
667 */
668 @Transient
669 public Set<TaxonNameBase> getBasionyms(){
670 Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
671 Set<NameRelationship> rels = this.getRelationsToThisName();
672 for (NameRelationship rel : rels){
673 if (rel.getType().isBasionymRelation()){
674 TaxonNameBase basionym = rel.getFromName();
675 result.add(basionym);
676 }
677 }
678 return result;
679 }
680
681 /**
682 * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name.
683 * The basionym {@link NameRelationship relationship} will be added to <i>this</i> taxon name
684 * and to the basionym. The basionym cannot have itself a basionym.
685 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
686 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
687 *
688 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
689 * @see #getBasionym()
690 * @see #addBasionym(TaxonNameBase, String)
691 */
692 public void addBasionym(T basionym){
693 addBasionym(basionym, null, null, null);
694 }
695 /**
696 * Assigns a taxon name as {@link NameRelationshipType#BASIONYM() basionym} of <i>this</i> taxon name
697 * and keeps the nomenclatural rule considered for it. The basionym
698 * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the basionym.
699 * The basionym cannot have itself a basionym.
700 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the basionym
701 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
702 *
703 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
704 * @param ruleConsidered the string identifying the nomenclatural rule
705 * @return
706 * @see #getBasionym()
707 * @see #addBasionym(TaxonNameBase)
708 */
709 public NameRelationship addBasionym(T basionym, Reference citation, String microcitation, String ruleConsidered){
710 if (basionym != null){
711 return basionym.addRelationshipToName(this, NameRelationshipType.BASIONYM(), citation, microcitation, ruleConsidered);
712 }else{
713 return null;
714 }
715 }
716
717 /**
718 * Assigns a taxon name as {@link NameRelationshipType#REPLACED_SYNONYM() replaced synonym} of <i>this</i> taxon name
719 * and keeps the nomenclatural rule considered for it. The replaced synonym
720 * {@link NameRelationship relationship} will be added to <i>this</i> taxon name and to the replaced synonym.
721 * The {@link HomotypicalGroup homotypical groups} of <i>this</i> taxon name and of the replaced synonym
722 * will be {@link HomotypicalGroup#merge(HomotypicalGroup) merged}.
723 *
724 * @param basionym the taxon name to be set as the basionym of <i>this</i> taxon name
725 * @param ruleConsidered the string identifying the nomenclatural rule
726 * @see #getBasionym()
727 * @see #addBasionym(TaxonNameBase)
728 */
729 //TODO: Check if true: The replaced synonym cannot have itself a replaced synonym (?).
730 public void addReplacedSynonym(T replacedSynonym, Reference citation, String microcitation, String ruleConsidered){
731 if (replacedSynonym != null){
732 replacedSynonym.addRelationshipToName(this, NameRelationshipType.REPLACED_SYNONYM(), citation, microcitation, ruleConsidered);
733 }
734 }
735
736 /**
737 * Removes the {@link NameRelationshipType#BASIONYM() basionym} {@link NameRelationship relationship} from the set of
738 * {@link #getRelationsToThisName() name relationships to} <i>this</i> taxon name. The same relationhip will be
739 * removed from the set of {@link #getRelationsFromThisName() name relationships from} the taxon name
740 * previously used as basionym.
741 *
742 * @see #getBasionym()
743 * @see #addBasionym(TaxonNameBase)
744 */
745 public void removeBasionyms(){
746 Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
747 for (NameRelationship nameRelation : this.getRelationsToThisName()){
748 if (nameRelation.getType().isBasionymRelation()){
749 removeRelations.add(nameRelation);
750 }
751 }
752 // Removing relations from a set through which we are iterating causes a
753 // ConcurrentModificationException. Therefore, we delete the targeted
754 // relations in a second step.
755 for (NameRelationship relation : removeRelations){
756 this.removeNameRelationship(relation);
757 }
758 }
759
760 /**
761 * Returns the taxonomic {@link Rank rank} of <i>this</i> taxon name.
762 *
763 * @see Rank
764 */
765 public Rank getRank(){
766 return this.rank;
767 }
768
769 /**
770 * @see #getRank()
771 */
772 public void setRank(Rank rank){
773 this.rank = rank;
774 }
775
776 /**
777 * Returns the {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} of <i>this</i> taxon name.
778 * The nomenclatural reference is here meant to be the one publication
779 * <i>this</i> taxon name was originally published in while fulfilling the formal
780 * requirements as specified by the corresponding {@link NomenclaturalCode nomenclatural code}.
781 *
782 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference
783 * @see eu.etaxonomy.cdm.model.reference.Reference
784 */
785 public INomenclaturalReference getNomenclaturalReference(){
786 return this.nomenclaturalReference;
787 }
788 /**
789 * Assigns a {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference nomenclatural reference} to <i>this</i> taxon name.
790 * The corresponding {@link eu.etaxonomy.cdm.model.reference.Reference.isNomenclaturallyRelevant nomenclaturally relevant flag} will be set to true
791 * as it is obviously used for nomenclatural purposes.
792 *
793 * @throws IllegalArgumentException if parameter <code>nomenclaturalReference</code> is not assignable from {@link INomenclaturalReference}
794 * @see #getNomenclaturalReference()
795 */
796 public void setNomenclaturalReference(INomenclaturalReference nomenclaturalReference){
797 if(nomenclaturalReference != null){
798 if(!INomenclaturalReference.class.isAssignableFrom(nomenclaturalReference.getClass())){
799 throw new IllegalArgumentException("Parameter nomenclaturalReference is not assignable from INomenclaturalReference");
800 }
801 this.nomenclaturalReference = (Reference)nomenclaturalReference;
802 } else {
803 this.nomenclaturalReference = null;
804 }
805 }
806
807 /**
808 * Returns the appended phrase string assigned to <i>this</i> taxon name.
809 * The appended phrase is a non-atomised addition to a name. It is
810 * not ruled by a nomenclatural code.
811 */
812 public String getAppendedPhrase(){
813 return this.appendedPhrase;
814 }
815
816 /**
817 * @see #getAppendedPhrase()
818 */
819 public void setAppendedPhrase(String appendedPhrase){
820 this.appendedPhrase = appendedPhrase;
821 }
822
823 /**
824 * Returns the details string of the {@link #getNomenclaturalReference() nomenclatural reference} assigned
825 * to <i>this</i> taxon name. The details describe the exact localisation within
826 * the publication used as nomenclature reference. These are mostly
827 * (implicitly) pages but can also be figures or tables or any other
828 * element of a publication. A nomenclatural micro reference (details)
829 * requires the existence of a nomenclatural reference.
830 */
831 //Details of the nomenclatural reference (protologue).
832 public String getNomenclaturalMicroReference(){
833 return this.nomenclaturalMicroReference;
834 }
835 /**
836 * @see #getNomenclaturalMicroReference()
837 */
838 public void setNomenclaturalMicroReference(String nomenclaturalMicroReference){
839 this.nomenclaturalMicroReference = nomenclaturalMicroReference;
840 }
841
842 /* (non-Javadoc)
843 * @see eu.etaxonomy.cdm.model.common.IParsable#getHasProblem()
844 */
845 public int getParsingProblem(){
846 return this.parsingProblem;
847 }
848
849 /* (non-Javadoc)
850 * @see eu.etaxonomy.cdm.model.common.IParsable#setHasProblem(int)
851 */
852 public void setParsingProblem(int parsingProblem){
853 this.parsingProblem = parsingProblem;
854 }
855
856 /* (non-Javadoc)
857 * @see eu.etaxonomy.cdm.model.common.IParsable#addProblem(eu.etaxonomy.cdm.strategy.parser.NameParserWarning)
858 */
859 public void addParsingProblem(ParserProblem problem){
860 parsingProblem = ParserProblem.addProblem(parsingProblem, problem);
861 }
862
863 /* (non-Javadoc)
864 * @see eu.etaxonomy.cdm.model.common.IParsable#removeParsingProblem(eu.etaxonomy.cdm.strategy.parser.ParserProblem)
865 */
866 public void removeParsingProblem(ParserProblem problem) {
867 parsingProblem = ParserProblem.removeProblem(parsingProblem, problem);
868 }
869
870 /**
871 * @param warnings
872 */
873 public void addParsingProblems(int problems){
874 parsingProblem = ParserProblem.addProblems(parsingProblem, problems);
875 }
876
877 /* (non-Javadoc)
878 * @see eu.etaxonomy.cdm.model.common.IParsable#hasProblem()
879 */
880 public boolean hasProblem(){
881 return parsingProblem != 0;
882 }
883
884
885
886 /* (non-Javadoc)
887 * @see eu.etaxonomy.cdm.model.common.IParsable#hasProblem(eu.etaxonomy.cdm.strategy.parser.ParserProblem)
888 */
889 public boolean hasProblem(ParserProblem problem) {
890 return getParsingProblems().contains(problem);
891 }
892
893
894 /* (non-Javadoc)
895 * @see eu.etaxonomy.cdm.model.common.IParsable#problemStarts()
896 */
897 public int getProblemStarts(){
898 return this.problemStarts;
899 }
900
901 /* (non-Javadoc)
902 * @see eu.etaxonomy.cdm.model.common.IParsable#setProblemStarts(int)
903 */
904 public void setProblemStarts(int start) {
905 this.problemStarts = start;
906 }
907
908 /* (non-Javadoc)
909 * @see eu.etaxonomy.cdm.model.common.IParsable#problemEnds()
910 */
911 public int getProblemEnds(){
912 return this.problemEnds;
913 }
914
915 /* (non-Javadoc)
916 * @see eu.etaxonomy.cdm.model.common.IParsable#setProblemEnds(int)
917 */
918 public void setProblemEnds(int end) {
919 this.problemEnds = end;
920 }
921
922 //*********************** TYPE DESIGNATION *********************************************//
923
924 /**
925 * Returns the set of {@link TypeDesignationBase type designations} assigned
926 * to <i>this</i> taxon name.
927 * @see NameTypeDesignation
928 * @see SpecimenTypeDesignation
929 */
930 public Set<TypeDesignationBase> getTypeDesignations() {
931 if(typeDesignations == null) {
932 this.typeDesignations = new HashSet<TypeDesignationBase>();
933 }
934 return typeDesignations;
935 }
936
937 /**
938 * Removes one element from the set of {@link TypeDesignationBase type designations} assigned to
939 * <i>this</i> taxon name. The type designation itself will be nullified.
940 *
941 * @param typeDesignation the type designation which should be deleted
942 */
943 @SuppressWarnings("deprecation")
944 public void removeTypeDesignation(TypeDesignationBase typeDesignation) {
945 this.typeDesignations.remove(typeDesignation);
946 typeDesignation.removeTypifiedName(this);
947 }
948
949 /**
950 * Returns the set of {@link SpecimenTypeDesignation specimen type designations} assigned
951 * to <i>this</i> taxon name. The {@link Rank rank} of <i>this</i> taxon name is generally
952 * "species" or below. The specimen type designations include all the
953 * specimens on which the typification of this name is based (which are
954 * exclusively used to typify taxon names belonging to the same
955 * {@link HomotypicalGroup homotypical group} to which <i>this</i> taxon name
956 * belongs) and eventually the status of these designations.
957 *
958 * @see SpecimenTypeDesignation
959 * @see NameTypeDesignation
960 * @see HomotypicalGroup
961 */
962 @Transient
963 public Set<SpecimenTypeDesignation> getSpecimenTypeDesignationsOfHomotypicalGroup() {
964 return this.getHomotypicalGroup().getSpecimenTypeDesignations();
965 }
966
967 //*********************** NAME TYPE DESIGNATION *********************************************//
968
969 /**
970 * Returns the set of {@link NameTypeDesignation name type designations} assigned
971 * to <i>this</i> taxon name the rank of which must be above "species".
972 * The name type designations include all the taxon names used to typify
973 * <i>this</i> taxon name and eventually the rejected or conserved status
974 * of these designations.
975 *
976 * @see NameTypeDesignation
977 * @see SpecimenTypeDesignation
978 */
979 @Transient
980 public Set<NameTypeDesignation> getNameTypeDesignations() {
981 Set<NameTypeDesignation> result = new HashSet<NameTypeDesignation>();
982 for (TypeDesignationBase typeDesignation : this.typeDesignations){
983 if (typeDesignation instanceof NameTypeDesignation){
984 result.add((NameTypeDesignation)typeDesignation);
985 }
986 }
987 return result;
988 }
989
990 /**
991 * Creates and adds a new {@link NameTypeDesignation name type designation}
992 * to <i>this</i> taxon name's set of type designations.
993 *
994 * @param typeSpecies the taxon name to be used as type of <i>this</i> taxon name
995 * @param citation the reference for this new designation
996 * @param citationMicroReference the string with the details (generally pages) within the reference
997 * @param originalNameString the taxon name string used in the reference to assert this designation
998 * @param isRejectedType the boolean status for a rejected name type designation
999 * @param isConservedType the boolean status for a conserved name type designation
1000 * @param isLectoType the boolean status for a lectotype name type designation
1001 * @param isNotDesignated the boolean status for a name type designation without name type
1002 * @param addToAllHomotypicNames the boolean indicating whether the name type designation should be
1003 * added to all taxon names of the homotypical group this taxon name belongs to
1004 * @return
1005 * @see #getNameTypeDesignations()
1006 * @see NameTypeDesignation
1007 * @see TypeDesignationBase#isNotDesignated()
1008 */
1009 public NameTypeDesignation addNameTypeDesignation(TaxonNameBase typeSpecies,
1010 Reference citation,
1011 String citationMicroReference,
1012 String originalNameString,
1013 NameTypeDesignationStatus status,
1014 boolean isRejectedType,
1015 boolean isConservedType,
1016 /*boolean isLectoType, */
1017 boolean isNotDesignated,
1018 boolean addToAllHomotypicNames) {
1019 NameTypeDesignation nameTypeDesignation = new NameTypeDesignation(typeSpecies, citation, citationMicroReference, originalNameString, status, isRejectedType, isConservedType, isNotDesignated);
1020 //nameTypeDesignation.setLectoType(isLectoType);
1021 addTypeDesignation(nameTypeDesignation, addToAllHomotypicNames);
1022 return nameTypeDesignation;
1023 }
1024
1025 /**
1026 * Creates and adds a new {@link NameTypeDesignation name type designation}
1027 * to <i>this</i> taxon name's set of type designations.
1028 *
1029 * @param typeSpecies the taxon name to be used as type of <i>this</i> taxon name
1030 * @param citation the reference for this new designation
1031 * @param citationMicroReference the string with the details (generally pages) within the reference
1032 * @param originalNameString the taxon name string used in the reference to assert this designation
1033 * @param status the name type designation status
1034 * @param addToAllHomotypicNames the boolean indicating whether the name type designation should be
1035 * added to all taxon names of the homotypical group this taxon name belongs to
1036 * @return
1037 * @see #getNameTypeDesignations()
1038 * @see NameTypeDesignation
1039 * @see TypeDesignationBase#isNotDesignated()
1040 */
1041 public NameTypeDesignation addNameTypeDesignation(TaxonNameBase typeSpecies,
1042 Reference citation,
1043 String citationMicroReference,
1044 String originalNameString,
1045 NameTypeDesignationStatus status,
1046 boolean addToAllHomotypicNames) {
1047 NameTypeDesignation nameTypeDesignation = new NameTypeDesignation(typeSpecies, status, citation, citationMicroReference, originalNameString);
1048 addTypeDesignation(nameTypeDesignation, addToAllHomotypicNames);
1049 return nameTypeDesignation;
1050 }
1051
1052 //*********************** SPECIMEN TYPE DESIGNATION *********************************************//
1053
1054 /**
1055 * Returns the set of {@link SpecimenTypeDesignation specimen type designations}
1056 * that typify <i>this</i> taxon name.
1057 */
1058 @Transient
1059 public Set<SpecimenTypeDesignation> getSpecimenTypeDesignations() {
1060 Set<SpecimenTypeDesignation> result = new HashSet<SpecimenTypeDesignation>();
1061 for (TypeDesignationBase typeDesignation : this.typeDesignations){
1062 if (typeDesignation instanceof SpecimenTypeDesignation){
1063 result.add((SpecimenTypeDesignation)typeDesignation);
1064 }
1065 }
1066 return result;
1067 }
1068
1069
1070 /**
1071 * Creates and adds a new {@link SpecimenTypeDesignation specimen type designation}
1072 * to <i>this</i> taxon name's set of type designations.
1073 *
1074 * @param typeSpecimen the specimen to be used as a type for <i>this</i> taxon name
1075 * @param status the specimen type designation status
1076 * @param citation the reference for this new specimen type designation
1077 * @param citationMicroReference the string with the details (generally pages) within the reference
1078 * @param originalNameString the taxon name used in the reference to assert this designation
1079 * @param isNotDesignated the boolean status for a specimen type designation without specimen type
1080 * @param addToAllHomotypicNames the boolean indicating whether the specimen type designation should be
1081 * added to all taxon names of the homotypical group the typified
1082 * taxon name belongs to
1083 * @return
1084 * @see #getSpecimenTypeDesignations()
1085 * @see SpecimenTypeDesignationStatus
1086 * @see SpecimenTypeDesignation
1087 * @see TypeDesignationBase#isNotDesignated()
1088 */
1089 public SpecimenTypeDesignation addSpecimenTypeDesignation(Specimen typeSpecimen,
1090 SpecimenTypeDesignationStatus status,
1091 Reference citation,
1092 String citationMicroReference,
1093 String originalNameString,
1094 boolean isNotDesignated,
1095 boolean addToAllHomotypicNames) {
1096 SpecimenTypeDesignation specimenTypeDesignation = new SpecimenTypeDesignation(typeSpecimen, status, citation, citationMicroReference, originalNameString, isNotDesignated);
1097 addTypeDesignation(specimenTypeDesignation, addToAllHomotypicNames);
1098 return specimenTypeDesignation;
1099 }
1100
1101 //used by merge strategy
1102 private boolean addTypeDesignation(TypeDesignationBase typeDesignation){
1103 return addTypeDesignation(typeDesignation, true);
1104 }
1105
1106 /**
1107 * Adds a {@link TypeDesignationBase type designation} to <code>this</code> taxon name's set of type designations
1108 *
1109 * @param typeDesignation the typeDesignation to be added to <code>this</code> taxon name
1110 * @param addToAllNames the boolean indicating whether the type designation should be
1111 * added to all taxon names of the homotypical group the typified
1112 * taxon name belongs to
1113 * @return true if the operation was succesful
1114 *
1115 * @throws IllegalArgumentException if the type designation already has typified names, an {@link IllegalArgumentException exception}
1116 * is thrown. We do this to prevent a type designation to be used for multiple taxon names.
1117 *
1118 */
1119 public boolean addTypeDesignation(TypeDesignationBase typeDesignation, boolean addToAllNames){
1120 //currently typeDesignations are not persisted with the homotypical group
1121 //so explicit adding to the homotypical group is not necessary.
1122 if (typeDesignation != null){
1123 checkHomotypicalGroup(typeDesignation);
1124 this.typeDesignations.add(typeDesignation);
1125 typeDesignation.addTypifiedName(this);
1126
1127 if (addToAllNames){
1128 for (TaxonNameBase taxonName : this.getHomotypicalGroup().getTypifiedNames()){
1129 if (taxonName != this){
1130 taxonName.addTypeDesignation(typeDesignation, false);
1131 }
1132 }
1133 }
1134 }
1135 return true;
1136 }
1137
1138 /**
1139 * Throws an Exception this type designation already has typified names from another homotypical group.
1140 * @param typeDesignation
1141 */
1142 private void checkHomotypicalGroup(TypeDesignationBase typeDesignation) {
1143 if(typeDesignation.getTypifiedNames().size() > 0){
1144 Set<HomotypicalGroup> groups = new HashSet<HomotypicalGroup>();
1145 Set<TaxonNameBase> names = typeDesignation.getTypifiedNames();
1146 for (TaxonNameBase taxonName: names){
1147 groups.add(taxonName.getHomotypicalGroup());
1148 }
1149 if (groups.size() > 1){
1150 throw new IllegalArgumentException("TypeDesignation already has typified names from another homotypical group.");
1151 }
1152 }
1153 }
1154
1155
1156
1157 //*********************** HOMOTYPICAL GROUP *********************************************//
1158
1159
1160 /**
1161 * Returns the {@link HomotypicalGroup homotypical group} to which
1162 * <i>this</i> taxon name belongs. A homotypical group represents all taxon names
1163 * that share the same types.
1164 *
1165 * @see HomotypicalGroup
1166 */
1167
1168 public HomotypicalGroup getHomotypicalGroup() {
1169 return homotypicalGroup;
1170 }
1171
1172 /*
1173 * @see #getHomotypicalGroup()
1174 */
1175 public void setHomotypicalGroup(HomotypicalGroup homotypicalGroup) {
1176 if (homotypicalGroup == null){
1177 throw new IllegalArgumentException("HomotypicalGroup of name should never be null but was set to 'null'");
1178 }
1179 this.homotypicalGroup = homotypicalGroup;
1180 if (! homotypicalGroup.typifiedNames.contains(this)){
1181 homotypicalGroup.addTypifiedName(this);
1182 }
1183 }
1184
1185
1186
1187 // *************************************************************************//
1188
1189 /**
1190 * @see #getNomenclaturalReference()
1191 */
1192 @Transient
1193 public Reference getCitation(){
1194 //TODO What is the purpose of this method differing from the getNomenclaturalReference method?
1195 logger.warn("getCitation not yet implemented");
1196 return null;
1197 }
1198
1199 /**
1200 * Returns the complete string containing the
1201 * {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation() nomenclatural reference citation}
1202 * and the {@link #getNomenclaturalMicroReference() details} assigned to <i>this</i> taxon name.
1203 *
1204 * @return the string containing the nomenclatural reference of <i>this</i> taxon name
1205 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation()
1206 * @see #getNomenclaturalReference()
1207 * @see #getNomenclaturalMicroReference()
1208 */
1209 @Transient
1210 public String getCitationString(){
1211 return getNomenclaturalReference().getNomenclaturalCitation(getNomenclaturalMicroReference());
1212 }
1213
1214 /**
1215 * Returns the parsing problems
1216 * @return
1217 */
1218 public List<ParserProblem> getParsingProblems(){
1219 return ParserProblem.warningList(this.parsingProblem);
1220 }
1221
1222 /**
1223 * Returns the string containing the publication date (generally only year)
1224 * of the {@link #getNomenclaturalReference() nomenclatural reference} for <i>this</i> taxon name, null if there is
1225 * no nomenclatural reference.
1226 *
1227 * @return the string containing the publication date of <i>this</i> taxon name
1228 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getYear()
1229 */
1230 @Transient
1231 public String getReferenceYear(){
1232 if (this.getNomenclaturalReference() != null ){
1233 return this.getNomenclaturalReference().getYear();
1234 }else{
1235 return null;
1236 }
1237 }
1238
1239 /**
1240 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1241 * In this context a taxon base means the use of a taxon name by a reference
1242 * either as a {@link eu.etaxonomy.cdm.model.taxon.Taxon taxon} ("accepted/correct" name) or
1243 * as a (junior) {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
1244 * A taxon name can be used by several distinct {@link eu.etaxonomy.cdm.model.reference.Reference references} but only once
1245 * within a taxonomic treatment (identified by one reference).
1246 *
1247 * @see #getTaxa()
1248 * @see #getSynonyms()
1249 */
1250 public Set<TaxonBase> getTaxonBases() {
1251 if(taxonBases == null) {
1252 this.taxonBases = new HashSet<TaxonBase>();
1253 }
1254 return this.taxonBases;
1255 }
1256
1257 /**
1258 * Adds a new {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon base}
1259 * to the set of taxon bases using <i>this</i> taxon name.
1260 *
1261 * @param taxonBase the taxon base to be added
1262 * @see #getTaxonBases()
1263 * @see #removeTaxonBase(TaxonBase)
1264 */
1265 //TODO protected
1266 public void addTaxonBase(TaxonBase taxonBase){
1267 Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonNameBase.class});
1268 ReflectionUtils.makeAccessible(method);
1269 ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {this});
1270 taxonBases.add(taxonBase);
1271 }
1272 /**
1273 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1274 *
1275 * @param taxonBase the taxon base which should be removed from the corresponding set
1276 * @see #getTaxonBases()
1277 * @see #addTaxonBase(TaxonBase)
1278 */
1279 public void removeTaxonBase(TaxonBase taxonBase){
1280 Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonNameBase.class});
1281 ReflectionUtils.makeAccessible(method);
1282 ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {null});
1283 taxonBases.remove(taxonBase);
1284 }
1285
1286 /**
1287 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Taxon taxa} ("accepted/correct" names according to any
1288 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1289 * the set returned by getTaxonBases().
1290 *
1291 * @see eu.etaxonomy.cdm.model.taxon.Taxon
1292 * @see #getTaxonBases()
1293 * @see #getSynonyms()
1294 */
1295 @Transient
1296 public Set<Taxon> getTaxa(){
1297 Set<Taxon> result = new HashSet<Taxon>();
1298 for (TaxonBase taxonBase : this.taxonBases){
1299 if (taxonBase instanceof Taxon){
1300 result.add((Taxon)taxonBase);
1301 }
1302 }
1303 return result;
1304 }
1305
1306 /**
1307 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Synonym (junior) synonyms} (according to any
1308 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1309 * the set returned by getTaxonBases().
1310 *
1311 * @see eu.etaxonomy.cdm.model.taxon.Synonym
1312 * @see #getTaxonBases()
1313 * @see #getTaxa()
1314 */
1315 @Transient
1316 public Set<Synonym> getSynonyms() {
1317 Set<Synonym> result = new HashSet<Synonym>();
1318 for (TaxonBase taxonBase : this.taxonBases){
1319 if (taxonBase instanceof Synonym){
1320 result.add((Synonym)taxonBase);
1321 }
1322 }
1323 return result;
1324 }
1325
1326
1327 // *********** DESCRIPTIONS *************************************
1328
1329 /**
1330 * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1331 * to <i>this</i> taxon name. A taxon name description is a piece of information
1332 * concerning the taxon name like for instance the content of its first
1333 * publication (protolog) or a picture of this publication.
1334 *
1335 * @see #addDescription(TaxonNameDescription)
1336 * @see #removeDescription(TaxonNameDescription)
1337 * @see eu.etaxonomy.cdm.model.description.TaxonNameDescription
1338 */
1339 public Set<TaxonNameDescription> getDescriptions() {
1340 return descriptions;
1341 }
1342
1343 /**
1344 * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name description}
1345 * to the set of taxon name descriptions assigned to <i>this</i> taxon name. The
1346 * content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute} of the
1347 * taxon name description itself will be replaced with <i>this</i> taxon name.
1348 *
1349 * @param description the taxon name description to be added
1350 * @see #getDescriptions()
1351 * @see #removeDescription(TaxonNameDescription)
1352 */
1353 public void addDescription(TaxonNameDescription description) {
1354 java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonNameBase.class);
1355 ReflectionUtils.makeAccessible(field);
1356 ReflectionUtils.setField(field, description, this);
1357 descriptions.add(description);
1358 }
1359 /**
1360 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1361 * to <i>this</i> taxon name. The content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute}
1362 * of the description itself will be set to "null".
1363 *
1364 * @param description the taxon name description which should be removed
1365 * @see #getDescriptions()
1366 * @see #addDescription(TaxonNameDescription)
1367 * @see eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName()
1368 */
1369 public void removeDescription(TaxonNameDescription description) {
1370 java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonNameBase.class);
1371 ReflectionUtils.makeAccessible(field);
1372 ReflectionUtils.setField(field, description, null);
1373 descriptions.remove(description);
1374 }
1375
1376 // *********** HOMOTYPIC GROUP METHODS **************************************************
1377
1378 @Transient
1379 public void mergeHomotypicGroups(TaxonNameBase name){
1380 this.getHomotypicalGroup().merge(name.getHomotypicalGroup());
1381 //HomotypicalGroup thatGroup = name.homotypicalGroup;
1382 name.setHomotypicalGroup(this.homotypicalGroup);
1383 }
1384
1385 /**
1386 * Returns the boolean value indicating whether a given taxon name belongs
1387 * to the same {@link HomotypicalGroup homotypical group} as <i>this</i> taxon name (true)
1388 * or not (false). Returns "true" only if the homotypical groups of both
1389 * taxon names exist and if they are identical.
1390 *
1391 * @param homoTypicName the taxon name the homotypical group of which is to be checked
1392 * @return the boolean value of the check
1393 * @see HomotypicalGroup
1394 */
1395 @Transient
1396 public boolean isHomotypic(TaxonNameBase homoTypicName) {
1397 if (homoTypicName == null) {
1398 return false;
1399 }
1400 HomotypicalGroup homotypicGroup = homoTypicName.getHomotypicalGroup();
1401 if (homotypicGroup == null || this.getHomotypicalGroup() == null) {
1402 return false;
1403 }
1404 if (homotypicGroup.equals(this.getHomotypicalGroup())) {
1405 return true;
1406 }
1407 return false;
1408 }
1409
1410
1411 /**
1412 * Checks whether name is a basionym for ALL names
1413 * in its homotypical group.
1414 * Returns <code>false</code> if there are no other names in the group
1415 * @param name
1416 * @return
1417 */
1418 @Transient
1419 public boolean isGroupsBasionym() {
1420 Set<TaxonNameBase> typifiedNames = homotypicalGroup.getTypifiedNames();
1421
1422 // Check whether there are any other names in the group
1423 if (typifiedNames.size() == 1) {
1424 return false;
1425 }
1426
1427 boolean isBasionymToAll = true;
1428
1429 for (TaxonNameBase taxonName : typifiedNames) {
1430 if (!taxonName.equals(this)) {
1431 if (! isBasionymFor(taxonName)) {
1432 return false;
1433 }
1434 }
1435 }
1436 return true;
1437 }
1438
1439 /**
1440 * Checks whether a basionym relationship exists between fromName and toName.
1441 *
1442 * @param fromName
1443 * @param toName
1444 * @return
1445 */
1446 @Transient
1447 public boolean isBasionymFor(TaxonNameBase newCombinationName) {
1448 Set<NameRelationship> relations = newCombinationName.getRelationsToThisName();
1449 for (NameRelationship relation : relations) {
1450 if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1451 relation.getFromName().equals(this)) {
1452 return true;
1453 }
1454 }
1455 return false;
1456 }
1457
1458 /**
1459 * Creates a basionym relationship to all other names in this names homotypical
1460 * group.
1461 *
1462 * @see HomotypicalGroup.setGroupBasionym(TaxonNameBase basionymName)
1463
1464 */
1465 /* (non-Javadoc)
1466 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup#setGroupBasionym(TaxonNameBase)
1467 */
1468 @Transient
1469 public void makeGroupsBasionym() {
1470 this.homotypicalGroup.setGroupBasionym(this);
1471 }
1472
1473
1474 //********* Rank comparison shortcuts ********************//
1475 /**
1476 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1477 * taxon name is higher than the genus rank (true) or not (false).
1478 * Suprageneric non viral names are monomials.
1479 * Returns false if rank is null.
1480 *
1481 * @see #isGenus()
1482 * @see #isInfraGeneric()
1483 * @see #isSpecies()
1484 * @see #isInfraSpecific()
1485 */
1486 @Transient
1487 public boolean isSupraGeneric() {
1488 if (rank == null){
1489 return false;
1490 }
1491 return getRank().isSupraGeneric();
1492 }
1493 /**
1494 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1495 * taxon name is the genus rank (true) or not (false). Non viral names with
1496 * genus rank are monomials. Returns false if rank is null.
1497 *
1498 * @see #isSupraGeneric()
1499 * @see #isInfraGeneric()
1500 * @see #isSpecies()
1501 * @see #isInfraSpecific()
1502 */
1503 @Transient
1504 public boolean isGenus() {
1505 if (rank == null){
1506 return false;
1507 }
1508 return getRank().isGenus();
1509 }
1510 /**
1511 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1512 * taxon name is higher than the species rank and lower than the
1513 * genus rank (true) or not (false). Infrageneric non viral names are
1514 * binomials. Returns false if rank is null.
1515 *
1516 * @see #isSupraGeneric()
1517 * @see #isGenus()
1518 * @see #isSpecies()
1519 * @see #isInfraSpecific()
1520 */
1521 @Transient
1522 public boolean isInfraGeneric() {
1523 if (rank == null){
1524 return false;
1525 }
1526 return getRank().isInfraGeneric();
1527 }
1528
1529 /**
1530 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1531 * taxon name is higher than the species rank (true) or not (false).
1532 * Returns false if rank is null.
1533 *
1534 * @see #isGenus()
1535 * @see #isInfraGeneric()
1536 * @see #isSpecies()
1537 * @see #isInfraSpecific()
1538 */
1539 @Transient
1540 public boolean isSupraSpecific(){
1541 if (rank == null) {
1542 return false;
1543 }
1544 return getRank().isHigher(Rank.SPECIES());
1545 }
1546
1547 /**
1548 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1549 * taxon name is the species rank (true) or not (false). Non viral names
1550 * with species rank are binomials.
1551 * Returns false if rank is null.
1552 *
1553 * @see #isSupraGeneric()
1554 * @see #isGenus()
1555 * @see #isInfraGeneric()
1556 * @see #isInfraSpecific()
1557 */
1558 @Transient
1559 public boolean isSpecies() {
1560 if (rank == null){
1561 return false;
1562 }
1563 return getRank().isSpecies();
1564 }
1565 /**
1566 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1567 * taxon name is lower than the species rank (true) or not (false).
1568 * Infraspecific non viral names are trinomials.
1569 * Returns false if rank is null.
1570 *
1571 * @see #isSupraGeneric()
1572 * @see #isGenus()
1573 * @see #isInfraGeneric()
1574 * @see #isSpecies()
1575 */
1576 @Transient
1577 public boolean isInfraSpecific() {
1578 if (rank == null){
1579 return false;
1580 }
1581 return getRank().isInfraSpecific();
1582 }
1583
1584
1585 /**
1586 * Returns null as the {@link NomenclaturalCode nomenclatural code} that governs
1587 * the construction of <i>this</i> taxon name since there is no specific
1588 * nomenclatural code defined. The real implementention takes place in the
1589 * subclasses {@link ViralName ViralName}, {@link BacterialName BacterialName},
1590 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
1591 * {@link ZoologicalName ZoologicalName}. Each taxon name is governed by one
1592 * and only one nomenclatural code.
1593 *
1594 * @return null
1595 * @see #isCodeCompliant()
1596 * @see #getHasProblem()
1597 */
1598 abstract public NomenclaturalCode getNomenclaturalCode();
1599
1600 /* (non-Javadoc)
1601 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1602 */
1603 /**
1604 * Generates and returns the string with the scientific name of <i>this</i>
1605 * taxon name (only non viral taxon names can be generated from their
1606 * components). This string may be stored in the inherited
1607 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
1608 * This method overrides the generic and inherited
1609 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() method} from
1610 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
1611 *
1612 * @return the string with the composed name of this non viral taxon name with authorship (and maybe year)
1613 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1614 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
1615 */
1616 // @Override
1617 // public abstract String generateTitle();
1618
1619 /**
1620 * Creates a basionym relationship between this name and
1621 * each name in its homotypic group.
1622 *
1623 * @param basionymName
1624 */
1625 @Transient
1626 public void setAsGroupsBasionym() {
1627
1628
1629 HomotypicalGroup homotypicalGroup = this.getHomotypicalGroup();
1630
1631 if (homotypicalGroup == null) {
1632 return;
1633 }
1634
1635 Set<NameRelationship> relations = new HashSet<NameRelationship>();
1636 Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
1637
1638 for(TaxonNameBase<?, ?> typifiedName : homotypicalGroup.getTypifiedNames()){
1639
1640 Set<NameRelationship> nameRelations = typifiedName.getRelationsFromThisName();
1641
1642 for(NameRelationship nameRelation : nameRelations){
1643 relations.add(nameRelation);
1644 }
1645 }
1646
1647 for (NameRelationship relation : relations) {
1648
1649 // If this is a basionym relation, and toName is in the homotypical group,
1650 // remove the relationship.
1651 if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1652 relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
1653 removeRelations.add(relation);
1654 }
1655 }
1656
1657 // Removing relations from a set through which we are iterating causes a
1658 // ConcurrentModificationException. Therefore, we delete the targeted
1659 // relations in a second step.
1660 for (NameRelationship relation : removeRelations) {
1661 this.removeNameRelationship(relation);
1662 }
1663
1664
1665 for (TaxonNameBase<?, ?> name : homotypicalGroup.getTypifiedNames()) {
1666 if (!name.equals(this)) {
1667
1668 // First check whether the relationship already exists
1669 if (!this.isBasionymFor(name)) {
1670
1671 // Then create it
1672 name.addRelationshipFromName(this,
1673 NameRelationshipType.BASIONYM(), null);
1674 }
1675 }
1676 }
1677 }
1678
1679 /**
1680 * Removes basionym relationship between this name and
1681 * each name in its homotypic group.
1682 *
1683 * @param basionymName
1684 */
1685 @Transient
1686 public void removeAsGroupsBasionym() {
1687
1688 HomotypicalGroup homotypicalGroup = this.getHomotypicalGroup();
1689
1690 if (homotypicalGroup == null) {
1691 return;
1692 }
1693
1694 Set<NameRelationship> relations = new HashSet<NameRelationship>();
1695 Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
1696
1697 for(TaxonNameBase<?, ?> typifiedName : homotypicalGroup.getTypifiedNames()){
1698
1699 Set<NameRelationship> nameRelations = typifiedName.getRelationsFromThisName();
1700
1701 for(NameRelationship nameRelation : nameRelations){
1702 relations.add(nameRelation);
1703 }
1704 }
1705
1706 for (NameRelationship relation : relations) {
1707
1708 // If this is a basionym relation, and toName is in the homotypical group,
1709 // and fromName is basionymName, remove the relationship.
1710 if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1711 relation.getFromName().equals(this) &&
1712 relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
1713 removeRelations.add(relation);
1714 }
1715 }
1716
1717 // Removing relations from a set through which we are iterating causes a
1718 // ConcurrentModificationException. Therefore, we delete the targeted
1719 // relations in a second step.
1720 for (NameRelationship relation : removeRelations) {
1721 this.removeNameRelationship(relation);
1722 }
1723 }
1724
1725 //*********************** CLONE ********************************************************/
1726
1727 /**
1728 * Clones <i>this</i> taxon name. This is a shortcut that enables to create
1729 * a new instance that differs only slightly from <i>this</i> taxon name by
1730 * modifying only some of the attributes.<BR><BR>
1731 * Usages of this name in a taxon concept are <b>not</b> cloned.<BR>
1732 * <b>The name gets a newly created homotypical group</b><BR>
1733 * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1734 * {@link TaxonNameDescription Name descriptions} are cloned and not reused.<BR>
1735 * {@link TypeDesignationBase Type designations} are cloned and not reused.<BR>
1736 *
1737 * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1738 * @see java.lang.Object#clone()
1739 */
1740 @Override
1741 public Object clone() {
1742 TaxonNameBase result;
1743 try {
1744 result = (TaxonNameBase)super.clone();
1745
1746 //taxonBases -> empty
1747 result.taxonBases = new HashSet<TaxonBase>();
1748
1749 //empty caches
1750 if (! protectedFullTitleCache){
1751 result.fullTitleCache = null;
1752 }
1753
1754 //descriptions
1755 result.descriptions = new HashSet<TaxonNameDescription>();
1756 for (TaxonNameDescription taxonNameDescription : getDescriptions()){
1757 TaxonNameDescription newDescription = (TaxonNameDescription)taxonNameDescription.clone();
1758 result.descriptions.add(newDescription);
1759 }
1760
1761 //status
1762 result.status = new HashSet<NomenclaturalStatus>();
1763 for (NomenclaturalStatus nomenclaturalStatus : getStatus()){
1764 NomenclaturalStatus newStatus = (NomenclaturalStatus)nomenclaturalStatus.clone();
1765 result.status.add(newStatus);
1766 }
1767
1768
1769 //To Relations
1770 result.relationsToThisName = new HashSet<NameRelationship>();
1771 for (NameRelationship toRelationship : getRelationsToThisName()){
1772 NameRelationship newRelationship = (NameRelationship)toRelationship.clone();
1773 newRelationship.setRelatedTo(result);
1774 result.relationsToThisName.add(newRelationship);
1775 }
1776
1777 //From Relations
1778 result.relationsFromThisName = new HashSet<NameRelationship>();
1779 for (NameRelationship fromRelationship : getRelationsFromThisName()){
1780 NameRelationship newRelationship = (NameRelationship)fromRelationship.clone();
1781 newRelationship.setRelatedFrom(result);
1782 result.relationsFromThisName.add(newRelationship);
1783 }
1784
1785 //type designations
1786 result.typeDesignations = new HashSet<TypeDesignationBase>();
1787 for (TypeDesignationBase typeDesignation : getTypeDesignations()){
1788 TypeDesignationBase newDesignation = (TypeDesignationBase)typeDesignation.clone();
1789 result.typeDesignations.add(newDesignation);
1790 newDesignation.addTypifiedName(result);
1791 }
1792
1793 //homotypicalGroup
1794 //TODO still needs to be discussed
1795 homotypicalGroup = HomotypicalGroup.NewInstance();
1796 homotypicalGroup.addTypifiedName(this);
1797
1798 //no changes to: appendedPharse, nomenclaturalReference,
1799 //nomenclaturalMicroReference, parsingProblem, problemEnds, problemStarts
1800 //protectedFullTitleCache, rank
1801 return result;
1802 } catch (CloneNotSupportedException e) {
1803 logger.warn("Object does not implement cloneable");
1804 e.printStackTrace();
1805 return null;
1806 }
1807
1808 }
1809 }