root/trunk/cdmlib/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/TaxonNameBase.java

Revision 14777, 73.5 kB (checked in by a.mueller, 6 weeks ago)

add nameCache index

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