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