minor changes
[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.Specimen;
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 import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
77
78 /**
79 * The upmost (abstract) class for scientific taxon names regardless of any
80 * particular {@link NomenclaturalCode nomenclature code}. The scientific taxon name does not depend
81 * on the use made of it in a publication or a treatment
82 * ({@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon concept respectively potential taxon})
83 * as an {@link eu.etaxonomy.cdm.model.taxon.Taxon "accepted" respectively "correct" (taxon) name}
84 * or as a {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
85 * <P>
86 * This class corresponds partially to: <ul>
87 * <li> TaxonName according to the TDWG ontology
88 * <li> ScientificName and CanonicalName according to the TCS
89 * <li> ScientificName according to the ABCD schema
90 * </ul>
91 *
92 * @author m.doering
93 * @version 1.0
94 * @created 08-Nov-2007 13:06:57
95 */
96 @XmlAccessorType(XmlAccessType.FIELD)
97 @XmlType(name = "TaxonNameBase", propOrder = {
98 "appendedPhrase",
99 "nomenclaturalMicroReference",
100 "nomenclaturalReference",
101 "rank",
102 "fullTitleCache",
103 "protectedFullTitleCache",
104 "homotypicalGroup",
105 "typeDesignations",
106 "relationsFromThisName",
107 "relationsToThisName",
108 "status",
109 "descriptions",
110 "taxonBases"
111 })
112 @XmlRootElement(name = "TaxonNameBase")
113 @Entity
114 @Audited
115 @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
116 @Table(appliesTo="TaxonNameBase", indexes = { @Index(name = "taxonNameBaseTitleCacheIndex", columnNames = { "titleCache" }), @Index(name = "taxonNameBaseNameCacheIndex", columnNames = { "nameCache" }) })
117 public abstract class TaxonNameBase<T extends TaxonNameBase<?,?>, S extends INameCacheStrategy> extends IdentifiableEntity<S> implements IReferencedEntity, IParsable, IRelated, IMatchable, Cloneable {
118 private static final long serialVersionUID = -4530368639601532116L;
119 private static final Logger logger = Logger.getLogger(TaxonNameBase.class);
120
121 @XmlElement(name = "FullTitleCache")
122 @Column(length=330, name="fullTitleCache")
123 @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
124 @CacheUpdate(noUpdate ="titleCache")
125 @NotEmpty(groups = Level2.class)
126 @Size(max = 330)
127 protected String fullTitleCache;
128
129 //if true titleCache will not be automatically generated/updated
130 @XmlElement(name = "ProtectedFullTitleCache")
131 @CacheUpdate(value ="fullTitleCache", noUpdate ="titleCache")
132 private boolean protectedFullTitleCache;
133
134 @XmlElementWrapper(name = "Descriptions")
135 @XmlElement(name = "Description")
136 @OneToMany(mappedBy="taxonName", fetch= FetchType.LAZY, orphanRemoval=true)
137 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
138 @NotNull
139 private Set<TaxonNameDescription> descriptions = new HashSet<TaxonNameDescription>();
140
141 @XmlElement(name = "AppendedPhrase")
142 @Field
143 @CacheUpdate(value ="nameCache")
144 //TODO Val #3379
145 // @NullOrNotEmpty
146 @Size(max = 255)
147 private String appendedPhrase;
148
149 @XmlElement(name = "NomenclaturalMicroReference")
150 @Field
151 @CacheUpdate(noUpdate ="titleCache")
152 //TODO Val #3379
153 // @NullOrNotEmpty
154 @Size(max = 255)
155 private String nomenclaturalMicroReference;
156
157 @XmlAttribute
158 @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
159 private int parsingProblem = 0;
160
161 @XmlAttribute
162 @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
163 private int problemStarts = -1;
164
165 @XmlAttribute
166 @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
167 private int problemEnds = -1;
168
169 @XmlElementWrapper(name = "TypeDesignations")
170 @XmlElement(name = "TypeDesignation")
171 @XmlIDREF
172 @XmlSchemaType(name = "IDREF")
173 @ManyToMany(fetch = FetchType.LAZY)
174 @JoinTable(
175 name="TaxonNameBase_TypeDesignationBase",
176 joinColumns=@javax.persistence.JoinColumn(name="TaxonNameBase_id"),
177 inverseJoinColumns=@javax.persistence.JoinColumn(name="typedesignations_id")
178 )
179 @Cascade({CascadeType.SAVE_UPDATE})
180 @NotNull
181 private Set<TypeDesignationBase> typeDesignations = new HashSet<TypeDesignationBase>();
182
183 @XmlElement(name = "HomotypicalGroup")
184 @XmlIDREF
185 @XmlSchemaType(name = "IDREF")
186 @ManyToOne(fetch = FetchType.LAZY)
187 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
188 @Match(MatchMode.IGNORE)
189 @CacheUpdate(noUpdate ="titleCache")
190 //TODO Val #3379
191 // @NotNull
192 private HomotypicalGroup homotypicalGroup;
193
194 @XmlElementWrapper(name = "RelationsFromThisName")
195 @XmlElement(name = "RelationFromThisName")
196 @OneToMany(mappedBy="relatedFrom", fetch= FetchType.LAZY, orphanRemoval=true)
197 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
198 @Merge(MergeMode.RELATION)
199 @NotNull
200 @Valid
201 private Set<NameRelationship> relationsFromThisName = new HashSet<NameRelationship>();
202
203 @XmlElementWrapper(name = "RelationsToThisName")
204 @XmlElement(name = "RelationToThisName")
205 @XmlIDREF
206 @XmlSchemaType(name = "IDREF")
207 @OneToMany(mappedBy="relatedTo", fetch= FetchType.LAZY, orphanRemoval=true)
208 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
209 @Merge(MergeMode.RELATION)
210 @NotNull
211 @Valid
212 private Set<NameRelationship> relationsToThisName = new HashSet<NameRelationship>();
213
214 @XmlElementWrapper(name = "NomenclaturalStatuses")
215 @XmlElement(name = "NomenclaturalStatus")
216 @OneToMany(fetch= FetchType.LAZY, orphanRemoval=true)
217 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE,CascadeType.DELETE})
218 @NotNull
219 @IndexedEmbedded(depth=1)
220 private Set<NomenclaturalStatus> status = new HashSet<NomenclaturalStatus>();
221
222 @XmlElementWrapper(name = "TaxonBases")
223 @XmlElement(name = "TaxonBase")
224 @XmlIDREF
225 @XmlSchemaType(name = "IDREF")
226 @OneToMany(mappedBy="name", fetch= FetchType.LAZY)
227 @NotNull
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(Specimen 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 * @see #getNomenclaturalReference()
1247 */
1248 @Override
1249 @Transient
1250 public Reference getCitation(){
1251 //TODO What is the purpose of this method differing from the getNomenclaturalReference method?
1252 logger.warn("getCitation not yet implemented");
1253 return null;
1254 }
1255
1256 /**
1257 * Returns the complete string containing the
1258 * {@link eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation() nomenclatural reference citation}
1259 * and the {@link #getNomenclaturalMicroReference() details} assigned to <i>this</i> taxon name.
1260 *
1261 * @return the string containing the nomenclatural reference of <i>this</i> taxon name
1262 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getNomenclaturalCitation()
1263 * @see #getNomenclaturalReference()
1264 * @see #getNomenclaturalMicroReference()
1265 */
1266 @Transient
1267 public String getCitationString(){
1268 return getNomenclaturalReference().getNomenclaturalCitation(getNomenclaturalMicroReference());
1269 }
1270
1271 /**
1272 * Returns the parsing problems
1273 * @return
1274 */
1275 @Override
1276 public List<ParserProblem> getParsingProblems(){
1277 return ParserProblem.warningList(this.parsingProblem);
1278 }
1279
1280 /**
1281 * Returns the string containing the publication date (generally only year)
1282 * of the {@link #getNomenclaturalReference() nomenclatural reference} for <i>this</i> taxon name, null if there is
1283 * no nomenclatural reference.
1284 *
1285 * @return the string containing the publication date of <i>this</i> taxon name
1286 * @see eu.etaxonomy.cdm.model.reference.INomenclaturalReference#getYear()
1287 */
1288 @Transient
1289 public String getReferenceYear(){
1290 if (this.getNomenclaturalReference() != null ){
1291 return this.getNomenclaturalReference().getYear();
1292 }else{
1293 return null;
1294 }
1295 }
1296
1297 /**
1298 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1299 * In this context a taxon base means the use of a taxon name by a reference
1300 * either as a {@link eu.etaxonomy.cdm.model.taxon.Taxon taxon} ("accepted/correct" name) or
1301 * as a (junior) {@link eu.etaxonomy.cdm.model.taxon.Synonym synonym}.
1302 * A taxon name can be used by several distinct {@link eu.etaxonomy.cdm.model.reference.Reference references} but only once
1303 * within a taxonomic treatment (identified by one reference).
1304 *
1305 * @see #getTaxa()
1306 * @see #getSynonyms()
1307 */
1308 public Set<TaxonBase> getTaxonBases() {
1309 if(taxonBases == null) {
1310 this.taxonBases = new HashSet<TaxonBase>();
1311 }
1312 return this.taxonBases;
1313 }
1314
1315 /**
1316 * Adds a new {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon base}
1317 * to the set of taxon bases using <i>this</i> taxon name.
1318 *
1319 * @param taxonBase the taxon base to be added
1320 * @see #getTaxonBases()
1321 * @see #removeTaxonBase(TaxonBase)
1322 */
1323 //TODO protected
1324 public void addTaxonBase(TaxonBase taxonBase){
1325 Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonNameBase.class});
1326 ReflectionUtils.makeAccessible(method);
1327 ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {this});
1328 taxonBases.add(taxonBase);
1329 }
1330 /**
1331 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.taxon.TaxonBase taxon bases} that refer to <i>this</i> taxon name.
1332 *
1333 * @param taxonBase the taxon base which should be removed from the corresponding set
1334 * @see #getTaxonBases()
1335 * @see #addTaxonBase(TaxonBase)
1336 */
1337 public void removeTaxonBase(TaxonBase taxonBase){
1338 Method method = ReflectionUtils.findMethod(TaxonBase.class, "setName", new Class[] {TaxonNameBase.class});
1339 ReflectionUtils.makeAccessible(method);
1340 ReflectionUtils.invokeMethod(method, taxonBase, new Object[] {null});
1341 Boolean test = null;
1342
1343 if (taxonBases.contains(taxonBase)){
1344 test = taxonBases.remove(taxonBase);
1345 }
1346 if (test != null){
1347 if (!test && !taxonBases.isEmpty()){
1348 HashSet<TaxonBase> copyTaxonBase = new HashSet<TaxonBase>();
1349 Iterator<TaxonBase> iterator = taxonBases.iterator();
1350 while (iterator.hasNext()){
1351 TaxonBase taxonBaseTest = iterator.next();
1352 if (taxonBaseTest.equals(taxonBase)){
1353 test = taxonBases.remove(taxonBaseTest);
1354 }
1355
1356
1357 }
1358 }
1359 }
1360
1361
1362
1363
1364
1365
1366 }
1367
1368 /**
1369 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Taxon taxa} ("accepted/correct" names according to any
1370 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1371 * the set returned by getTaxonBases().
1372 *
1373 * @see eu.etaxonomy.cdm.model.taxon.Taxon
1374 * @see #getTaxonBases()
1375 * @see #getSynonyms()
1376 */
1377 @Transient
1378 public Set<Taxon> getTaxa(){
1379 Set<Taxon> result = new HashSet<Taxon>();
1380 for (TaxonBase taxonBase : this.taxonBases){
1381 if (taxonBase instanceof Taxon){
1382 result.add((Taxon)taxonBase);
1383 }
1384 }
1385 return result;
1386 }
1387
1388 /**
1389 * Returns the set of {@link eu.etaxonomy.cdm.model.taxon.Synonym (junior) synonyms} (according to any
1390 * reference) that are based on <i>this</i> taxon name. This set is a subset of
1391 * the set returned by getTaxonBases().
1392 *
1393 * @see eu.etaxonomy.cdm.model.taxon.Synonym
1394 * @see #getTaxonBases()
1395 * @see #getTaxa()
1396 */
1397 @Transient
1398 public Set<Synonym> getSynonyms() {
1399 Set<Synonym> result = new HashSet<Synonym>();
1400 for (TaxonBase taxonBase : this.taxonBases){
1401 if (taxonBase instanceof Synonym){
1402 result.add((Synonym)taxonBase);
1403 }
1404 }
1405 return result;
1406 }
1407
1408
1409 // *********** DESCRIPTIONS *************************************
1410
1411 /**
1412 * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1413 * to <i>this</i> taxon name. A taxon name description is a piece of information
1414 * concerning the taxon name like for instance the content of its first
1415 * publication (protolog) or a picture of this publication.
1416 *
1417 * @see #addDescription(TaxonNameDescription)
1418 * @see #removeDescription(TaxonNameDescription)
1419 * @see eu.etaxonomy.cdm.model.description.TaxonNameDescription
1420 */
1421 public Set<TaxonNameDescription> getDescriptions() {
1422 return descriptions;
1423 }
1424
1425 /**
1426 * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name description}
1427 * to the set of taxon name descriptions assigned to <i>this</i> taxon name. The
1428 * content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute} of the
1429 * taxon name description itself will be replaced with <i>this</i> taxon name.
1430 *
1431 * @param description the taxon name description to be added
1432 * @see #getDescriptions()
1433 * @see #removeDescription(TaxonNameDescription)
1434 */
1435 public void addDescription(TaxonNameDescription description) {
1436 java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonNameBase.class);
1437 ReflectionUtils.makeAccessible(field);
1438 ReflectionUtils.setField(field, description, this);
1439 descriptions.add(description);
1440 }
1441 /**
1442 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription taxon name descriptions} assigned
1443 * to <i>this</i> taxon name. The content of the {@link eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName() taxonName attribute}
1444 * of the description itself will be set to "null".
1445 *
1446 * @param description the taxon name description which should be removed
1447 * @see #getDescriptions()
1448 * @see #addDescription(TaxonNameDescription)
1449 * @see eu.etaxonomy.cdm.model.description.TaxonNameDescription#getTaxonName()
1450 */
1451 public void removeDescription(TaxonNameDescription description) {
1452 java.lang.reflect.Field field = ReflectionUtils.findField(TaxonNameDescription.class, "taxonName", TaxonNameBase.class);
1453 ReflectionUtils.makeAccessible(field);
1454 ReflectionUtils.setField(field, description, null);
1455 descriptions.remove(description);
1456 }
1457
1458 // *********** HOMOTYPIC GROUP METHODS **************************************************
1459
1460 @Transient
1461 public void mergeHomotypicGroups(TaxonNameBase name){
1462 this.getHomotypicalGroup().merge(name.getHomotypicalGroup());
1463 //HomotypicalGroup thatGroup = name.homotypicalGroup;
1464 name.setHomotypicalGroup(this.homotypicalGroup);
1465 }
1466
1467 /**
1468 * Returns the boolean value indicating whether a given taxon name belongs
1469 * to the same {@link HomotypicalGroup homotypical group} as <i>this</i> taxon name (true)
1470 * or not (false). Returns "true" only if the homotypical groups of both
1471 * taxon names exist and if they are identical.
1472 *
1473 * @param homoTypicName the taxon name the homotypical group of which is to be checked
1474 * @return the boolean value of the check
1475 * @see HomotypicalGroup
1476 */
1477 @Transient
1478 public boolean isHomotypic(TaxonNameBase homoTypicName) {
1479 if (homoTypicName == null) {
1480 return false;
1481 }
1482 HomotypicalGroup homotypicGroup = homoTypicName.getHomotypicalGroup();
1483 if (homotypicGroup == null || this.getHomotypicalGroup() == null) {
1484 return false;
1485 }
1486 if (homotypicGroup.equals(this.getHomotypicalGroup())) {
1487 return true;
1488 }
1489 return false;
1490 }
1491
1492
1493 /**
1494 * Checks whether name is a basionym for ALL names
1495 * in its homotypical group.
1496 * Returns <code>false</code> if there are no other names in the group
1497 * @param name
1498 * @return
1499 */
1500 @Transient
1501 public boolean isGroupsBasionym() {
1502 Set<TaxonNameBase> typifiedNames = homotypicalGroup.getTypifiedNames();
1503
1504 // Check whether there are any other names in the group
1505 if (typifiedNames.size() == 1) {
1506 return false;
1507 }
1508
1509 boolean isBasionymToAll = true;
1510
1511 for (TaxonNameBase taxonName : typifiedNames) {
1512 if (!taxonName.equals(this)) {
1513 if (! isBasionymFor(taxonName)) {
1514 return false;
1515 }
1516 }
1517 }
1518 return true;
1519 }
1520
1521 /**
1522 * Checks whether a basionym relationship exists between fromName and toName.
1523 *
1524 * @param fromName
1525 * @param toName
1526 * @return
1527 */
1528 @Transient
1529 public boolean isBasionymFor(TaxonNameBase newCombinationName) {
1530 Set<NameRelationship> relations = newCombinationName.getRelationsToThisName();
1531 for (NameRelationship relation : relations) {
1532 if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1533 relation.getFromName().equals(this)) {
1534 return true;
1535 }
1536 }
1537 return false;
1538 }
1539
1540 /**
1541 * Creates a basionym relationship to all other names in this names homotypical
1542 * group.
1543 *
1544 * @see HomotypicalGroup.setGroupBasionym(TaxonNameBase basionymName)
1545
1546 */
1547 /* (non-Javadoc)
1548 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup#setGroupBasionym(TaxonNameBase)
1549 */
1550 @Transient
1551 public void makeGroupsBasionym() {
1552 this.homotypicalGroup.setGroupBasionym(this);
1553 }
1554
1555
1556 //********* Rank comparison shortcuts ********************//
1557 /**
1558 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1559 * taxon name is higher than the genus rank (true) or not (false).
1560 * Suprageneric non viral names are monomials.
1561 * Returns false if rank is null.
1562 *
1563 * @see #isGenus()
1564 * @see #isInfraGeneric()
1565 * @see #isSpecies()
1566 * @see #isInfraSpecific()
1567 */
1568 @Transient
1569 public boolean isSupraGeneric() {
1570 if (rank == null){
1571 return false;
1572 }
1573 return getRank().isSupraGeneric();
1574 }
1575 /**
1576 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1577 * taxon name is the genus rank (true) or not (false). Non viral names with
1578 * genus rank are monomials. Returns false if rank is null.
1579 *
1580 * @see #isSupraGeneric()
1581 * @see #isInfraGeneric()
1582 * @see #isSpecies()
1583 * @see #isInfraSpecific()
1584 */
1585 @Transient
1586 public boolean isGenus() {
1587 if (rank == null){
1588 return false;
1589 }
1590 return getRank().isGenus();
1591 }
1592 /**
1593 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1594 * taxon name is higher than the species rank and lower than the
1595 * genus rank (true) or not (false). Infrageneric non viral names are
1596 * binomials. Returns false if rank is null.
1597 *
1598 * @see #isSupraGeneric()
1599 * @see #isGenus()
1600 * @see #isSpecies()
1601 * @see #isInfraSpecific()
1602 */
1603 @Transient
1604 public boolean isInfraGeneric() {
1605 if (rank == null){
1606 return false;
1607 }
1608 return getRank().isInfraGeneric();
1609 }
1610
1611 /**
1612 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1613 * taxon name is higher than the species rank (true) or not (false).
1614 * Returns false if rank is null.
1615 *
1616 * @see #isGenus()
1617 * @see #isInfraGeneric()
1618 * @see #isSpecies()
1619 * @see #isInfraSpecific()
1620 */
1621 @Transient
1622 public boolean isSupraSpecific(){
1623 if (rank == null) {
1624 return false;
1625 }
1626 return getRank().isHigher(Rank.SPECIES());
1627 }
1628
1629 /**
1630 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1631 * taxon name is the species rank (true) or not (false). Non viral names
1632 * with species rank are binomials.
1633 * Returns false if rank is null.
1634 *
1635 * @see #isSupraGeneric()
1636 * @see #isGenus()
1637 * @see #isInfraGeneric()
1638 * @see #isInfraSpecific()
1639 */
1640 @Transient
1641 public boolean isSpecies() {
1642 if (rank == null){
1643 return false;
1644 }
1645 return getRank().isSpecies();
1646 }
1647 /**
1648 * Returns the boolean value indicating whether the taxonomic {@link Rank rank} of <i>this</i>
1649 * taxon name is lower than the species rank (true) or not (false).
1650 * Infraspecific non viral names are trinomials.
1651 * Returns false if rank is null.
1652 *
1653 * @see #isSupraGeneric()
1654 * @see #isGenus()
1655 * @see #isInfraGeneric()
1656 * @see #isSpecies()
1657 */
1658 @Transient
1659 public boolean isInfraSpecific() {
1660 if (rank == null){
1661 return false;
1662 }
1663 return getRank().isInfraSpecific();
1664 }
1665
1666
1667 /**
1668 * Returns null as the {@link NomenclaturalCode nomenclatural code} that governs
1669 * the construction of <i>this</i> taxon name since there is no specific
1670 * nomenclatural code defined. The real implementention takes place in the
1671 * subclasses {@link ViralName ViralName}, {@link BacterialName BacterialName},
1672 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
1673 * {@link ZoologicalName ZoologicalName}. Each taxon name is governed by one
1674 * and only one nomenclatural code.
1675 *
1676 * @return null
1677 * @see #isCodeCompliant()
1678 * @see #getHasProblem()
1679 */
1680 abstract public NomenclaturalCode getNomenclaturalCode();
1681
1682 /* (non-Javadoc)
1683 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1684 */
1685 /**
1686 * Generates and returns the string with the scientific name of <i>this</i>
1687 * taxon name (only non viral taxon names can be generated from their
1688 * components). This string may be stored in the inherited
1689 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
1690 * This method overrides the generic and inherited
1691 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() method} from
1692 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
1693 *
1694 * @return the string with the composed name of this non viral taxon name with authorship (and maybe year)
1695 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
1696 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
1697 */
1698 // @Override
1699 // public abstract String generateTitle();
1700
1701 /**
1702 * Creates a basionym relationship between this name and
1703 * each name in its homotypic group.
1704 *
1705 * @param basionymName
1706 */
1707 @Transient
1708 public void setAsGroupsBasionym() {
1709
1710
1711 HomotypicalGroup homotypicalGroup = this.getHomotypicalGroup();
1712
1713 if (homotypicalGroup == null) {
1714 return;
1715 }
1716
1717 Set<NameRelationship> relations = new HashSet<NameRelationship>();
1718 Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
1719
1720 for(TaxonNameBase<?, ?> typifiedName : homotypicalGroup.getTypifiedNames()){
1721
1722 Set<NameRelationship> nameRelations = typifiedName.getRelationsFromThisName();
1723
1724 for(NameRelationship nameRelation : nameRelations){
1725 relations.add(nameRelation);
1726 }
1727 }
1728
1729 for (NameRelationship relation : relations) {
1730
1731 // If this is a basionym relation, and toName is in the homotypical group,
1732 // remove the relationship.
1733 if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1734 relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
1735 removeRelations.add(relation);
1736 }
1737 }
1738
1739 // Removing relations from a set through which we are iterating causes a
1740 // ConcurrentModificationException. Therefore, we delete the targeted
1741 // relations in a second step.
1742 for (NameRelationship relation : removeRelations) {
1743 this.removeNameRelationship(relation);
1744 }
1745
1746
1747 for (TaxonNameBase<?, ?> name : homotypicalGroup.getTypifiedNames()) {
1748 if (!name.equals(this)) {
1749
1750 // First check whether the relationship already exists
1751 if (!this.isBasionymFor(name)) {
1752
1753 // Then create it
1754 name.addRelationshipFromName(this,
1755 NameRelationshipType.BASIONYM(), null);
1756 }
1757 }
1758 }
1759 }
1760
1761 /**
1762 * Removes basionym relationship between this name and
1763 * each name in its homotypic group.
1764 *
1765 * @param basionymName
1766 */
1767 @Transient
1768 public void removeAsGroupsBasionym() {
1769
1770 HomotypicalGroup homotypicalGroup = this.getHomotypicalGroup();
1771
1772 if (homotypicalGroup == null) {
1773 return;
1774 }
1775
1776 Set<NameRelationship> relations = new HashSet<NameRelationship>();
1777 Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
1778
1779 for(TaxonNameBase<?, ?> typifiedName : homotypicalGroup.getTypifiedNames()){
1780
1781 Set<NameRelationship> nameRelations = typifiedName.getRelationsFromThisName();
1782
1783 for(NameRelationship nameRelation : nameRelations){
1784 relations.add(nameRelation);
1785 }
1786 }
1787
1788 for (NameRelationship relation : relations) {
1789
1790 // If this is a basionym relation, and toName is in the homotypical group,
1791 // and fromName is basionymName, remove the relationship.
1792 if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
1793 relation.getFromName().equals(this) &&
1794 relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
1795 removeRelations.add(relation);
1796 }
1797 }
1798
1799 // Removing relations from a set through which we are iterating causes a
1800 // ConcurrentModificationException. Therefore, we delete the targeted
1801 // relations in a second step.
1802 for (NameRelationship relation : removeRelations) {
1803 this.removeNameRelationship(relation);
1804 }
1805 }
1806
1807 //*********************** CLONE ********************************************************/
1808
1809 /**
1810 * Clones <i>this</i> taxon name. This is a shortcut that enables to create
1811 * a new instance that differs only slightly from <i>this</i> taxon name by
1812 * modifying only some of the attributes.<BR><BR>
1813 * Usages of this name in a taxon concept are <b>not</b> cloned.<BR>
1814 * <b>The name gets a newly created homotypical group</b><BR>
1815 * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1816 * {@link TaxonNameDescription Name descriptions} are cloned and not reused.<BR>
1817 * {@link TypeDesignationBase Type designations} are cloned and not reused.<BR>
1818 *
1819 * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1820 * @see java.lang.Object#clone()
1821 */
1822 @Override
1823 public Object clone() {
1824 TaxonNameBase result;
1825 try {
1826 result = (TaxonNameBase)super.clone();
1827
1828 //taxonBases -> empty
1829 result.taxonBases = new HashSet<TaxonBase>();
1830
1831 //empty caches
1832 if (! protectedFullTitleCache){
1833 result.fullTitleCache = null;
1834 }
1835
1836 //descriptions
1837 result.descriptions = new HashSet<TaxonNameDescription>();
1838 for (TaxonNameDescription taxonNameDescription : getDescriptions()){
1839 TaxonNameDescription newDescription = (TaxonNameDescription)taxonNameDescription.clone();
1840 result.descriptions.add(newDescription);
1841 }
1842
1843 //status
1844 result.status = new HashSet<NomenclaturalStatus>();
1845 for (NomenclaturalStatus nomenclaturalStatus : getStatus()){
1846 NomenclaturalStatus newStatus = (NomenclaturalStatus)nomenclaturalStatus.clone();
1847 result.status.add(newStatus);
1848 }
1849
1850
1851 //To Relations
1852 result.relationsToThisName = new HashSet<NameRelationship>();
1853 for (NameRelationship toRelationship : getRelationsToThisName()){
1854 NameRelationship newRelationship = (NameRelationship)toRelationship.clone();
1855 newRelationship.setRelatedTo(result);
1856 result.relationsToThisName.add(newRelationship);
1857 }
1858
1859 //From Relations
1860 result.relationsFromThisName = new HashSet<NameRelationship>();
1861 for (NameRelationship fromRelationship : getRelationsFromThisName()){
1862 NameRelationship newRelationship = (NameRelationship)fromRelationship.clone();
1863 newRelationship.setRelatedFrom(result);
1864 result.relationsFromThisName.add(newRelationship);
1865 }
1866
1867 //type designations
1868 result.typeDesignations = new HashSet<TypeDesignationBase>();
1869 for (TypeDesignationBase typeDesignation : getTypeDesignations()){
1870 TypeDesignationBase newDesignation = (TypeDesignationBase)typeDesignation.clone();
1871 result.typeDesignations.add(newDesignation);
1872 newDesignation.addTypifiedName(result);
1873 }
1874
1875 //homotypicalGroup
1876 //TODO still needs to be discussed
1877 homotypicalGroup = HomotypicalGroup.NewInstance();
1878 homotypicalGroup.addTypifiedName(this);
1879
1880 //no changes to: appendedPharse, nomenclaturalReference,
1881 //nomenclaturalMicroReference, parsingProblem, problemEnds, problemStarts
1882 //protectedFullTitleCache, rank
1883 return result;
1884 } catch (CloneNotSupportedException e) {
1885 logger.warn("Object does not implement cloneable");
1886 e.printStackTrace();
1887 return null;
1888 }
1889
1890 }
1891 }