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

Revision 14889, 58.4 kB (checked in by a.kohlbecker, 2 weeks ago)

fixing #2879 (Error when using the remote webservice function find NamebyNames?)

  • 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
12
13import java.beans.PropertyChangeEvent;
14import java.beans.PropertyChangeListener;
15import java.util.ArrayList;
16import java.util.Collections;
17import java.util.HashSet;
18import java.util.List;
19import java.util.Map;
20import java.util.Set;
21
22import javax.persistence.Entity;
23import javax.persistence.FetchType;
24import javax.persistence.ManyToOne;
25import javax.persistence.OneToMany;
26import javax.persistence.Transient;
27import javax.validation.constraints.NotNull;
28import javax.validation.constraints.Pattern;
29import javax.validation.constraints.Size;
30import javax.xml.bind.annotation.XmlAccessType;
31import javax.xml.bind.annotation.XmlAccessorType;
32import javax.xml.bind.annotation.XmlElement;
33import javax.xml.bind.annotation.XmlElementWrapper;
34import javax.xml.bind.annotation.XmlIDREF;
35import javax.xml.bind.annotation.XmlRootElement;
36import javax.xml.bind.annotation.XmlSchemaType;
37import javax.xml.bind.annotation.XmlType;
38
39import org.apache.log4j.Logger;
40import org.hibernate.annotations.Cascade;
41import org.hibernate.annotations.CascadeType;
42import org.hibernate.annotations.Target;
43import org.hibernate.envers.Audited;
44import org.hibernate.search.annotations.Field;
45import org.hibernate.search.annotations.Fields;
46import org.hibernate.search.annotations.Index;
47import org.hibernate.search.annotations.Indexed;
48import org.hibernate.search.annotations.IndexedEmbedded;
49import org.hibernate.validator.constraints.NotEmpty;
50import org.springframework.beans.factory.annotation.Configurable;
51
52import eu.etaxonomy.cdm.common.CdmUtils;
53import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
54import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
55import eu.etaxonomy.cdm.model.common.CdmBase;
56import eu.etaxonomy.cdm.model.common.RelationshipBase;
57import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
58import eu.etaxonomy.cdm.model.reference.Reference;
59import eu.etaxonomy.cdm.strategy.cache.name.CacheUpdate;
60import eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy;
61import eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy;
62import eu.etaxonomy.cdm.strategy.match.Match;
63import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
64import eu.etaxonomy.cdm.strategy.match.MatchMode;
65import eu.etaxonomy.cdm.strategy.merge.Merge;
66import eu.etaxonomy.cdm.strategy.merge.MergeMode;
67import eu.etaxonomy.cdm.validation.Level2;
68import eu.etaxonomy.cdm.validation.Level3;
69import eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank;
70import eu.etaxonomy.cdm.validation.annotation.MustHaveAuthority;
71import eu.etaxonomy.cdm.validation.annotation.NoDuplicateNames;
72import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
73
74/**
75 * The taxon name class for all non viral taxa. Parenthetical authorship is derived
76 * from basionym relationship. The scientific name including author strings and
77 * maybe year can be stored as a string in the inherited {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
78 * The year itself is an information obtained from the {@link eu.etaxonomy.cdm.model.reference.Reference#getYear() nomenclatural reference}.
79 * The scientific name string without author strings and year can be stored in the {@link #getNameCache() nameCache} attribute.
80 * <P>
81 * This class corresponds partially to: <ul>
82 * <li> TaxonName according to the TDWG ontology
83 * <li> ScientificName and CanonicalName according to the TCS
84 * <li> ScientificName according to the ABCD schema
85 * </ul>
86 *
87 * @author m.doering
88 * @version 1.0
89 * @created 08-Nov-2007 13:06:39
90 */
91@XmlAccessorType(XmlAccessType.FIELD)
92@XmlType(name = "NonViralName", propOrder = {
93    "nameCache",
94    "genusOrUninomial",
95    "infraGenericEpithet",
96    "specificEpithet",
97    "infraSpecificEpithet",
98    "combinationAuthorTeam",
99    "exCombinationAuthorTeam",
100    "basionymAuthorTeam",
101    "exBasionymAuthorTeam",
102    "authorshipCache",
103    "protectedAuthorshipCache",
104    "protectedNameCache",
105    "hybridParentRelations",
106    "hybridChildRelations",
107    "hybridFormula",
108    "monomHybrid",
109    "binomHybrid",
110    "trinomHybrid"
111})
112@XmlRootElement(name = "NonViralName")
113@Entity
114@Indexed(index = "eu.etaxonomy.cdm.model.name.TaxonNameBase")
115@Audited
116@Configurable
117@CorrectEpithetsForRank(groups = Level2.class)
118@MustHaveAuthority(groups = Level2.class)
119@NoDuplicateNames(groups = Level3.class)
120public class NonViralName<T extends NonViralName> extends TaxonNameBase<T, INonViralNameCacheStrategy> implements Cloneable{
121    private static final long serialVersionUID = 4441110073881088033L;
122    private static final Logger logger = Logger.getLogger(NonViralName.class);
123
124    @XmlElement(name = "NameCache")
125    @Fields({@Field(name = "nameCache_tokenized",index = org.hibernate.search.annotations.Index.TOKENIZED),
126         @Field(index = org.hibernate.search.annotations.Index.UN_TOKENIZED)
127    })
128    @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.DEFINED,
129            cacheReplacedProperties={"genusOrUninomial", "infraGenericEpithet", "specificEpithet", "infraSpecificEpithet"} )
130    @NotEmpty(groups = Level2.class) // implictly NotNull
131    @Size(max = 255)
132    private String nameCache;
133
134    @XmlElement(name = "ProtectedNameCache")
135    @CacheUpdate(value="nameCache")
136    protected boolean protectedNameCache;
137
138    @XmlElement(name = "GenusOrUninomial")
139    @Field(index=Index.TOKENIZED)
140    @Match(MatchMode.EQUAL_REQUIRED)
141    @CacheUpdate("nameCache")
142    @NullOrNotEmpty
143    @Size(max = 255)
144    @Pattern(regexp = "[A-Z][a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class, message="{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForUninomial.message}")
145    @NotEmpty(groups = Level3.class)
146    private String genusOrUninomial;
147
148    @XmlElement(name = "InfraGenericEpithet")
149    @Field(index=Index.TOKENIZED)
150    @CacheUpdate("nameCache")
151    @NullOrNotEmpty
152    @Size(max = 255)
153    @Pattern(regexp = "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class,message="{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}")
154    private String infraGenericEpithet;
155
156    @XmlElement(name = "SpecificEpithet")
157    @Field(index=Index.TOKENIZED)
158    @CacheUpdate("nameCache")
159    @NullOrNotEmpty
160    @Size(max = 255)
161    @Pattern(regexp = "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class, message = "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}")
162    private String specificEpithet;
163
164    @XmlElement(name = "InfraSpecificEpithet")
165    @Field(index=Index.TOKENIZED)
166    @CacheUpdate("nameCache")
167    @NullOrNotEmpty
168    @Size(max = 255)
169    @Pattern(regexp = "[a-z\\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-]+", groups=Level2.class, message = "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForEpithet.message}")
170    private String infraSpecificEpithet;
171
172    @XmlElement(name = "CombinationAuthorTeam", type = TeamOrPersonBase.class)
173    @XmlIDREF
174    @XmlSchemaType(name = "IDREF")
175    @ManyToOne(fetch = FetchType.LAZY)
176    @Target(TeamOrPersonBase.class)
177    @Cascade(CascadeType.SAVE_UPDATE)
178    @CacheUpdate("authorshipCache")
179    @IndexedEmbedded
180    private INomenclaturalAuthor combinationAuthorTeam;
181
182    @XmlElement(name = "ExCombinationAuthorTeam", type = TeamOrPersonBase.class)
183    @XmlIDREF
184    @XmlSchemaType(name = "IDREF")
185    @ManyToOne(fetch = FetchType.LAZY)
186    @Target(TeamOrPersonBase.class)
187    @Cascade(CascadeType.SAVE_UPDATE)
188    @CacheUpdate("authorshipCache")
189    @IndexedEmbedded
190    private INomenclaturalAuthor exCombinationAuthorTeam;
191
192    @XmlElement(name = "BasionymAuthorTeam", type = TeamOrPersonBase.class)
193    @XmlIDREF
194    @XmlSchemaType(name = "IDREF")
195    @ManyToOne(fetch = FetchType.LAZY)
196    @Target(TeamOrPersonBase.class)
197    @Cascade(CascadeType.SAVE_UPDATE)
198    @CacheUpdate("authorshipCache")
199    @IndexedEmbedded
200    private INomenclaturalAuthor basionymAuthorTeam;
201
202    @XmlElement(name = "ExBasionymAuthorTeam", type = TeamOrPersonBase.class)
203    @XmlIDREF
204    @XmlSchemaType(name = "IDREF")
205    @ManyToOne(fetch = FetchType.LAZY)
206    @Target(TeamOrPersonBase.class)
207    @Cascade(CascadeType.SAVE_UPDATE)
208    @CacheUpdate("authorshipCache")
209    @IndexedEmbedded
210    private INomenclaturalAuthor exBasionymAuthorTeam;
211
212    @XmlElement(name = "AuthorshipCache")
213    @Fields({@Field(name = "authorshipCache_tokenized",index = org.hibernate.search.annotations.Index.TOKENIZED),
214             @Field(index = org.hibernate.search.annotations.Index.UN_TOKENIZED)
215    })
216    @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.DEFINED,
217            cacheReplacedProperties={"combinationAuthorTeam", "basionymAuthorTeam", "exCombinationAuthorTeam", "exBasionymAuthorTeam"} )
218    @NullOrNotEmpty
219    @Size(max = 255)
220    @Pattern(regexp = "[A-Za-z0-9 \\u00E4\\u00EB\\u00EF\\u00F6\\u00FC\\-\\&\\,\\(\\)\\.]+", groups=Level2.class, message = "{eu.etaxonomy.cdm.model.name.NonViralName.allowedCharactersForAuthority.message}")
221    private String authorshipCache;
222
223    @XmlElement(name = "ProtectedAuthorshipCache")
224    @CacheUpdate("authorshipCache")
225    protected boolean protectedAuthorshipCache;
226
227    @XmlElementWrapper(name = "HybridRelationsFromThisName")
228    @XmlElement(name = "HybridRelationsFromThisName")
229    @OneToMany(mappedBy="relatedFrom", fetch = FetchType.LAZY)
230    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE_ORPHAN })
231    @Merge(MergeMode.RELATION)
232    @NotNull
233    private Set<HybridRelationship> hybridParentRelations = new HashSet<HybridRelationship>();
234
235    @XmlElementWrapper(name = "HybridRelationsToThisName")
236    @XmlElement(name = "HybridRelationsToThisName")
237    @OneToMany(mappedBy="relatedTo", fetch = FetchType.LAZY)
238    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN })  //a hybrid relation can be deleted automatically if the child is deleted.
239    @Merge(MergeMode.RELATION)
240    @NotNull
241    private Set<HybridRelationship> hybridChildRelations = new HashSet<HybridRelationship>();
242
243    //if set: this name is a hybrid formula (a hybrid that does not have an own name) and no other hybrid flags may be set. A
244    //hybrid name  may not have either an authorteam nor other name components.
245    @XmlElement(name ="IsHybridFormula")
246    @CacheUpdate("nameCache")
247    private boolean hybridFormula = false;
248
249    @XmlElement(name ="IsMonomHybrid")
250    @CacheUpdate("nameCache")
251    private boolean monomHybrid = false;
252
253    @XmlElement(name ="IsBinomHybrid")
254    @CacheUpdate("nameCache")
255    private boolean binomHybrid = false;
256
257    @XmlElement(name ="IsTrinomHybrid")
258    @CacheUpdate("nameCache")
259    private boolean trinomHybrid = false;
260
261    /**
262     * Creates a new non viral taxon name instance
263     * only containing its {@link common.Rank rank} and
264      * the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}.
265     *
266     * @param  rank  the rank to be assigned to <i>this</i> non viral taxon name
267     * @see    #NewInstance(Rank, HomotypicalGroup)
268     * @see    #NonViralName(Rank, HomotypicalGroup)
269     * @see    #NonViralName()
270     * @see    #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup)
271     * @see    eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy
272     * @see    eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy
273     * @see    eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
274     */
275    public static NonViralName NewInstance(Rank rank){
276        return new NonViralName(rank, null);
277    }
278
279    /**
280     * Creates a new non viral taxon name instance
281     * only containing its {@link common.Rank rank},
282     * its {@link HomotypicalGroup homotypical group} and
283      * the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}.
284     * The new non viral taxon name instance will be also added to the set of
285     * non viral taxon names belonging to this homotypical group.
286     *
287     * @param  rank  the rank to be assigned to <i>this</i> non viral taxon name
288     * @param  homotypicalGroup  the homotypical group to which <i>this</i> non viral taxon name belongs
289     * @see    #NewInstance(Rank)
290     * @see    #NonViralName(Rank, HomotypicalGroup)
291     * @see    #NonViralName()
292     * @see    #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup)
293     * @see    eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy
294     * @see    eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy
295     * @see    eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
296     */
297    public static NonViralName NewInstance(Rank rank, HomotypicalGroup homotypicalGroup){
298        return new NonViralName(rank, homotypicalGroup);
299    }
300
301// ************************** CONSTRUCTORS *************/
302
303    //needed by hibernate
304    /**
305     * Class constructor: creates a new non viral taxon name instance
306     * only containing the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}.
307     *
308     * @see #NonViralName(Rank, HomotypicalGroup)
309     * @see #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup)
310     * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy
311     * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy
312     * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
313     */
314    protected NonViralName(){
315        super();
316        setNameCacheStrategy();
317    }
318
319    /**
320     * Class constructor: creates a new non viral taxon name instance
321     * only containing its {@link Rank rank},
322     * its {@link HomotypicalGroup homotypical group} and
323     * the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}.
324     * The new non viral taxon name instance will be also added to the set of
325     * non viral taxon names belonging to this homotypical group.
326     *
327     * @param   rank  the rank to be assigned to <i>this</i> non viral taxon name
328     * @param   homotypicalGroup  the homotypical group to which <i>this</i> non viral taxon name belongs
329     * @see     #NonViralName()
330     * @see             #NonViralName(Rank, String, String, String, String, TeamOrPersonBase, Reference, String, HomotypicalGroup)
331     * @see             #NewInstance(Rank, HomotypicalGroup)
332     * @see     eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy
333     * @see     eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy
334     * @see     eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
335     */
336    protected NonViralName(Rank rank, HomotypicalGroup homotypicalGroup) {
337        super(rank, homotypicalGroup);
338        setNameCacheStrategy();
339    }
340    /**
341     * Class constructor: creates a new non viral taxon name instance
342     * containing its {@link Rank rank},
343     * its {@link HomotypicalGroup homotypical group},
344     * its scientific name components, its {@link eu.etaxonomy.cdm.model.agent.TeamOrPersonBase author(team)},
345     * its {@link eu.etaxonomy.cdm.model.reference.Reference nomenclatural reference} and
346     * the {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy default cache strategy}.
347     * The new non viral taxon name instance will be also added to the set of
348     * non viral taxon names belonging to this homotypical group.
349     *
350     * @param   rank  the rank to be assigned to <i>this</i> non viral taxon name
351     * @param   genusOrUninomial the string for <i>this</i> non viral taxon name
352     *                  if its rank is genus or higher or for the genus part
353     *                  if its rank is lower than genus
354     * @param   infraGenericEpithet  the string for the first epithet of
355     *                  <i>this</i> non viral taxon name if its rank is lower than genus
356     *                  and higher than species aggregate
357     * @param   specificEpithet  the string for the first epithet of
358     *                  <i>this</i> non viral taxon name if its rank is species aggregate or lower
359     * @param   infraSpecificEpithet  the string for the second epithet of
360     *                  <i>this</i> non viral taxon name if its rank is lower than species
361     * @param   combinationAuthorTeam  the author or the team who published <i>this</i> non viral taxon name
362     * @param   nomenclaturalReference  the nomenclatural reference where <i>this</i> non viral taxon name was published
363     * @param   nomenclMicroRef  the string with the details for precise location within the nomenclatural reference
364     * @param   homotypicalGroup  the homotypical group to which <i>this</i> non viral taxon name belongs
365     * @see     #NonViralName()
366     * @see             #NonViralName(Rank, HomotypicalGroup)
367     * @see             #NewInstance(Rank, HomotypicalGroup)
368     * @see     eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy
369     * @see     eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy
370     * @see     eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
371     */
372    protected NonViralName(Rank rank, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, TeamOrPersonBase combinationAuthorTeam, INomenclaturalReference nomenclaturalReference, String nomenclMicroRef, HomotypicalGroup homotypicalGroup) {
373        super(rank, homotypicalGroup);
374        setNameCacheStrategy();
375        setGenusOrUninomial(genusOrUninomial);
376        setInfraGenericEpithet (infraGenericEpithet);
377        setSpecificEpithet(specificEpithet);
378        setInfraSpecificEpithet(infraSpecificEpithet);
379        setCombinationAuthorTeam(combinationAuthorTeam);
380        setNomenclaturalReference((Reference)nomenclaturalReference);
381        this.setNomenclaturalMicroReference(nomenclMicroRef);
382    }
383
384
385
386//**************************** METHODS **************************************/
387
388
389    private void setNameCacheStrategy(){
390        if (getClass() == NonViralName.class){
391            this.cacheStrategy = NonViralNameDefaultCacheStrategy.NewInstance();
392        }
393    }
394
395    protected void initListener(){
396        PropertyChangeListener listener = new PropertyChangeListener() {
397            public void propertyChange(PropertyChangeEvent e) {
398                boolean protectedByLowerCache = false;
399                //authorship cache
400                if (fieldHasCacheUpdateProperty(e.getPropertyName(), "authorshipCache")){
401                    if (protectedAuthorshipCache){
402                        protectedByLowerCache = true;
403                    }else{
404                        authorshipCache = null;
405                    }
406                }
407
408                //nameCache
409                if (fieldHasCacheUpdateProperty(e.getPropertyName(), "nameCache")){
410                    if (protectedNameCache){
411                        protectedByLowerCache = true;
412                    }else{
413                        nameCache = null;
414                    }
415                }
416                //title cache
417                if (! fieldHasNoUpdateProperty(e.getPropertyName(), "titleCache")){
418                    if (isProtectedTitleCache()|| protectedByLowerCache == true ){
419                        protectedByLowerCache = true;
420                    }else{
421                        titleCache = null;
422                    }
423                }
424                //full title cache
425                if (! fieldHasNoUpdateProperty(e.getPropertyName(), "fullTitleCache")){
426                    if (isProtectedFullTitleCache()|| protectedByLowerCache == true ){
427                        protectedByLowerCache = true;
428                    }else{
429                        fullTitleCache = null;
430                    }
431                }
432            }
433        };
434        addPropertyChangeListener(listener);  //didn't use this.addXXX to make lsid.AssemblerTest run in cdmlib-remote
435    }
436
437    private static Map<String, java.lang.reflect.Field> allFields = null;
438    @Override
439    protected Map<String, java.lang.reflect.Field> getAllFields(){
440        if (allFields == null){
441            allFields = CdmUtils.getAllFields(this.getClass(), CdmBase.class, false, false, false, true);
442        }
443        return allFields;
444    }
445
446    /**
447     * @param propertyName
448     * @param string
449     * @return
450     */
451    private boolean fieldHasCacheUpdateProperty(String propertyName, String cacheName) {
452        java.lang.reflect.Field field;
453        try {
454            field = getAllFields().get(propertyName);
455            if (field != null){
456                CacheUpdate updateAnnotation = field.getAnnotation(CacheUpdate.class);
457                if (updateAnnotation != null){
458                    for (String value : updateAnnotation.value()){
459                        if (cacheName.equals(value)){
460                            return true;
461                        }
462                    }
463                }
464            }
465            return false;
466        } catch (SecurityException e1) {
467            throw e1;
468        }
469    }
470
471    private boolean fieldHasNoUpdateProperty(String propertyName, String cacheName) {
472        java.lang.reflect.Field field;
473        //do not update fields with the same name
474        if (cacheName.equals(propertyName)){
475            return true;
476        }
477        //evaluate annotation
478        try {
479            field = getAllFields().get(propertyName);
480            if (field != null){
481                CacheUpdate updateAnnotation = field.getAnnotation(CacheUpdate.class);
482                if (updateAnnotation != null){
483                    for (String value : updateAnnotation.noUpdate()){
484                        if (cacheName.equals(value)){
485                            return true;
486                        }
487                    }
488                }
489            }
490            return false;
491        } catch (SecurityException e1) {
492            throw e1;
493        }
494    }
495
496
497    /**
498     * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that published <i>this</i> non viral
499     * taxon name.
500     *
501     * @return  the nomenclatural author (team) of <i>this</i> non viral taxon name
502     * @see     eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
503     * @see     eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
504     */
505    public INomenclaturalAuthor getCombinationAuthorTeam(){
506        return this.combinationAuthorTeam;
507    }
508
509    /**
510     * @see  #getCombinationAuthorTeam()
511     */
512    public void setCombinationAuthorTeam(INomenclaturalAuthor combinationAuthorTeam){
513        this.combinationAuthorTeam = combinationAuthorTeam;
514    }
515
516    /**
517     * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that contributed to
518     * the publication of <i>this</i> non viral taxon name as generally stated by
519     * the {@link #getCombinationAuthorTeam() combination author (team)} itself.<BR>
520     * An ex-author(-team) is an author(-team) to whom a taxon name was ascribed
521     * although it is not the author(-team) of a valid publication (for instance
522     * without the validating description or diagnosis in case of a name for a
523     * new taxon). The name of this ascribed authorship, followed by "ex", may
524     * be inserted before the name(s) of the publishing author(s) of the validly
525     * published name:<BR>
526     * <i>Lilium tianschanicum</i> was described by Grubov (1977) as a new species and
527     * its name was ascribed to Ivanova; since there is no indication that
528     * Ivanova provided the validating description, the name may be cited as
529     * <i>Lilium tianschanicum</i> N. A. Ivanova ex Grubov or <i>Lilium tianschanicum</i> Grubov.
530     * <P>
531     * The presence of an author (team) of <i>this</i> non viral taxon name is a
532     * condition for the existence of an ex author (team) for <i>this</i> same name.
533     *
534     * @return  the nomenclatural ex author (team) of <i>this</i> non viral taxon name
535     * @see     #getCombinationAuthorTeam()
536     * @see     eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
537     * @see     eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
538     */
539    public INomenclaturalAuthor getExCombinationAuthorTeam(){
540        return this.exCombinationAuthorTeam;
541    }
542
543    /**
544     * @see  #getExCombinationAuthorTeam()
545     */
546    public void setExCombinationAuthorTeam(INomenclaturalAuthor exCombinationAuthorTeam){
547        this.exCombinationAuthorTeam = exCombinationAuthorTeam;
548    }
549
550    /**
551     * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that published the original combination
552     * on which <i>this</i> non viral taxon name is nomenclaturally based. Such an
553     * author (team) can only exist if <i>this</i> non viral taxon name is a new
554     * combination due to a taxonomical revision.
555     *
556     * @return  the nomenclatural basionym author (team) of <i>this</i> non viral taxon name
557     * @see     #getCombinationAuthorTeam()
558     * @see     eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
559     * @see     eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
560     */
561    public INomenclaturalAuthor getBasionymAuthorTeam(){
562        return basionymAuthorTeam;
563    }
564
565    /**
566     * @see  #getBasionymAuthorTeam()
567     */
568    public void setBasionymAuthorTeam(INomenclaturalAuthor basionymAuthorTeam) {
569        this.basionymAuthorTeam = basionymAuthorTeam;
570    }
571
572    /**
573     * Returns the {@link eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor author (team)} that contributed to
574     * the publication of the original combination <i>this</i> non viral taxon name is
575     * based on. This should have been generally stated by
576     * the {@link #getBasionymAuthorTeam() basionym author (team)} itself.
577     * The presence of a basionym author (team) of <i>this</i> non viral taxon name is a
578     * condition for the existence of an ex basionym author (team)
579     * for <i>this</i> same name.
580     *
581     * @return  the nomenclatural ex basionym author (team) of <i>this</i> non viral taxon name
582     * @see     #getBasionymAuthorTeam()
583     * @see     #getExCombinationAuthorTeam()
584     * @see     #getCombinationAuthorTeam()
585     * @see     eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor
586     * @see     eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getNomenclaturalTitle()
587     */
588    public INomenclaturalAuthor getExBasionymAuthorTeam(){
589        return exBasionymAuthorTeam;
590    }
591
592    /**
593     * @see  #getExBasionymAuthorTeam()
594     */
595    public void setExBasionymAuthorTeam(INomenclaturalAuthor exBasionymAuthorTeam) {
596        this.exBasionymAuthorTeam = exBasionymAuthorTeam;
597    }
598    /**
599     * Returns either the scientific name string (without authorship) for <i>this</i>
600     * non viral taxon name if its rank is genus or higher (monomial) or the string for
601     * the genus part of it if its {@link Rank rank} is lower than genus (bi- or trinomial).
602     * Genus or uninomial strings begin with an upper case letter.
603     *
604     * @return  the string containing the suprageneric name, the genus name or the genus part of <i>this</i> non viral taxon name
605     * @see     #getNameCache()
606     */
607    public String getGenusOrUninomial() {
608        return genusOrUninomial;
609    }
610
611    /**
612     * @see  #getGenusOrUninomial()
613     */
614    public void setGenusOrUninomial(String genusOrUninomial) {
615        this.genusOrUninomial = genusOrUninomial;
616    }
617
618    /**
619     * Returns the genus subdivision epithet string (infrageneric part) for
620     * <i>this</i> non viral taxon name if its {@link Rank rank} is infrageneric (lower than genus and
621     * higher than species aggregate: binomial). Genus subdivision epithet
622     * strings begin with an upper case letter.
623     *
624     * @return  the string containing the infrageneric part of <i>this</i> non viral taxon name
625     * @see     #getNameCache()
626     */
627    public String getInfraGenericEpithet(){
628        return this.infraGenericEpithet;
629    }
630
631    /**
632     * @see  #getInfraGenericEpithet()
633     */
634    public void setInfraGenericEpithet(String infraGenericEpithet){
635        this.infraGenericEpithet = infraGenericEpithet;
636    }
637
638    /**
639     * Returns the species epithet string for <i>this</i> non viral taxon name if its {@link Rank rank} is
640     * species aggregate or lower (bi- or trinomial). Species epithet strings
641     * begin with a lower case letter.
642     *
643     * @return  the string containing the species epithet of <i>this</i> non viral taxon name
644     * @see     #getNameCache()
645     */
646    public String getSpecificEpithet(){
647        return this.specificEpithet;
648    }
649
650    /**
651     * @see  #getSpecificEpithet()
652     */
653    public void setSpecificEpithet(String specificEpithet){
654        this.specificEpithet = specificEpithet;
655    }
656
657    /**
658     * Returns the species subdivision epithet string (infraspecific part) for
659     * <i>this</i> non viral taxon name if its {@link Rank rank} is infraspecific
660     * (lower than species: trinomial). Species subdivision epithet strings
661     * begin with a lower case letter.
662     *
663     * @return  the string containing the infraspecific part of <i>this</i> non viral taxon name
664     * @see     #getNameCache()
665     */
666    public String getInfraSpecificEpithet(){
667        return this.infraSpecificEpithet;
668    }
669
670    /**
671     * @see  #getInfraSpecificEpithet()
672     */
673    public void setInfraSpecificEpithet(String infraSpecificEpithet){
674        this.infraSpecificEpithet = infraSpecificEpithet;
675    }
676
677    /**
678     * Generates and returns the string with the scientific name of <i>this</i>
679     * non viral taxon name including author strings and maybe year according to
680     * the strategy defined in
681     *  {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy INonViralNameCacheStrategy}.
682     * This string may be stored in the inherited
683     * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
684     * This method overrides the generic and inherited
685     * TaxonNameBase#generateTitle() method.
686     *
687     * @return  the string with the composed name of <i>this</i> non viral taxon name with authorship (and maybe year)
688     * @see     eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
689     * @see     eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
690     * @see     TaxonNameBase#generateTitle()
691     */
692//      @Override
693//      public String generateTitle(){
694//              if (cacheStrategy == null){
695//                      logger.warn("No CacheStrategy defined for nonViralName: " + this.getUuid());
696//                      return null;
697//              }else{
698//                      return cacheStrategy.getTitleCache(this);
699//              }
700//      }
701
702    @Override
703    public String generateFullTitle(){
704        if (cacheStrategy == null){
705            logger.warn("No CacheStrategy defined for nonViralName: " + this.getUuid());
706            return null;
707        }else{
708            return cacheStrategy.getFullTitleCache(this);
709        }
710    }
711
712    /**
713     * Generates the composed name string of <i>this</i> non viral taxon name without author
714     * strings or year according to the strategy defined in
715     * {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy INonViralNameCacheStrategy}.
716     * The result might be stored in {@link #getNameCache() nameCache} if the
717     * flag {@link #isProtectedNameCache() protectedNameCache} is not set.
718     *
719     * @return  the string with the composed name of <i>this</i> non viral taxon name without authors or year
720     * @see     #getNameCache()
721     */
722    protected String generateNameCache(){
723        if (cacheStrategy == null){
724            logger.warn("No CacheStrategy defined for taxonName: " + this.toString());
725            return null;
726        }else{
727            return cacheStrategy.getNameCache(this);
728        }
729    }
730
731    /**
732     * Returns or generates the nameCache (scientific name
733     * without author strings and year) string for <i>this</i> non viral taxon name. If the
734     * {@link #isProtectedNameCache() protectedNameCache} flag is not set (False)
735     * the string will be generated according to a defined strategy,
736     * otherwise the value of the actual nameCache string will be returned.
737     *
738     * @return  the string which identifies <i>this</i> non viral taxon name (without authors or year)
739     * @see     #generateNameCache()
740     */
741    @Transient
742    public String getNameCache() {
743        if (protectedNameCache){
744            return this.nameCache;
745        }
746        // is title dirty, i.e. equal NULL?
747        if (nameCache == null){
748            this.nameCache = generateNameCache();
749        }
750        return nameCache;
751    }
752
753    /**
754     * Assigns a nameCache string to <i>this</i> non viral taxon name and protects it from being overwritten.
755     * Sets the protectedNameCache flag to <code>true</code>.
756     *
757     * @param  nameCache  the string which identifies <i>this</i> non viral taxon name (without authors or year)
758     * @see        #getNameCache()
759     */
760    public void setNameCache(String nameCache){
761        setNameCache(nameCache, true);
762    }
763
764    /**
765     * Assigns a nameCache string to <i>this</i> non viral taxon name and protects it from being overwritten.
766     * Sets the protectedNameCache flag to <code>true</code>.
767     *
768     * @param  nameCache  the string which identifies <i>this</i> non viral taxon name (without authors or year)
769     * @param  protectedNameCache if true teh protectedNameCache is set to <code>true</code> or otherwise set to
770     * <code>false</code>
771     * @see        #getNameCache()
772     */
773    public void setNameCache(String nameCache, boolean protectedNameCache){
774        this.nameCache = nameCache;
775        this.setProtectedNameCache(protectedNameCache);
776    }
777
778    /**
779     * Returns the boolean value of the flag intended to protect (true)
780     * or not (false) the {@link #getNameCache() nameCache} (scientific name without author strings and year)
781     * string of <i>this</i> non viral taxon name.
782     *
783     * @return  the boolean value of the protectedNameCache flag
784     * @see     #getNameCache()
785     */
786    public boolean isProtectedNameCache() {
787        return protectedNameCache;
788    }
789
790    /**
791     * @see     #isProtectedNameCache()
792     */
793    public void setProtectedNameCache(boolean protectedNameCache) {
794        this.protectedNameCache = protectedNameCache;
795    }
796
797
798    /**
799     * Generates and returns a concatenated and formated authorteams string
800     * including basionym and combination authors of <i>this</i> non viral taxon name
801     * according to the strategy defined in
802     * {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(NonViralName) INonViralNameCacheStrategy}.
803     *
804     * @return  the string with the concatenated and formated authorteams for <i>this</i> non viral taxon name
805     * @see     eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(NonViralName)
806     */
807    public String generateAuthorship(){
808        if (cacheStrategy == null){
809            logger.warn("No CacheStrategy defined for nonViralName: " + this.getUuid());
810            return null;
811        }else{
812            return ((INonViralNameCacheStrategy<T>)cacheStrategy).getAuthorshipCache((T)this);
813        }
814    }
815
816    /**
817     * Returns the concatenated and formated authorteams string including
818     * basionym and combination authors of <i>this</i> non viral taxon name.
819     * If the protectedAuthorshipCache flag is set this method returns the
820     * string stored in the the authorshipCache attribute, otherwise it
821     * generates the complete authorship string, returns it and stores it in
822     * the authorshipCache attribute.
823     *
824     * @return  the string with the concatenated and formated authorteams for <i>this</i> non viral taxon name
825     * @see     #generateAuthorship()
826     */
827    @Transient
828    public String getAuthorshipCache() {
829        if (protectedAuthorshipCache){
830            return this.authorshipCache;
831        }
832        if (this.authorshipCache == null ){
833            this.authorshipCache = generateAuthorship();
834        }else{
835            //TODO get is Dirty of authors, make better if possible
836            this.setAuthorshipCache(generateAuthorship(), protectedAuthorshipCache); //throw change event to inform higher caches
837
838        }
839        return authorshipCache;
840    }
841
842
843    /**
844     * Updates the authorship cache if any changes appeared in the authors nomenclatural caches.
845     * Deletes the titleCache and the fullTitleCache if not protected and if any change has happened
846     * @return
847     */
848    private void updateAuthorshipCache() {
849        //updates the authorship cache if necessary and via the listener updates all higher caches
850        if (protectedAuthorshipCache == false){
851            String oldCache = this.authorshipCache;
852            String newCache = this.getAuthorshipCache();
853            if ( (oldCache == null && newCache != null)  ||  ! oldCache.equals(newCache)){
854                this.setAuthorshipCache(this.getAuthorshipCache(), false);
855            }
856        }
857    }
858
859    /**
860     * Assigns an authorshipCache string to <i>this</i> non viral taxon name. Sets the isProtectedAuthorshipCache
861     * flag to <code>true</code>.
862     *
863     * @param  authorshipCache  the string which identifies the complete authorship of <i>this</i> non viral taxon name
864     * @see        #getAuthorshipCache()
865     */
866    public void setAuthorshipCache(String authorshipCache) {
867        setAuthorshipCache(authorshipCache, true);
868    }
869
870    @Transient
871    public String getFullTitleCache(){
872        updateAuthorshipCache();
873        return super.getFullTitleCache();
874    }
875
876    public String getTitleCache(){
877        if(!protectedTitleCache) {
878            updateAuthorshipCache();
879        }
880
881        return super.getTitleCache();
882    }
883
884
885    /**
886     * Assigns an authorshipCache string to <i>this</i> non viral taxon name.
887     *
888     * @param  authorshipCache  the string which identifies the complete authorship of <i>this</i> non viral taxon name
889     * @param  protectedAuthorshipCache if true the isProtectedAuthorshipCache flag is set to <code>true</code>, otherwise
890     * the flag is set to <code>false</code>.
891     * @see        #getAuthorshipCache()
892     */
893    public void setAuthorshipCache(String authorshipCache, boolean protectedAuthorshipCache) {
894        this.authorshipCache = authorshipCache;
895        this.setProtectedAuthorshipCache(protectedAuthorshipCache);
896    }
897
898    public void setTitleCache(String titleCache, boolean protectCache){
899        super.setTitleCache(titleCache, protectCache);
900    }
901
902    /**
903     * Returns the boolean value "false" since the components of <i>this</i> taxon name
904     * cannot follow the rules of a corresponding {@link NomenclaturalCode nomenclatural code}
905     * which is not defined for this class. The nomenclature code depends on
906     * the concrete name subclass ({@link BacterialName BacterialName},
907     * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} or
908     * {@link ZoologicalName ZoologicalName} to which <i>this</i> non viral taxon name belongs.
909     * This method overrides the isCodeCompliant method from the abstract
910     * {@link TaxonNameBase#isCodeCompliant() TaxonNameBase} class.
911     *
912     * @return  false
913     * @see             TaxonNameBase#isCodeCompliant()
914     */
915    @Override
916    @Transient
917    public boolean isCodeCompliant() {
918        //FIXME
919        logger.warn("is CodeCompliant not yet implemented");
920        return false;
921    }
922
923    /* (non-Javadoc)
924     * @see eu.etaxonomy.cdm.model.name.TaxonNameBase#getNomeclaturalCode()
925     */
926    /**
927     * Returns null as {@link NomenclaturalCode nomenclatural code} that governs
928     * the construction of <i>this</i> non viral taxon name since there is no specific
929     * nomenclatural code defined. The real implementention takes place in the
930     * subclasses {@link BacterialName BacterialName},
931     * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
932     * {@link ZoologicalName ZoologicalName}.
933     * This method overrides the getNomeclaturalCode method from {@link TaxonNameBase TaxonNameBase}.
934     *
935     * @return  null
936     * @see     #isCodeCompliant()
937     * @see     TaxonNameBase#getHasProblem()
938     */
939    @Override
940    @Transient
941    public NomenclaturalCode getNomenclaturalCode() {
942        logger.warn("Non Viral Name has no specific Code defined. Use subclasses");
943        return null;
944    }
945
946    /**
947     * Returns the boolean value of the flag intended to protect (true)
948     * or not (false) the {@link #getAuthorshipCache() authorshipCache} (complete authorship string)
949     * of <i>this</i> non viral taxon name.
950     *
951     * @return  the boolean value of the protectedAuthorshipCache flag
952     * @see     #getAuthorshipCache()
953     */
954    public boolean isProtectedAuthorshipCache() {
955        return protectedAuthorshipCache;
956    }
957
958    /**
959     * @see     #isProtectedAuthorshipCache()
960     * @see     #getAuthorshipCache()
961     */
962    public void setProtectedAuthorshipCache(boolean protectedAuthorshipCache) {
963        this.protectedAuthorshipCache = protectedAuthorshipCache;
964    }
965
966
967    /**
968     * Returns the boolean value of the flag indicating whether the name of <i>this</i>
969     * botanical taxon name is a hybrid formula (true) or not (false). A hybrid
970     * named by a hybrid formula (composed with its parent names by placing the
971     * multiplication sign between them) does not have an own published name
972     * and therefore has neither an {@link NonViralName#getAuthorshipCache() autorship}
973     * nor other name components. If this flag is set no other hybrid flags may
974     * be set.
975     *
976     * @return  the boolean value of the isHybridFormula flag
977     * @see             #isMonomHybrid()
978     * @see             #isBinomHybrid()
979     * @see             #isTrinomHybrid()
980     */
981    public boolean isHybridFormula(){
982        return this.hybridFormula;
983    }
984
985    /**
986     * @see  #isHybridFormula()
987     */
988    public void setHybridFormula(boolean hybridFormula){
989        this.hybridFormula = hybridFormula;
990    }
991
992    /**
993     * Returns the boolean value of the flag indicating whether <i>this</i> botanical
994     * taxon name is the name of an intergeneric hybrid (true) or not (false).
995     * In this case the multiplication sign is placed before the scientific
996     * name. If this flag is set no other hybrid flags may be set.
997     *
998     * @return  the boolean value of the isMonomHybrid flag
999     * @see             #isHybridFormula()
1000     * @see             #isBinomHybrid()
1001     * @see             #isTrinomHybrid()
1002     */
1003    public boolean isMonomHybrid(){
1004        return this.monomHybrid;
1005    }
1006
1007    /**
1008     * @see  #isMonomHybrid()
1009     * @see      #isBinomHybrid()
1010     * @see      #isTrinomHybrid()
1011     */
1012    public void setMonomHybrid(boolean monomHybrid){
1013        this.monomHybrid = monomHybrid;
1014    }
1015
1016    /**
1017     * Returns the boolean value of the flag indicating whether <i>this</i> botanical
1018     * taxon name is the name of an interspecific hybrid (true) or not (false).
1019     * In this case the multiplication sign is placed before the species
1020     * epithet. If this flag is set no other hybrid flags may be set.
1021     *
1022     * @return  the boolean value of the isBinomHybrid flag
1023     * @see             #isHybridFormula()
1024     * @see             #isMonomHybrid()
1025     * @see             #isTrinomHybrid()
1026     */
1027    public boolean isBinomHybrid(){
1028        return this.binomHybrid;
1029    }
1030
1031    /**
1032     * @see      #isBinomHybrid()
1033     * @see  #isMonomHybrid()
1034     * @see      #isTrinomHybrid()
1035     */
1036    public void setBinomHybrid(boolean binomHybrid){
1037        this.binomHybrid = binomHybrid;
1038    }
1039
1040    /**
1041     * Returns the boolean value of the flag indicating whether <i>this</i> botanical
1042     * taxon name is the name of an infraspecific hybrid (true) or not (false).
1043     * In this case the term "notho-" (optionally abbreviated "n-") is used as
1044     * a prefix to the term denoting the infraspecific rank of <i>this</i> botanical
1045     * taxon name. If this flag is set no other hybrid flags may be set.
1046     *
1047     * @return  the boolean value of the isTrinomHybrid flag
1048     * @see             #isHybridFormula()
1049     * @see             #isMonomHybrid()
1050     * @see             #isBinomHybrid()
1051     */
1052    public boolean isTrinomHybrid(){
1053        return this.trinomHybrid;
1054    }
1055
1056    /**
1057     * @see      #isTrinomHybrid()
1058     * @see      #isBinomHybrid()
1059     * @see  #isMonomHybrid()
1060     */
1061    public void setTrinomHybrid(boolean trinomHybrid){
1062        this.trinomHybrid = trinomHybrid;
1063    }
1064
1065
1066    /**
1067     * Returns the set of all {@link HybridRelationship hybrid relationships}
1068     * in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedFrom() parent}.
1069     *
1070     * @see    #getHybridRelationships()
1071     * @see    #getChildRelationships()
1072     * @see    HybridRelationshipType
1073     */
1074    public Set<HybridRelationship> getHybridParentRelations() {
1075        if(hybridParentRelations == null) {
1076            this.hybridParentRelations = new HashSet<HybridRelationship>();
1077        }
1078        return hybridParentRelations;
1079    }
1080
1081    private void setHybridParentRelations(Set<HybridRelationship> hybridParentRelations) {
1082        this.hybridParentRelations = hybridParentRelations;
1083    }
1084
1085
1086    /**
1087     * Returns the set of all {@link HybridRelationship hybrid relationships}
1088     * in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedTo() child}.
1089     *
1090     * @see    #getHybridRelationships()
1091     * @see    #getParentRelationships()
1092     * @see    HybridRelationshipType
1093     */
1094    public Set<HybridRelationship> getHybridChildRelations() {
1095        if(hybridChildRelations == null) {
1096            this.hybridChildRelations = new HashSet<HybridRelationship>();
1097        }
1098        return hybridChildRelations;
1099    }
1100
1101    private void setHybridChildRelations(Set<HybridRelationship> hybridChildRelations) {
1102        this.hybridChildRelations = hybridChildRelations;
1103    }
1104
1105    /**
1106     * Returns the set of all {@link HybridRelationship hybrid relationships}
1107     * in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedFrom() parent}.
1108     * @see     #getHybridParentRelations()
1109     * @see     #getHybridRelationships()
1110     * @see     #getChildRelationships()
1111     * @see     HybridRelationshipType
1112     * @deprecated use {@link #getHybridParentRelations()} instead. Will be removed in higher versions.
1113     */
1114    @Transient
1115    public Set<HybridRelationship> getParentRelationships() {
1116        return getHybridParentRelations();
1117    }
1118
1119    /**
1120     * Returns the hybrid child relationships ordered by relationship type, or if equal
1121     * by title cache of the related names.
1122     * @see #getHybridParentRelations()
1123     */
1124    @Transient
1125    public List<HybridRelationship> getOrderedChildRelationships(){
1126        List<HybridRelationship> result = new ArrayList<HybridRelationship>();
1127        result.addAll(this.hybridChildRelations);
1128        Collections.sort(result);
1129        Collections.reverse(result);
1130        return result;
1131
1132    }
1133
1134
1135    /**
1136     * @see #getHybridChildRelations()
1137     * @deprecated use {@link #getHybridChildRelations()} instead. Will be removed in higher versions.
1138     */
1139    @Transient
1140    @Deprecated
1141    public Set<HybridRelationship> getChildRelationships() {
1142        return this.getHybridChildRelations();
1143    }
1144
1145    /**
1146     * Adds the given {@link HybridRelationship hybrid relationship} to the set
1147     * of {@link #getHybridRelationships() hybrid relationships} of both non-viral names
1148     * involved in this hybrid relationship. One of both non-viral names
1149     * must be <i>this</i> non-viral name otherwise no addition will be carried
1150     * out. The {@link eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo() child
1151     * non viral taxon name} must be a hybrid, which means that one of its four hybrid flags must be set.
1152     *
1153     * @param relationship  the hybrid relationship to be added
1154     * @see                             #isHybridFormula()
1155     * @see                             #isMonomHybrid()
1156     * @see                             #isBinomHybrid()
1157     * @see                             #isTrinomHybrid()
1158     * @see                             #getHybridRelationships()
1159     * @see                             #getParentRelationships()
1160     * @see                             #getChildRelationships()
1161     * @see                             #addRelationship(RelationshipBase)
1162     * @throws                          IllegalArgumentException
1163     */
1164    protected void addHybridRelationship(HybridRelationship rel) {
1165        if (rel!=null && rel.getHybridName().equals(this)){
1166            this.hybridChildRelations.add(rel);
1167        }else if(rel!=null && rel.getParentName().equals(this)){
1168            this.hybridParentRelations.add(rel);
1169        }else{
1170            throw new IllegalArgumentException("Hybrid relationship is either null or the relationship does not reference this name");
1171        }
1172    }
1173
1174
1175
1176    /**
1177     * Does the same as the addHybridRelationship method if the given
1178     * {@link common.RelationshipBase relation} is also a {@link HybridRelationship hybrid relationship}.
1179     * Otherwise this method does the same as the overwritten {@link TaxonNameBase#addRelationship(RelationshipBase) addRelationship}
1180     * method from TaxonNameBase.
1181     *
1182     * @param relation  the relationship to be added to some of <i>this</i> taxon name's relationships sets
1183     * @see                     #addHybridRelationship(HybridRelationship)
1184     * @see                     TaxonNameBase#addRelationship(RelationshipBase)
1185     * @see                     TaxonNameBase#addNameRelationship(NameRelationship)
1186     * @deprecated to be used by RelationshipBase only
1187     */
1188    @Override
1189    @Deprecated //to be used by RelationshipBase only
1190    public void addRelationship(RelationshipBase relation) {
1191        if (relation instanceof HybridRelationship){
1192            addHybridRelationship((HybridRelationship)relation);
1193        }else {
1194            super.addRelationship(relation);
1195        }
1196    }
1197
1198    /**
1199     * Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship}
1200     * to <i>this</i> botanical name. A HybridRelationship may be of type
1201     * "is first/second parent" or "is male/female parent". By invoking this
1202     * method <i>this</i> botanical name becomes a hybrid child of the parent
1203     * botanical name.
1204     *
1205     * @param parentName          the botanical name of the parent for this new hybrid name relationship
1206     * @param type                        the type of this new name relationship
1207     * @param ruleConsidered  the string which specifies the rule on which this name relationship is based
1208     * @return
1209     * @see                               #addHybridChild(BotanicalName, HybridRelationshipType,String )
1210     * @see                               #getRelationsToThisName()
1211     * @see                               #getNameRelations()
1212     * @see                               #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
1213     * @see                               #addNameRelationship(NameRelationship)
1214     */
1215    public HybridRelationship addHybridParent(NonViralName parentName, HybridRelationshipType type, String ruleConsidered){
1216        return new HybridRelationship(this, parentName, type, ruleConsidered);
1217    }
1218
1219    /**
1220     * Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship}
1221     * to <i>this</i> botanical name. A HybridRelationship may be of type
1222     * "is first/second parent" or "is male/female parent". By invoking this
1223     * method <i>this</i> botanical name becomes a parent of the hybrid child
1224     * botanical name.
1225     *
1226     * @param childName           the botanical name of the child for this new hybrid name relationship
1227     * @param type                        the type of this new name relationship
1228     * @param ruleConsidered  the string which specifies the rule on which this name relationship is based
1229     * @return
1230     * @see                               #addHybridParent(BotanicalName, HybridRelationshipType,String )
1231     * @see                               #getRelationsToThisName()
1232     * @see                               #getNameRelations()
1233     * @see                               #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
1234     * @see                               #addNameRelationship(NameRelationship)
1235     */
1236    public HybridRelationship addHybridChild(NonViralName childName, HybridRelationshipType type, String ruleConsidered){
1237        return new HybridRelationship(childName, this, type, ruleConsidered);
1238    }
1239
1240
1241    /**
1242     * Removes one {@link HybridRelationship hybrid relationship} from the set of
1243     * {@link #getHybridRelationships() hybrid relationships} in which <i>this</i> botanical taxon name
1244     * is involved. The hybrid relationship will also be removed from the set
1245     * belonging to the second botanical taxon name involved.
1246     *
1247     * @param  relationship  the hybrid relationship which should be deleted from the corresponding sets
1248     * @see                              #getHybridRelationships()
1249     */
1250    public void removeHybridRelationship(HybridRelationship hybridRelation) {
1251        if (hybridRelation == null) {
1252            return;
1253        }
1254
1255        NonViralName parent = hybridRelation.getParentName();
1256        NonViralName child = hybridRelation.getHybridName();
1257
1258        hybridRelation.setHybridName(null);
1259        hybridRelation.setParentName(null);
1260
1261        if (parent != null) {
1262            parent.removeHybridRelationship(hybridRelation);
1263        }
1264
1265        if (child != null) {
1266            child.removeHybridRelationship(hybridRelation);
1267        }
1268
1269        this.hybridChildRelations.remove(hybridRelation);
1270        this.hybridParentRelations.remove(hybridRelation);
1271    }
1272
1273
1274    public void removeHybridChild(NonViralName child) {
1275        Set<HybridRelationship> hybridRelationships = new HashSet<HybridRelationship>();
1276        hybridRelationships.addAll(this.getChildRelationships());
1277        hybridRelationships.addAll(this.getParentRelationships());
1278        for(HybridRelationship hybridRelationship : hybridRelationships) {
1279            // remove name relationship from this side
1280            if (hybridRelationship.getParentName().equals(this) && hybridRelationship.getHybridName().equals(child)) {
1281                this.removeHybridRelationship(hybridRelationship);
1282            }
1283        }
1284    }
1285
1286    public void removeHybridParent(NonViralName parent) {
1287        Set<HybridRelationship> hybridRelationships = new HashSet<HybridRelationship>();
1288        hybridRelationships.addAll(this.getChildRelationships());
1289        hybridRelationships.addAll(this.getParentRelationships());
1290        for(HybridRelationship hybridRelationship : hybridRelationships) {
1291            // remove name relationship from this side
1292            if (hybridRelationship.getParentName().equals(parent) && hybridRelationship.getHybridName().equals(this)) {
1293                this.removeHybridRelationship(hybridRelationship);
1294            }
1295        }
1296    }
1297
1298    /**
1299      * Needs to be implemented by those classes that handle autonyms (e.g. botanical names).
1300      **/
1301    @Transient
1302    public boolean isAutonym(){
1303        return false;
1304    }
1305
1306
1307//      /**
1308//       * Returns the boolean value indicating whether <i>this</i> names rank is Rank "unranked"
1309//       * (uuid = 'a965befb-70a9-4747-a18f-624456c65223') but most likely it is an infrageneric rank
1310//       * due to existing atomized data for the genus epithet and the infrageneric epithet but missing
1311//       * specific epithet.
1312//       * Returns false if <i>this</i> names rank is null.
1313//       *
1314//       * @see  #isSupraGeneric()
1315//       * @see  #isGenus()
1316//       * @see  #isSpeciesAggregate()
1317//       * @see  #isSpecies()
1318//       * @see  #isInfraSpecific()
1319//       */
1320//      @Transient
1321//      public boolean isInfragenericUnranked() {
1322//              Rank rank = this.getRank();
1323//              if (rank == null || ! rank.equals(Rank.UNRANKED())){
1324//                      return false;
1325//              }
1326//              if (StringUtils.isBlank(this.getSpecificEpithet()) && StringUtils.isBlank(this.getInfraSpecificEpithet()) ){
1327//                      return true;
1328//              }else{
1329//                      return false;
1330//              }
1331//      }
1332
1333
1334    /**
1335     * Tests if the given name has any authors.
1336     * @return false if no author ((ex)combination or (ex)basionym) exists, true otherwise
1337     */
1338    public boolean hasAuthors() {
1339        return (this.getCombinationAuthorTeam() != null ||
1340                this.getExCombinationAuthorTeam() != null ||
1341                this.getBasionymAuthorTeam() != null ||
1342                this.getExBasionymAuthorTeam() != null);
1343    }
1344
1345    /**
1346     * Shortcut. Returns the combination authors title cache. Returns null if no combination author exists.
1347     * @return
1348     */
1349    public String computeCombinationAuthorNomenclaturalTitle() {
1350        return computeNomenclaturalTitle(this.getCombinationAuthorTeam());
1351    }
1352
1353    /**
1354     * Shortcut. Returns the basionym authors title cache. Returns null if no basionym author exists.
1355     * @return
1356     */
1357    public String computeBasionymAuthorNomenclaturalTitle() {
1358        return computeNomenclaturalTitle(this.getBasionymAuthorTeam());
1359    }
1360
1361
1362    /**
1363     * Shortcut. Returns the ex-combination authors title cache. Returns null if no ex-combination author exists.
1364     * @return
1365     */
1366    public String computeExCombinationAuthorNomenclaturalTitle() {
1367        return computeNomenclaturalTitle(this.getExCombinationAuthorTeam());
1368    }
1369
1370    /**
1371     * Shortcut. Returns the ex-basionym authors title cache. Returns null if no exbasionym author exists.
1372     * @return
1373     */
1374    public String computeExBasionymAuthorNomenclaturalTitle() {
1375        return computeNomenclaturalTitle(this.getExBasionymAuthorTeam());
1376    }
1377
1378    private String computeNomenclaturalTitle(INomenclaturalAuthor author){
1379        if (author == null){
1380            return null;
1381        }else{
1382            return author.getNomenclaturalTitle();
1383        }
1384    }
1385
1386//*********************** CLONE ********************************************************/
1387
1388    /**
1389     * Clones <i>this</i> non-viral name. This is a shortcut that enables to create
1390     * a new instance that differs only slightly from <i>this</i> non-viral name by
1391     * modifying only some of the attributes.
1392     *
1393     * @see eu.etaxonomy.cdm.model.name.TaxonNameBase#clone()
1394     * @see java.lang.Object#clone()
1395     */
1396    @Override
1397    public Object clone() {
1398        NonViralName result = (NonViralName)super.clone();
1399
1400        //HybridChildRelations
1401        result.hybridChildRelations = new HashSet<HybridRelationship>();
1402        for (HybridRelationship hybridRelationship : getHybridChildRelations()){
1403            HybridRelationship newChildRelationship = (HybridRelationship)hybridRelationship.clone();
1404            newChildRelationship.setRelatedTo(result);
1405            result.hybridChildRelations.add(newChildRelationship);
1406        }
1407
1408        //HybridParentRelations
1409        result.hybridParentRelations = new HashSet<HybridRelationship>();
1410        for (HybridRelationship hybridRelationship : getHybridParentRelations()){
1411            HybridRelationship newParentRelationship = (HybridRelationship)hybridRelationship.clone();
1412            newParentRelationship.setRelatedFrom(result);
1413            result.hybridParentRelations.add(newParentRelationship);
1414        }
1415
1416        //empty caches
1417        if (! protectedNameCache){
1418            result.nameCache = null;
1419        }
1420
1421        //empty caches
1422        if (! protectedAuthorshipCache){
1423            result.authorshipCache = null;
1424        }
1425
1426        //no changes to: basionamyAuthorTeam, combinationAuthorTeam, exBasionymAuthorTeam, exCombinationAuthorTeam
1427        //genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
1428        //protectedAuthorshipCache, protectedNameCache
1429        //binomHybrid, monomHybrid, trinomHybrid, hybridFormula,
1430        return result;
1431    }
1432}
Note: See TracBrowser for help on using the browser.