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