Project

General

Profile

Download (54.1 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 EDIT
3
* European Distributed Institute of Taxonomy 
4
* http://www.e-taxonomy.eu
5
* 
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9

    
10
package eu.etaxonomy.cdm.model.name;
11

    
12

    
13
import java.beans.PropertyChangeEvent;
14
import java.beans.PropertyChangeListener;
15
import java.util.ArrayList;
16
import java.util.Collections;
17
import java.util.HashSet;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.Set;
21

    
22
import javax.persistence.Entity;
23
import javax.persistence.FetchType;
24
import javax.persistence.ManyToOne;
25
import javax.persistence.OneToMany;
26
import javax.persistence.Transient;
27
import javax.validation.constraints.NotNull;
28
import javax.validation.constraints.Pattern;
29
import javax.validation.constraints.Size;
30
import javax.xml.bind.annotation.XmlAccessType;
31
import javax.xml.bind.annotation.XmlAccessorType;
32
import javax.xml.bind.annotation.XmlElement;
33
import javax.xml.bind.annotation.XmlElementWrapper;
34
import javax.xml.bind.annotation.XmlIDREF;
35
import javax.xml.bind.annotation.XmlRootElement;
36
import javax.xml.bind.annotation.XmlSchemaType;
37
import javax.xml.bind.annotation.XmlType;
38

    
39
import org.apache.log4j.Logger;
40
import org.hibernate.annotations.Cascade;
41
import org.hibernate.annotations.CascadeType;
42
import org.hibernate.annotations.Target;
43
import org.hibernate.envers.Audited;
44
import org.hibernate.search.annotations.Field;
45
import org.hibernate.search.annotations.Fields;
46
import org.hibernate.search.annotations.Index;
47
import org.hibernate.search.annotations.Indexed;
48
import org.hibernate.search.annotations.IndexedEmbedded;
49
import org.hibernate.validator.constraints.NotEmpty;
50
import org.springframework.beans.factory.annotation.Configurable;
51

    
52
import eu.etaxonomy.cdm.common.CdmUtils;
53
import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
54
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
55
import eu.etaxonomy.cdm.model.common.CdmBase;
56
import eu.etaxonomy.cdm.model.common.RelationshipBase;
57
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
58
import eu.etaxonomy.cdm.model.reference.Reference;
59
import eu.etaxonomy.cdm.strategy.cache.name.CacheUpdate;
60
import eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy;
61
import eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy;
62
import eu.etaxonomy.cdm.strategy.match.Match;
63
import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
64
import eu.etaxonomy.cdm.strategy.match.MatchMode;
65
import eu.etaxonomy.cdm.strategy.merge.Merge;
66
import eu.etaxonomy.cdm.strategy.merge.MergeMode;
67
import eu.etaxonomy.cdm.validation.Level2;
68
import eu.etaxonomy.cdm.validation.Level3;
69
import eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank;
70
import eu.etaxonomy.cdm.validation.annotation.MustHaveAuthority;
71
import eu.etaxonomy.cdm.validation.annotation.NoDuplicateNames;
72
import 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)
120
public 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_ORPHAN })
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
//	@Transient
877
	public String getTitleCache(){
878
		if(!protectedTitleCache) {
879
		    updateAuthorshipCache();
880
		}
881
		
882
		return super.getTitleCache();
883
	}
884

    
885
	
886
	/**
887
	 * Assigns an authorshipCache string to <i>this</i> non viral taxon name.
888
	 *  
889
	 * @param  authorshipCache  the string which identifies the complete authorship of <i>this</i> non viral taxon name
890
	 * @param  protectedAuthorshipCache if true the isProtectedAuthorshipCache flag is set to <code>true</code>, otherwise 
891
	 * the flag is set to <code>false</code>.
892
	 * @see	   #getAuthorshipCache()
893
	 */
894
	public void setAuthorshipCache(String authorshipCache, boolean protectedAuthorshipCache) {
895
		this.authorshipCache = authorshipCache;
896
		this.setProtectedAuthorshipCache(protectedAuthorshipCache);
897
	}
898

    
899
	public void setTitleCache(String titleCache, boolean protectCache){
900
		super.setTitleCache(titleCache, protectCache);
901
	}
902
	
903
	/**
904
	 * Returns the boolean value "false" since the components of <i>this</i> taxon name
905
	 * cannot follow the rules of a corresponding {@link NomenclaturalCode nomenclatural code}
906
	 * which is not defined for this class. The nomenclature code depends on
907
	 * the concrete name subclass ({@link BacterialName BacterialName},
908
	 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} or
909
	 * {@link ZoologicalName ZoologicalName} to which <i>this</i> non viral taxon name belongs.
910
	 * This method overrides the isCodeCompliant method from the abstract
911
	 * {@link TaxonNameBase#isCodeCompliant() TaxonNameBase} class.
912
	 *  
913
	 * @return  false
914
	 * @see	   	TaxonNameBase#isCodeCompliant()
915
	 */
916
	@Override
917
	@Transient
918
	public boolean isCodeCompliant() {
919
		//FIXME
920
		logger.warn("is CodeCompliant not yet implemented");
921
		return false;
922
	}
923

    
924
	/* (non-Javadoc)
925
	 * @see eu.etaxonomy.cdm.model.name.TaxonNameBase#getNomeclaturalCode()
926
	 */
927
	/**
928
	 * Returns null as {@link NomenclaturalCode nomenclatural code} that governs
929
	 * the construction of <i>this</i> non viral taxon name since there is no specific
930
	 * nomenclatural code defined. The real implementention takes place in the
931
	 * subclasses {@link BacterialName BacterialName},
932
	 * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
933
	 * {@link ZoologicalName ZoologicalName}.
934
	 * This method overrides the getNomeclaturalCode method from {@link TaxonNameBase TaxonNameBase}.
935
	 *
936
	 * @return  null
937
	 * @see  	#isCodeCompliant()
938
	 * @see  	TaxonNameBase#getHasProblem()
939
	 */
940
	@Override
941
	@Transient
942
	public NomenclaturalCode getNomenclaturalCode() {
943
		logger.warn("Non Viral Name has no specific Code defined. Use subclasses");
944
		return null;
945
	}
946

    
947
	/**
948
	 * Returns the boolean value of the flag intended to protect (true)
949
	 * or not (false) the {@link #getAuthorshipCache() authorshipCache} (complete authorship string)
950
	 * of <i>this</i> non viral taxon name.
951
	 *  
952
	 * @return  the boolean value of the protectedAuthorshipCache flag
953
	 * @see     #getAuthorshipCache()
954
	 */
955
	public boolean isProtectedAuthorshipCache() {
956
		return protectedAuthorshipCache;
957
	}
958

    
959
	/** 
960
	 * @see     #isProtectedAuthorshipCache()
961
	 * @see     #getAuthorshipCache()
962
	 */
963
	public void setProtectedAuthorshipCache(boolean protectedAuthorshipCache) {
964
		this.protectedAuthorshipCache = protectedAuthorshipCache;
965
	}
966
	
967

    
968
	/**
969
	 * Returns the boolean value of the flag indicating whether the name of <i>this</i>
970
	 * botanical taxon name is a hybrid formula (true) or not (false). A hybrid
971
	 * named by a hybrid formula (composed with its parent names by placing the
972
	 * multiplication sign between them) does not have an own published name
973
	 * and therefore has neither an {@link NonViralName#getAuthorshipCache() autorship}
974
	 * nor other name components. If this flag is set no other hybrid flags may
975
	 * be set.
976
	 *  
977
	 * @return  the boolean value of the isHybridFormula flag
978
	 * @see		#isMonomHybrid()
979
	 * @see		#isBinomHybrid()
980
	 * @see		#isTrinomHybrid()
981
	 */
982
	public boolean isHybridFormula(){
983
		return this.hybridFormula;
984
	}
985

    
986
	/**
987
	 * @see  #isHybridFormula()
988
	 */
989
	public void setHybridFormula(boolean hybridFormula){
990
		this.hybridFormula = hybridFormula;
991
	}
992

    
993
	/**
994
	 * Returns the boolean value of the flag indicating whether <i>this</i> botanical
995
	 * taxon name is the name of an intergeneric hybrid (true) or not (false).
996
	 * In this case the multiplication sign is placed before the scientific
997
	 * name. If this flag is set no other hybrid flags may be set.
998
	 *  
999
	 * @return  the boolean value of the isMonomHybrid flag
1000
	 * @see		#isHybridFormula()
1001
	 * @see		#isBinomHybrid()
1002
	 * @see		#isTrinomHybrid()
1003
	 */
1004
	public boolean isMonomHybrid(){
1005
		return this.monomHybrid;
1006
	}
1007

    
1008
	/**
1009
	 * @see  #isMonomHybrid()
1010
	 * @see	 #isBinomHybrid()
1011
	 * @see	 #isTrinomHybrid()
1012
	 */
1013
	public void setMonomHybrid(boolean monomHybrid){
1014
		this.monomHybrid = monomHybrid;
1015
	}
1016

    
1017
	/**
1018
	 * Returns the boolean value of the flag indicating whether <i>this</i> botanical
1019
	 * taxon name is the name of an interspecific hybrid (true) or not (false).
1020
	 * In this case the multiplication sign is placed before the species
1021
	 * epithet. If this flag is set no other hybrid flags may be set.
1022
	 *  
1023
	 * @return  the boolean value of the isBinomHybrid flag
1024
	 * @see		#isHybridFormula()
1025
	 * @see		#isMonomHybrid()
1026
	 * @see		#isTrinomHybrid()
1027
	 */
1028
	public boolean isBinomHybrid(){
1029
		return this.binomHybrid;
1030
	}
1031

    
1032
	/**
1033
	 * @see	 #isBinomHybrid()
1034
	 * @see  #isMonomHybrid()
1035
	 * @see	 #isTrinomHybrid()
1036
	 */
1037
	public void setBinomHybrid(boolean binomHybrid){
1038
		this.binomHybrid = binomHybrid;
1039
	}
1040

    
1041
	/**
1042
	 * Returns the boolean value of the flag indicating whether <i>this</i> botanical
1043
	 * taxon name is the name of an infraspecific hybrid (true) or not (false).
1044
	 * In this case the term "notho-" (optionally abbreviated "n-") is used as
1045
	 * a prefix to the term denoting the infraspecific rank of <i>this</i> botanical
1046
	 * taxon name. If this flag is set no other hybrid flags may be set.
1047
	 *  
1048
	 * @return  the boolean value of the isTrinomHybrid flag
1049
	 * @see		#isHybridFormula()
1050
	 * @see		#isMonomHybrid()
1051
	 * @see		#isBinomHybrid()
1052
	 */
1053
	public boolean isTrinomHybrid(){
1054
		return this.trinomHybrid;
1055
	}
1056

    
1057
	/**
1058
	 * @see	 #isTrinomHybrid()
1059
	 * @see	 #isBinomHybrid()
1060
	 * @see  #isMonomHybrid()
1061
	 */
1062
	public void setTrinomHybrid(boolean trinomHybrid){
1063
		this.trinomHybrid = trinomHybrid;
1064
	}
1065
	
1066

    
1067
	/** 
1068
	 * Returns the set of all {@link HybridRelationship hybrid relationships}
1069
	 * in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedFrom() parent}.
1070
	 *  
1071
	 * @see    #getHybridRelationships()
1072
	 * @see    #getChildRelationships()
1073
	 * @see    HybridRelationshipType
1074
	 */
1075
    public Set<HybridRelationship> getHybridParentRelations() {
1076
		if(hybridParentRelations == null) {
1077
			this.hybridParentRelations = new HashSet<HybridRelationship>();
1078
		}
1079
		return hybridParentRelations;
1080
	}
1081

    
1082
	private void setHybridParentRelations(Set<HybridRelationship> hybridParentRelations) {
1083
		this.hybridParentRelations = hybridParentRelations;
1084
	}
1085

    
1086
	
1087
	/** 
1088
	 * Returns the set of all {@link HybridRelationship hybrid relationships}
1089
	 * in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedTo() child}.
1090
	 *  
1091
	 * @see    #getHybridRelationships()
1092
	 * @see    #getParentRelationships()
1093
	 * @see    HybridRelationshipType
1094
	 */
1095
	public Set<HybridRelationship> getHybridChildRelations() {
1096
		if(hybridChildRelations == null) {
1097
			this.hybridChildRelations = new HashSet<HybridRelationship>();
1098
		}
1099
		return hybridChildRelations;
1100
	}
1101

    
1102
	private void setHybridChildRelations(Set<HybridRelationship> hybridChildRelations) {
1103
		this.hybridChildRelations = hybridChildRelations;
1104
	}
1105
	
1106
	/** 
1107
	 * Returns the set of all {@link HybridRelationship hybrid relationships}
1108
	 * in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedFrom() parent}.
1109
	 * @see 	#getHybridParentRelations()
1110
	 * @see    	#getHybridRelationships()
1111
	 * @see    	#getChildRelationships()
1112
	 * @see    	HybridRelationshipType
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
	 * @return
1138
	 */
1139
	@Transient
1140
	public Set<HybridRelationship> getChildRelationships() {
1141
		return this.getHybridChildRelations();
1142
	}
1143

    
1144
	/**
1145
	 * Adds the given {@link HybridRelationship hybrid relationship} to the set
1146
	 * of {@link #getHybridRelationships() hybrid relationships} of both non viral taxon names
1147
	 * involved in this hybrid relationship. One of both non viral taxon names
1148
	 * must be <i>this</i> botanical taxon name otherwise no addition will be carried
1149
	 * out. The {@link eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo() child non viral taxon name}
1150
	 * must be a hybrid, which means that one of its four hybrid flags must be set.
1151
	 * 
1152
	 * @param relationship  the hybrid relationship to be added
1153
	 * @see    				#isHybridFormula()
1154
	 * @see    				#isMonomHybrid()
1155
	 * @see    				#isBinomHybrid()
1156
	 * @see    				#isTrinomHybrid()
1157
	 * @see    				#getHybridRelationships()
1158
	 * @see    				#getParentRelationships()
1159
	 * @see    				#getChildRelationships()
1160
	 * @see    				#addRelationship(RelationshipBase)
1161
	 * @throws 				IllegalArgumentException
1162
	 */
1163
	protected void addHybridRelationship(HybridRelationship rel) {
1164
		if (rel!=null && rel.getHybridName().equals(this)){
1165
			this.hybridChildRelations.add(rel);
1166
		}else if(rel!=null && rel.getParentName().equals(this)){
1167
			this.hybridParentRelations.add(rel);			
1168
		}else{
1169
			throw new IllegalArgumentException("Hybrid relationship is either null or the relationship does not reference this name");
1170
		}
1171
	}
1172

    
1173
	
1174

    
1175
	/**
1176
	 * Does the same as the addHybridRelationship method if the given
1177
	 * {@link common.RelationshipBase relation} is also a {@link HybridRelationship hybrid relationship}.
1178
	 * Otherwise this method does the same as the overwritten {@link TaxonNameBase#addRelationship(RelationshipBase) addRelationship}
1179
	 * method from TaxonNameBase.
1180
	 * 
1181
	 * @param relation  the relationship to be added to some of <i>this</i> taxon name's relationships sets
1182
	 * @see    	   		#addHybridRelationship(HybridRelationship)
1183
	 * @see    	   		TaxonNameBase#addRelationship(RelationshipBase)
1184
	 * @see    	   		TaxonNameBase#addNameRelationship(NameRelationship)
1185
	 * @deprecated to be used by RelationshipBase only
1186
	 */
1187
	@Override
1188
	@Deprecated //to be used by RelationshipBase only
1189
	public void addRelationship(RelationshipBase relation) {
1190
		if (relation instanceof HybridRelationship){
1191
			addHybridRelationship((HybridRelationship)relation);
1192
		}else {
1193
			super.addRelationship(relation);
1194
		}
1195
	}
1196
	
1197
	/**
1198
	 * Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship} 
1199
	 * to <i>this</i> botanical name. A HybridRelationship may be of type
1200
	 * "is first/second parent" or "is male/female parent". By invoking this
1201
	 * method <i>this</i> botanical name becomes a hybrid child of the parent
1202
	 * botanical name.
1203
	 * 
1204
	 * @param parentName	  the botanical name of the parent for this new hybrid name relationship
1205
	 * @param type			  the type of this new name relationship
1206
	 * @param ruleConsidered  the string which specifies the rule on which this name relationship is based
1207
	 * @return 
1208
	 * @see    				  #addHybridChild(BotanicalName, HybridRelationshipType,String )
1209
	 * @see    				  #getRelationsToThisName()
1210
	 * @see    				  #getNameRelations()
1211
	 * @see    				  #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
1212
	 * @see    				  #addNameRelationship(NameRelationship)
1213
	 */
1214
	public HybridRelationship addHybridParent(NonViralName parentName, HybridRelationshipType type, String ruleConsidered){
1215
		return new HybridRelationship(this, parentName, type, ruleConsidered);
1216
	}
1217
	
1218
	/**
1219
	 * Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship} 
1220
	 * to <i>this</i> botanical name. A HybridRelationship may be of type
1221
	 * "is first/second parent" or "is male/female parent". By invoking this
1222
	 * method <i>this</i> botanical name becomes a parent of the hybrid child
1223
	 * botanical name.
1224
	 * 
1225
	 * @param childName		  the botanical name of the child for this new hybrid name relationship
1226
	 * @param type			  the type of this new name relationship
1227
	 * @param ruleConsidered  the string which specifies the rule on which this name relationship is based
1228
	 * @return 
1229
	 * @see    				  #addHybridParent(BotanicalName, HybridRelationshipType,String )
1230
	 * @see    				  #getRelationsToThisName()
1231
	 * @see    				  #getNameRelations()
1232
	 * @see    				  #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
1233
	 * @see    				  #addNameRelationship(NameRelationship)
1234
	 */
1235
	public HybridRelationship addHybridChild(NonViralName childName, HybridRelationshipType type, String ruleConsidered){
1236
		return new HybridRelationship(childName, this, type, ruleConsidered);
1237
	}
1238
	
1239
	
1240
	/** 
1241
	 * Removes one {@link HybridRelationship hybrid relationship} from the set of
1242
	 * {@link #getHybridRelationships() hybrid relationships} in which <i>this</i> botanical taxon name
1243
	 * is involved. The hybrid relationship will also be removed from the set
1244
	 * belonging to the second botanical taxon name involved. 
1245
	 *
1246
	 * @param  relationship  the hybrid relationship which should be deleted from the corresponding sets
1247
	 * @see    				 #getHybridRelationships()
1248
	 */
1249
	public void removeHybridRelationship(HybridRelationship hybridRelation) {
1250
		if (hybridRelation == null) {
1251
			return;
1252
		}
1253
		
1254
		NonViralName parent = hybridRelation.getParentName();
1255
		NonViralName child = hybridRelation.getHybridName();
1256

    
1257
		hybridRelation.setHybridName(null);
1258
		hybridRelation.setParentName(null);
1259

    
1260
		if (parent != null) {
1261
			parent.removeHybridRelationship(hybridRelation);
1262
		}
1263
		
1264
		if (child != null) {
1265
			child.removeHybridRelationship(hybridRelation);
1266
		}
1267
		
1268
		this.hybridChildRelations.remove(hybridRelation);
1269
		this.hybridParentRelations.remove(hybridRelation);
1270
	}
1271

    
1272
	
1273
	public void removeHybridChild(NonViralName child) {
1274
		Set<HybridRelationship> hybridRelationships = new HashSet<HybridRelationship>();
1275
		hybridRelationships.addAll(this.getChildRelationships());
1276
		hybridRelationships.addAll(this.getParentRelationships());
1277
		for(HybridRelationship hybridRelationship : hybridRelationships) {
1278
			// remove name relationship from this side 
1279
			if (hybridRelationship.getParentName().equals(this) && hybridRelationship.getHybridName().equals(child)) {
1280
				this.removeHybridRelationship(hybridRelationship);
1281
			}
1282
		}
1283
	}
1284
	
1285
	public void removeHybridParent(NonViralName parent) {
1286
		Set<HybridRelationship> hybridRelationships = new HashSet<HybridRelationship>();
1287
		hybridRelationships.addAll(this.getChildRelationships());
1288
		hybridRelationships.addAll(this.getParentRelationships());
1289
		for(HybridRelationship hybridRelationship : hybridRelationships) {
1290
			// remove name relationship from this side 
1291
			if (hybridRelationship.getParentName().equals(parent) && hybridRelationship.getHybridName().equals(this)) {
1292
				this.removeHybridRelationship(hybridRelationship);
1293
			}
1294
		}
1295
	}
1296
	
1297
	/**
1298
	  * Needs to be implemented by those classes that handle autonyms (e.g. botanical names).
1299
	  **/
1300
	@Transient
1301
	public boolean isAutonym(){
1302
		return false;
1303
	}
1304
	
1305
	
1306
//	/**
1307
//	 * Returns the boolean value indicating whether <i>this</i> names rank is Rank "unranked"
1308
//	 * (uuid = 'a965befb-70a9-4747-a18f-624456c65223') but most likely it is an infrageneric rank
1309
//	 * due to existing atomized data for the genus epithet and the infrageneric epithet but missing
1310
//	 * specific epithet.
1311
//	 * Returns false if <i>this</i> names rank is null.
1312
//	 *
1313
//	 * @see  #isSupraGeneric()
1314
//	 * @see  #isGenus()
1315
//	 * @see  #isSpeciesAggregate()
1316
//	 * @see  #isSpecies()
1317
//	 * @see  #isInfraSpecific()
1318
//	 */
1319
//	@Transient
1320
//	public boolean isInfragenericUnranked() {
1321
//		Rank rank = this.getRank();
1322
//		if (rank == null || ! rank.equals(Rank.UNRANKED())){
1323
//			return false;
1324
//		}
1325
//		if (StringUtils.isBlank(this.getSpecificEpithet()) && StringUtils.isBlank(this.getInfraSpecificEpithet()) ){
1326
//			return true;
1327
//		}else{
1328
//			return false;
1329
//		}
1330
//	}
1331
	
1332

    
1333
	/**
1334
	 * Tests if the given name has any authors.
1335
	 * @return false if no author ((ex)combination or (ex)basionym) exists, true otherwise
1336
	 */
1337
	public boolean hasAuthors() {
1338
		return (this.getCombinationAuthorTeam() != null || 
1339
				this.getExCombinationAuthorTeam() != null ||
1340
				this.getBasionymAuthorTeam() != null ||
1341
				this.getExBasionymAuthorTeam() != null);
1342
	}
1343
	
1344
	/**
1345
	 * Shortcut. Returns the combination authors title cache. Returns null if no combination author exists.
1346
	 * @return
1347
	 */
1348
	public String computeCombinationAuthorNomenclaturalTitle() {
1349
		return computeNomenclaturalTitle(this.getCombinationAuthorTeam());
1350
	}
1351

    
1352
	/**
1353
	 * Shortcut. Returns the basionym authors title cache. Returns null if no basionym author exists.
1354
	 * @return
1355
	 */
1356
	public String computeBasionymAuthorNomenclaturalTitle() {
1357
		return computeNomenclaturalTitle(this.getBasionymAuthorTeam());
1358
	}
1359
	
1360
	
1361
	/**
1362
	 * Shortcut. Returns the ex-combination authors title cache. Returns null if no ex-combination author exists.
1363
	 * @return
1364
	 */
1365
	public String computeExCombinationAuthorNomenclaturalTitle() {
1366
		return computeNomenclaturalTitle(this.getExCombinationAuthorTeam());
1367
	}
1368

    
1369
	/**
1370
	 * Shortcut. Returns the ex-basionym authors title cache. Returns null if no exbasionym author exists.
1371
	 * @return
1372
	 */
1373
	public String computeExBasionymAuthorNomenclaturalTitle() {
1374
		return computeNomenclaturalTitle(this.getExBasionymAuthorTeam());
1375
	}
1376
	
1377
	private String computeNomenclaturalTitle(INomenclaturalAuthor author){
1378
		if (author == null){
1379
			return null;
1380
		}else{
1381
			return author.getNomenclaturalTitle();
1382
		}
1383
	}
1384
	
1385
//*********************** CLONE ********************************************************/
1386

    
1387
	/** 
1388
	 * Clones <i>this</i> non-viral name. This is a shortcut that enables to create
1389
	 * a new instance that differs only slightly from <i>this</i> non-viral name by
1390
	 * modifying only some of the attributes.
1391
	 * 
1392
	 * @see eu.etaxonomy.cdm.model.name.TaxonNameBase#clone()
1393
	 * @see java.lang.Object#clone()
1394
	 */
1395
	@Override
1396
	public Object clone() {
1397
		NonViralName result = (NonViralName)super.clone();
1398
		
1399
		//HybridChildRelations
1400
		result.hybridChildRelations = new HashSet<HybridRelationship>();
1401
		for (HybridRelationship hybridRelationship : getHybridChildRelations()){
1402
			HybridRelationship newChildRelationship = (HybridRelationship)hybridRelationship.clone();
1403
			newChildRelationship.setRelatedTo(result);
1404
			result.hybridChildRelations.add(newChildRelationship);
1405
		}
1406
		
1407
		//HybridParentRelations
1408
		result.hybridParentRelations = new HashSet<HybridRelationship>();
1409
		for (HybridRelationship hybridRelationship : getHybridParentRelations()){
1410
			HybridRelationship newParentRelationship = (HybridRelationship)hybridRelationship.clone();
1411
			newParentRelationship.setRelatedFrom(result);
1412
			result.hybridParentRelations.add(newParentRelationship);
1413
		}
1414

    
1415
		//empty caches
1416
		if (! protectedNameCache){
1417
			result.nameCache = null;
1418
		}
1419

    
1420
		//empty caches
1421
		if (! protectedAuthorshipCache){
1422
			result.authorshipCache = null;
1423
		}
1424

    
1425
		//no changes to: basionamyAuthorTeam, combinationAuthorTeam, exBasionymAuthorTeam, exCombinationAuthorTeam
1426
		//genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
1427
		//protectedAuthorshipCache, protectedNameCache
1428
		//binomHybrid, monomHybrid, trinomHybrid, hybridFormula,
1429
		return result;
1430
	}
1431
}
(15-15/26)