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