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