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