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