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