cleanup
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / taxon / Taxon.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.taxon;
11
12
13 import java.lang.reflect.Field;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.Comparator;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23
24 import javax.persistence.Entity;
25 import javax.persistence.FetchType;
26 import javax.persistence.OneToMany;
27 import javax.persistence.Transient;
28 import javax.validation.Valid;
29 import javax.validation.constraints.NotNull;
30 import javax.xml.bind.annotation.XmlAccessType;
31 import javax.xml.bind.annotation.XmlAccessorType;
32 import javax.xml.bind.annotation.XmlAttribute;
33 import javax.xml.bind.annotation.XmlElement;
34 import javax.xml.bind.annotation.XmlElementWrapper;
35 import javax.xml.bind.annotation.XmlIDREF;
36 import javax.xml.bind.annotation.XmlRootElement;
37 import javax.xml.bind.annotation.XmlSchemaType;
38 import javax.xml.bind.annotation.XmlType;
39
40 import org.apache.log4j.Logger;
41 import org.hibernate.annotations.Cascade;
42 import org.hibernate.annotations.CascadeType;
43 import org.hibernate.envers.Audited;
44 import org.hibernate.search.annotations.ClassBridge;
45 import org.hibernate.search.annotations.ClassBridges;
46 import org.hibernate.search.annotations.ContainedIn;
47 import org.hibernate.search.annotations.Indexed;
48 import org.hibernate.search.annotations.IndexedEmbedded;
49 import org.springframework.beans.factory.annotation.Configurable;
50 import org.springframework.util.ReflectionUtils;
51
52 import eu.etaxonomy.cdm.hibernate.search.GroupByTaxonClassBridge;
53 import eu.etaxonomy.cdm.hibernate.search.TaxonRelationshipClassBridge;
54 import eu.etaxonomy.cdm.model.common.IRelated;
55 import eu.etaxonomy.cdm.model.common.RelationshipBase;
56 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
57 import eu.etaxonomy.cdm.model.description.DescriptionType;
58 import eu.etaxonomy.cdm.model.description.IDescribable;
59 import eu.etaxonomy.cdm.model.description.TaxonDescription;
60 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
61 import eu.etaxonomy.cdm.model.name.ITaxonNameBase;
62 import eu.etaxonomy.cdm.model.name.TaxonName;
63 import eu.etaxonomy.cdm.model.reference.ICdmTarget;
64 import eu.etaxonomy.cdm.model.reference.Reference;
65 import eu.etaxonomy.cdm.strategy.cache.taxon.ITaxonCacheStrategy;
66 import eu.etaxonomy.cdm.strategy.cache.taxon.TaxonBaseDefaultCacheStrategy;
67
68 /**
69 * The class for "accepted/correct" {@link TaxonBase taxa} (only these taxa according to
70 * the opinion of the {@link eu.etaxonomy.cdm.model.reference.Reference reference} can build a classification).
71 * An {@link java.lang.Iterable interface} is supported to iterate through taxonomic children.<BR>
72 * Splitting taxa in "accepted/correct" and {@link Synonym "synonyms"} makes it easier to handle
73 * particular relationships between ("accepted/correct") taxa on the one hand
74 * and between ("synonym") taxa and ("accepted/correct") taxa on the other.
75 *
76 * @author m.doering
77 * @since 08-Nov-2007 13:06:56
78 */
79 @XmlAccessorType(XmlAccessType.FIELD)
80 @XmlType(name = "Taxon", propOrder = {
81 "taxonNodes",
82 "synonyms",
83 "relationsFromThisTaxon",
84 "relationsToThisTaxon",
85 "descriptions"
86 })
87 @XmlRootElement(name = "Taxon")
88 @Entity
89 @Indexed(index = "eu.etaxonomy.cdm.model.taxon.TaxonBase")
90 @Audited
91 @Configurable
92 @ClassBridges({
93 @ClassBridge(impl = GroupByTaxonClassBridge.class),
94 @ClassBridge(impl = TaxonRelationshipClassBridge.class)
95 })
96 public class Taxon
97 extends TaxonBase<ITaxonCacheStrategy<Taxon>>
98 implements IRelated<RelationshipBase>, IDescribable<TaxonDescription>, ICdmTarget{
99
100 private static final long serialVersionUID = -584946869762749006L;
101 private static final Logger logger = Logger.getLogger(Taxon.class);
102
103 private static final TaxonComparator defaultTaxonComparator = new TaxonComparator();
104
105 @XmlElementWrapper(name = "Descriptions")
106 @XmlElement(name = "Description")
107 @OneToMany(mappedBy="taxon", fetch= FetchType.LAZY)
108 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
109 @NotNull
110 @ContainedIn
111 private Set<TaxonDescription> descriptions = new HashSet<>();
112
113 // all related synonyms
114 @XmlElementWrapper(name = "Synonyms")
115 @XmlElement(name = "Synonym")
116 @XmlIDREF
117 @XmlSchemaType(name = "IDREF")
118 @OneToMany(mappedBy="acceptedTaxon", fetch=FetchType.LAZY, orphanRemoval=false) //we allow synonyms to stay on their own for dirty data and for intermediate states during e.g. imports
119 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
120 @NotNull
121 @Valid
122 @ContainedIn
123 private Set<Synonym> synonyms = new HashSet<>();
124
125 // all taxa relations with rel.fromTaxon==this
126 @XmlElementWrapper(name = "RelationsFromThisTaxon")
127 @XmlElement(name = "FromThisTaxonRelationship")
128 @OneToMany(mappedBy="relatedFrom", fetch=FetchType.LAZY, orphanRemoval=true)
129 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
130 @NotNull
131 // @Valid
132 @ContainedIn
133 private Set<TaxonRelationship> relationsFromThisTaxon = new HashSet<>();
134
135 // all taxa relations with rel.toTaxon==this
136 @XmlElementWrapper(name = "RelationsToThisTaxon")
137 @XmlElement(name = "ToThisTaxonRelationship")
138 @XmlIDREF
139 @XmlSchemaType(name = "IDREF")
140 @OneToMany(mappedBy="relatedTo", fetch=FetchType.LAZY, orphanRemoval=true)
141 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
142 // @Valid
143 @ContainedIn
144 private Set<TaxonRelationship> relationsToThisTaxon = new HashSet<>();
145
146 @XmlAttribute(name= "taxonStatusUnknown")
147 private boolean taxonStatusUnknown = false;
148 /**
149 * The status of this taxon is unknown it could also be some kind of synonym.
150 * @return the taxonStatusUnknown
151 */
152 public boolean isTaxonStatusUnknown() {return taxonStatusUnknown;}
153 /** @see #isTaxonStatusUnknown()*/
154 public void setTaxonStatusUnknown(boolean taxonStatusUnknown) {this.taxonStatusUnknown = taxonStatusUnknown;}
155
156 @XmlElementWrapper(name = "taxonNodes")
157 @XmlElement(name = "taxonNode")
158 @XmlIDREF
159 @XmlSchemaType(name = "IDREF")
160 @OneToMany(mappedBy="taxon", fetch=FetchType.LAZY)
161 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
162 @IndexedEmbedded
163 private Set<TaxonNode> taxonNodes = new HashSet<>();
164
165 // ************************* FACTORY METHODS ********************************/
166
167 /**
168 * @see #NewInstance(TaxonName, Reference)
169 * @param taxonName
170 * @param sec
171 * @return
172 */
173 public static Taxon NewInstance(ITaxonNameBase taxonName, Reference sec){
174 return NewInstance(TaxonName.castAndDeproxy(taxonName), sec);
175 }
176
177 /**
178 * Creates a new (accepted/valid) taxon instance with
179 * the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
180 * using it.
181 *
182 * @param taxonName the taxon name used
183 * @param sec the reference using the taxon name
184 * @see #Taxon(TaxonName, Reference)
185 */
186 public static Taxon NewInstance(TaxonName taxonName, Reference sec){
187 Taxon result = new Taxon(taxonName, sec);
188 return result;
189 }
190
191 /**
192 * Creates a new Taxon for the given name, secundum reference and secundum detail
193 * @param taxonName
194 * @param sec
195 * @param secMicroReference
196 * @see #
197 */
198 public static Taxon NewInstance(TaxonName taxonName, Reference sec, String secMicroReference){
199 Taxon result = new Taxon(taxonName, sec, secMicroReference);
200 return result;
201 }
202
203 /**
204 * Creates a new taxon instance with an unknown status (accepted/synonym) and with
205 * the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
206 * using it.
207 *
208 * @param taxonName the taxon name used
209 * @param sec the reference using the taxon name
210 * @see #Taxon(TaxonName, Reference)
211 */
212 public static Taxon NewUnknownStatusInstance(TaxonName taxonName, Reference sec){
213 Taxon result = new Taxon(taxonName, sec);
214 result.setTaxonStatusUnknown(true);
215 return result;
216 }
217 // ************* CONSTRUCTORS *************/
218
219 //TODO should be private, but still produces Spring init errors
220 @Deprecated
221 public Taxon(){
222 this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<>();
223 }
224
225 private Taxon(TaxonName taxonName, Reference sec){
226 super(taxonName, sec, null);
227 this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<>();
228 }
229
230 private Taxon(TaxonName taxonName, Reference sec, String secMicroReference){
231 super(taxonName, sec, secMicroReference);
232 this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Taxon>();
233 }
234
235 //********* METHODS **************************************/
236
237 /**
238 * Returns the set of {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon descriptions}
239 * concerning <i>this</i> taxon.
240 *
241 * @see #removeDescription(TaxonDescription)
242 * @see #addDescription(TaxonDescription)
243 * @see eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
244 */
245 @Override
246 public Set<TaxonDescription> getDescriptions() {
247 if(descriptions == null) {
248 descriptions = new HashSet<>();
249 }
250 return descriptions;
251 }
252
253 public Set<TaxonDescription> getDescriptions(DescriptionType type) {
254 Set<TaxonDescription> result = new HashSet<>();
255 for (TaxonDescription description : getDescriptions()){
256 if (description.getTypes().contains(type)){
257 result.add(description);
258 }
259 }
260 return result;
261 }
262
263 /**
264 * Adds a new {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon description} to the set
265 * of taxon descriptions assigned to <i>this</i> (accepted/correct) taxon.
266 * Due to bidirectionality the content of the {@link eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon() taxon attribute} of the
267 * taxon description itself will be replaced with <i>this</i> taxon. The taxon
268 * description will also be removed from the set of taxon descriptions
269 * assigned to its previous taxon.
270 *
271 * @param description the taxon description to be added for <i>this</i> taxon
272 * @see #getDescriptions()
273 * @see #removeDescription(TaxonDescription)
274 * @see eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
275 */
276 @Override
277 public void addDescription(TaxonDescription description) {
278 if (description.getTaxon() != null){
279 description.getTaxon().removeDescription(description);
280 }
281 Field field = ReflectionUtils.findField(TaxonDescription.class, "taxon", Taxon.class);
282 ReflectionUtils.makeAccessible(field);
283 ReflectionUtils.setField(field, description, this);
284 descriptions.add(description);
285
286 }
287
288 /**
289 * Removes one element from the set of {@link eu.etaxonomy.cdm.model.description.TaxonDescription taxon descriptions} assigned
290 * to <i>this</i> (accepted/correct) taxon. Due to bidirectionality the content of
291 * the {@link eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon() taxon attribute} of the taxon description
292 * itself will be set to "null".
293 *
294 * @param description the taxon description which should be removed
295 * @see #getDescriptions()
296 * @see #addDescription(TaxonDescription)
297 * @see eu.etaxonomy.cdm.model.description.TaxonDescription#getTaxon()
298 */
299 @Override
300 public void removeDescription(TaxonDescription description) {
301 //description.setTaxon(null) for not visible method
302 Field field = ReflectionUtils.findField(TaxonDescription.class, "taxon", Taxon.class);
303 ReflectionUtils.makeAccessible(field);
304 ReflectionUtils.setField(field, description, null);
305 descriptions.remove(description);
306 }
307
308 public void removeDescription(TaxonDescription description, boolean removeElements){
309 if (removeElements){
310 Set<DescriptionElementBase> elements = new HashSet<DescriptionElementBase>(description.getElements());
311 for (DescriptionElementBase el:elements){
312 description.getElements().remove(el);
313 }
314 removeDescription(description);
315 } else{
316 removeDescription(description);
317 }
318 }
319
320 /**
321 * Returns the image gallery for a taxon. If there are multiple taxon descriptions
322 * marked as image galleries an arbitrary one is chosen.
323 * If no image gallery exists, a new one is created if <code>createNewIfNotExists</code>
324 * is <code>true</code>.
325 * @param createNewIfNotExists
326 * @return
327 */
328 public TaxonDescription getImageGallery(boolean createNewIfNotExists) {
329 TaxonDescription result = null;
330 Set<TaxonDescription> descriptions= getDescriptions();
331 for (TaxonDescription description : descriptions){
332 if (description.isImageGallery()){
333 result = description;
334 break;
335 }
336 }
337 if (result == null && createNewIfNotExists){
338 result = TaxonDescription.NewInstance(this);
339 result.setImageGallery(true);
340 }
341 return result;
342 }
343
344
345
346 public Set<TaxonNode> getTaxonNodes() {
347 return taxonNodes;
348 }
349 // protected void setTaxonNodes(Set<TaxonNode> taxonNodes) {
350 // this.taxonNodes = taxonNodes;
351 // }
352 protected void addTaxonNode(TaxonNode taxonNode){
353 taxonNodes.add(taxonNode);
354 }
355
356 public boolean removeTaxonNode(TaxonNode taxonNode){
357 if (!taxonNodes.contains(taxonNode)){
358 return false;
359 }
360 TaxonNode parent = taxonNode.getParent();
361 if (parent != null){
362 parent.removeChildNode(taxonNode);
363 }
364 taxonNode.setTaxon(null);
365 return taxonNodes.remove(taxonNode);
366
367 }
368
369 public boolean removeTaxonNode(TaxonNode taxonNode, boolean deleteChildren){
370 TaxonNode parent = taxonNode.getParent();
371 boolean success = true;
372
373 if ((!taxonNode.getChildNodes().isEmpty() && deleteChildren) || (taxonNode.getChildNodes().isEmpty()) ){
374
375 taxonNode.delete();
376
377 } else if (!taxonNode.isTopmostNode()){
378
379 List<TaxonNode> nodes = new ArrayList<TaxonNode> (taxonNode.getChildNodes());
380 for (TaxonNode childNode: nodes){
381 taxonNode.getChildNodes().remove(childNode);
382 parent.addChildNode(childNode, null, null);
383 }
384
385 taxonNode.delete();
386
387 } else if (taxonNode.isTopmostNode()){
388 success = false;
389 }
390 return success;
391 }
392
393 public boolean removeTaxonNodes(boolean deleteChildren){
394 Iterator<TaxonNode> nodesIterator = taxonNodes.iterator();
395 TaxonNode node;
396 TaxonNode parent;
397 boolean success = false;
398 List<TaxonNode> removeNodes = new ArrayList<>();
399 while (nodesIterator.hasNext()){
400 node = nodesIterator.next();
401 if (!deleteChildren){
402 List<TaxonNode> children = node.getChildNodes();
403 Iterator<TaxonNode> childrenIterator = children.iterator();
404 parent = node.getParent();
405 while (childrenIterator.hasNext()){
406 TaxonNode childNode = childrenIterator.next();
407 if (parent != null){
408 parent.addChildNode(childNode, null, null);
409 }else{
410 childNode.setParent(null);
411 }
412 }
413
414 for (int i = 0; i<node.getChildNodes().size(); i++){
415 node.removeChild(i);
416 }
417 }
418
419 removeNodes.add(node);
420 }
421 for (int i = 0; i<removeNodes.size(); i++){
422 TaxonNode removeNode = removeNodes.get(i);
423 success = removeNode.delete(deleteChildren);
424 removeNode.setTaxon(null);
425 removeTaxonNode(removeNode);
426 }
427 return success;
428
429 }
430
431 public TaxonNode getTaxonNode(Classification classification) {
432 if (classification == null){
433 return null;
434 }
435 for (TaxonNode node : this.getTaxonNodes()){
436 if (classification.equals(node.getClassification())){
437 return node;
438 }
439 }
440 return null;
441 }
442
443 /**
444 * Returns the set of all {@link Synonym synonyms}
445 * for which <i>this</i> ("accepted/valid") taxon is the accepted taxon.
446 *
447 * @see #addSynonym(Synonym, SynonymType)
448 * @see #removeSynonym(Synonym)
449 */
450 public Set<Synonym> getSynonyms() {
451 if(synonyms == null) {
452 this.synonyms = new HashSet<>();
453 }
454 return synonyms;
455 }
456
457 /**
458 * Returns the set of all {@link TaxonRelationship taxon relationships}
459 * between two taxa in which <i>this</i> taxon is involved as a source.
460 *
461 * @see #getRelationsToThisTaxon()
462 * @see #getTaxonRelations()
463 */
464 public Set<TaxonRelationship> getRelationsFromThisTaxon() {
465 if(relationsFromThisTaxon == null) {
466 this.relationsFromThisTaxon = new HashSet<>();
467 }
468 return relationsFromThisTaxon;
469 }
470
471 /**
472 * Returns the set of all {@link TaxonRelationship taxon relationships}
473 * between two taxa in which <i>this</i> taxon is involved as a target.
474 *
475 * @see #getRelationsFromThisTaxon()
476 * @see #getTaxonRelations()
477 */
478 public Set<TaxonRelationship> getRelationsToThisTaxon() {
479 if(relationsToThisTaxon == null) {
480 this.relationsToThisTaxon = new HashSet<>();
481 }
482 return relationsToThisTaxon;
483 }
484
485 /**
486 * Returns the set of all {@link TaxonRelationship taxon relationships}
487 * between two taxa in which <i>this</i> taxon is involved either as a source or
488 * as a target.
489 *
490 * @see #getRelationsFromThisTaxon()
491 * @see #getRelationsToThisTaxon()
492 */
493 @Transient
494 public Set<TaxonRelationship> getTaxonRelations() {
495 Set<TaxonRelationship> rels = new HashSet<>();
496 rels.addAll(getRelationsToThisTaxon());
497 rels.addAll(getRelationsFromThisTaxon());
498 return rels;
499 }
500
501 /**
502 * @see #getRelationsToThisTaxon()
503 */
504 protected void setRelationsToThisTaxon(Set<TaxonRelationship> relationsToThisTaxon) {
505 this.relationsToThisTaxon = relationsToThisTaxon;
506 }
507
508 /**
509 * @see #getRelationsFromThisTaxon()
510 */
511 protected void setRelationsFromThisTaxon(Set<TaxonRelationship> relationsFromThisTaxon) {
512 this.relationsFromThisTaxon = relationsFromThisTaxon;
513 }
514
515 /**
516 * If a relationships between <i>this</i> and the given taxon exists they will be returned.
517 * <i>This</i> taxon is involved either as a source or as a target in the relationships.
518 * The method will return <code>null</code> if no relations exist between the two taxa.
519 *
520 * @param possiblyRelatedTaxon
521 * a taxon to check for a relationship
522 * @return
523 * a set of <code>TaxonRelationship</code>s or <code>null</null> if none exists.
524 */
525 public Set<TaxonRelationship> getTaxonRelations(Taxon possiblyRelatedTaxon){
526 Set<TaxonRelationship> relations = new HashSet<>();
527
528 for(TaxonRelationship relationship : getTaxonRelations()){
529 if(relationship.getFromTaxon().equals(possiblyRelatedTaxon)) {
530 relations.add(relationship);
531 }
532 if(relationship.getToTaxon().equals(possiblyRelatedTaxon)) {
533 relations.add(relationship);
534 }
535 }
536
537 return relations.size() > 0 ? relations : null;
538 }
539
540 /**
541 * Removes one {@link TaxonRelationship taxon relationship} from one of both sets of
542 * {@link #getTaxonRelations() taxon relationships} in which <i>this</i> taxon is involved
543 * either as a {@link #getRelationsFromThisTaxon() source} or as a {@link #getRelationsToThisTaxon() target}.
544 * The taxon relationship will also be removed from one of both sets
545 * belonging to the second taxon involved. Furthermore the inherited RelatedFrom and
546 * RelatedTo attributes of the given taxon relationship will be nullified.<P>
547 *
548 * @param rel the taxon relationship which should be removed from one
549 * of both sets
550 * @see #getTaxonRelations()
551 * @see eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom()
552 * @see eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo()
553 *
554 */
555 public void removeTaxonRelation(TaxonRelationship rel) {
556 this.relationsToThisTaxon.remove(rel);
557 this.relationsFromThisTaxon.remove(rel);
558 Taxon fromTaxon = rel.getFromTaxon();
559 Taxon toTaxon = rel.getToTaxon();
560
561 //delete Relationship from other related Taxon
562 if (fromTaxon != this){
563 rel.setToTaxon(null); //remove this Taxon from relationship
564 if (fromTaxon != null){
565 if (fromTaxon.getTaxonRelations().contains(rel)){
566 fromTaxon.removeTaxonRelation(rel);
567 }
568 }
569 }
570 if (toTaxon != this ){
571 rel.setFromTaxon(null); //remove this Taxon from relationship
572 if (toTaxon != null){
573 if (toTaxon.getTaxonRelations().contains(rel)) {
574 toTaxon.removeTaxonRelation(rel);
575 }
576 }
577 }
578 }
579
580 /**
581 * Adds an existing {@link TaxonRelationship taxon relationship} either to the set of
582 * {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon} or to the set of
583 * {@link #getRelationsFromThisTaxon() taxon relationships from <i>this</i> taxon}. If neither the
584 * source nor the target of the taxon relationship match with <i>this</i> taxon
585 * no addition will be carried out. The taxon relationship will also be
586 * added to the second taxon involved in the given relationship.<P>
587 *
588 * @param rel the taxon relationship to be added to one of <i>this</i> taxon's taxon relationships sets
589 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
590 * @see #getTaxonRelations()
591 * @see #getRelationsFromThisTaxon()
592 * @see #getRelationsToThisTaxon()
593 */
594 public void addTaxonRelation(TaxonRelationship rel) {
595 if (rel!=null && rel.getType()!=null && !getTaxonRelations().contains(rel) ){
596 Taxon toTaxon=rel.getToTaxon();
597 Taxon fromTaxon=rel.getFromTaxon();
598 if ( this.equals(toTaxon) || this.equals(fromTaxon) ){
599 if (this.equals(fromTaxon)){
600 relationsFromThisTaxon.add(rel);
601 // also add relation to other taxon object
602 if (toTaxon!=null){
603 toTaxon.addTaxonRelation(rel);
604 }
605 }else if (this.equals(toTaxon)){
606 relationsToThisTaxon.add(rel);
607 // also add relation to other taxon object
608 if (fromTaxon!=null){
609 fromTaxon.addTaxonRelation(rel);
610 }
611 }
612 }else if (toTaxon == null || fromTaxon == null){
613 if (toTaxon == null){
614 toTaxon = this;
615 relationsToThisTaxon.add(rel);
616 if (fromTaxon!= null){
617 fromTaxon.addTaxonRelation(rel);
618 }
619 }else if (fromTaxon == null && toTaxon != null){
620 fromTaxon = this;
621 relationsFromThisTaxon.add(rel);
622 toTaxon.addTaxonRelation(rel);
623 }
624 }
625 }
626 }
627
628 @Override
629 @Deprecated //for inner use by RelationshipBase only
630 public void addRelationship(RelationshipBase rel){
631 if (rel instanceof TaxonRelationship){
632 addTaxonRelation((TaxonRelationship)rel);
633 }else{
634 throw new ClassCastException("Wrong Relationsship type for Taxon.addRelationship");
635 }
636 }
637
638 /**
639 * Creates a new {@link TaxonRelationship taxon relationship} instance where <i>this</i> taxon
640 * plays the source role and adds it to the set of
641 * {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to <i>this</i> taxon.
642 * The taxon relationship will also be added to the set of taxon
643 * relationships to the second taxon involved in the created relationship.<P>
644 *
645 * @param toTaxon the taxon which plays the target role in the new taxon relationship
646 * @param type the taxon relationship type for the new taxon relationship
647 * @param citation the reference source for the new taxon relationship
648 * @param microcitation the string with the details describing the exact localisation within the reference
649 * @return
650 * @see #addTaxonRelation(TaxonRelationship)
651 * @see #getTaxonRelations()
652 * @see #getRelationsFromThisTaxon()
653 * @see #getRelationsToThisTaxon()
654 */
655 public TaxonRelationship addTaxonRelation(Taxon toTaxon, TaxonRelationshipType type, Reference citation, String microcitation) {
656 return new TaxonRelationship(this, toTaxon, type, citation, microcitation);
657 }
658
659 /**
660 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
661 * "misapplied name for") instance where <i>this</i> taxon plays the target role
662 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
663 * The taxon relationship will also be added to the set of taxon
664 * relationships to the other (misapplied name) taxon involved in the created relationship.
665 *
666 * @param misappliedNameTaxon the taxon which plays the source role in the new taxon relationship
667 * @param citation the reference source for the new taxon relationship
668 * @param microcitation the string with the details describing the exact localisation within the reference
669 * @return
670 * @see #getMisappliedNames()
671 * @see #addProParteMisappliedName(Taxon, Reference, String)
672 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
673 * @see #addTaxonRelation(TaxonRelationship)
674 * @see #getTaxonRelations()
675 * @see #getRelationsFromThisTaxon()
676 * @see #getRelationsToThisTaxon()
677 */
678 public TaxonRelationship addMisappliedName(Taxon misappliedNameTaxon, Reference citation, String microcitation) {
679 return misappliedNameTaxon.addTaxonRelation(this, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), citation, microcitation);
680 }
681
682 // public void removeMisappliedName(Taxon misappliedNameTaxon){
683 // Set<TaxonRelationship> taxRels = this.getTaxonRelations();
684 // for (TaxonRelationship taxRel : taxRels ){
685 // if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())
686 // && taxRel.getFromTaxon().equals(misappliedNameTaxon)){
687 // this.removeTaxonRelation(taxRel);
688 // }
689 // }
690 // }
691
692 /**
693 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
694 * "pro parte misapplied name for") instance where <i>this</i> taxon plays the target role
695 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
696 * The taxon relationship will also be added to the set of taxon
697 * relationships to the other (pro parte misapplied name) taxon involved in the created relationship.
698 *
699 * @param proParteMisappliedNameTaxon the taxon which plays the source role in the new taxon relationship
700 * @param citation the reference source for the new taxon relationship
701 * @param microcitation the string with the details describing the exact localisation within the reference
702 * @return
703 * @see #addMisappliedName(Taxon, Reference, String)
704 * @see #getMisappliedNames()
705 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
706 * @see #addTaxonRelation(TaxonRelationship)
707 * @see #getTaxonRelations()
708 * @see #getRelationsFromThisTaxon()
709 * @see #getRelationsToThisTaxon()
710 */
711 public TaxonRelationship addProParteMisappliedName(Taxon proParteMisappliedNameTaxon, Reference citation, String microcitation) {
712 return proParteMisappliedNameTaxon.addTaxonRelation(this, TaxonRelationshipType.PRO_PARTE_MISAPPLIED_NAME_FOR(), citation, microcitation);
713 }
714
715 /**
716 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
717 * "partial misapplied name for") instance where <i>this</i> taxon plays the target role
718 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
719 * The taxon relationship will also be added to the set of taxon
720 * relationships to the other (pro parte misapplied name) taxon involved in the created relationship.
721 *
722 * @param partialMisappliedNameTaxon the taxon which plays the source role in the new taxon relationship
723 * @param citation the reference source for the new taxon relationship
724 * @param microcitation the string with the details describing the exact localization within the reference
725 * @return
726 * @see #addMisappliedName(Taxon, Reference, String)
727 * @see #addProParteMisappliedName(Taxon, Reference, String)
728 * @see #getMisappliedNames()
729 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
730 * @see #addTaxonRelation(TaxonRelationship)
731 * @see #getTaxonRelations()
732 * @see #getRelationsFromThisTaxon()
733 * @see #getRelationsToThisTaxon()
734 */
735 public TaxonRelationship addPartialMisappliedName(Taxon partialMisappliedNameTaxon, Reference citation, String microcitation) {
736 return partialMisappliedNameTaxon.addTaxonRelation(this, TaxonRelationshipType.PARTIAL_MISAPPLIED_NAME_FOR(), citation, microcitation);
737 }
738
739 /**
740 * Creates a new {@link TaxonRelationship taxon relationship} (with {@link TaxonRelationshipType taxon relationship type}
741 * "pro parte synonym for") instance where <i>this</i> taxon plays the target role
742 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
743 * The taxon relationship will also be added to the set of taxon
744 * relationships to the other (pro parte synonym) taxon involved in the created relationship.
745 *
746 * @param proParteTaxon the taxon which plays the source role in the new taxon relationship
747 * @param citation the reference source for the new taxon relationship
748 * @param microcitation the string with the details describing the exact localisation within the reference
749 * @return
750 * @see #getMisappliedNames()
751 * @see #addProParteMisappliedName(Taxon, Reference, String)
752 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
753 * @see #addTaxonRelation(TaxonRelationship)
754 * @see #getTaxonRelations()
755 * @see #getRelationsFromThisTaxon()
756 * @see #getRelationsToThisTaxon()
757 */
758 public TaxonRelationship addProparteSynonym(Taxon proParteTaxon, Reference citation, String microcitation) {
759 return proParteTaxon.addTaxonRelation(this, TaxonRelationshipType.PRO_PARTE_SYNONYM_FOR(), citation, microcitation);
760 }
761
762 /**
763 * Creates a new {@link TaxonRelationship taxon relationship} instance with
764 * {@link TaxonRelationshipType taxon relationship type} {@link TaxonRelationshipType#PARTIAL_SYNONYM_FOR()
765 * partial synonym for} where <i>this</i> taxon plays the target role
766 * and adds it to the set of {@link #getRelationsToThisTaxon() taxon relationships to <i>this</i> taxon}.
767 * The taxon relationship will also be added to the set of taxon
768 * relationships to the other (partial synonym) taxon involved in the created relationship.
769 *
770 * @param partialTaxon the taxon which plays the source role in the new taxon relationship
771 * @param citation the reference source for the new taxon relationship
772 * @param microcitation the string with the details describing the exact localisation within the reference
773 * @return
774 * @see #addProparteSynonym(Taxon, Reference, String)
775 * @see #addTaxonRelation(Taxon, TaxonRelationshipType, Reference, String)
776 * @see #addTaxonRelation(TaxonRelationship)
777 * @see #getTaxonRelations()
778 * @see #getRelationsFromThisTaxon()
779 * @see #getRelationsToThisTaxon()
780 */
781 public TaxonRelationship addPartialSynonym(Taxon partialTaxon, Reference citation, String microcitation) {
782 return partialTaxon.addTaxonRelation(this, TaxonRelationshipType.PARTIAL_SYNONYM_FOR(), citation, microcitation);
783 }
784
785 /**
786 * TODO update documentation
787 * Removes one {@link TaxonRelationship taxon relationship} with {@link TaxonRelationshipType taxon relationship type}
788 * taxonRelType and with the given child taxon playing the
789 * source role from the set of {@link #getRelationsToThisTaxon() "taxon relationships to"} belonging
790 * to <i>this</i> taxon. The taxon relationship will also be removed from the set
791 * of {@link #getRelationsFromThisTaxon() "taxon relationships from"} belonging to the other side taxon.
792 * Furthermore, the inherited RelatedFrom and RelatedTo attributes of the
793 * taxon relationship will be nullified.<P>
794 *
795 * @param taxon the taxon which plays the source role in the taxon relationship
796 * @param taxonRelType the taxon relationship type
797 */
798 public void removeTaxon(Taxon taxon, TaxonRelationshipType taxonRelType){
799 Set<TaxonRelationship> taxRels = this.getTaxonRelations();
800 for (TaxonRelationship taxRel : taxRels ){
801 if (taxRel.getType().equals(taxonRelType)
802 && taxRel.getFromTaxon().equals(taxon)){
803 this.removeTaxonRelation(taxRel);
804 }
805 }
806 }
807
808 /**
809 * Returns the boolean value indicating whether <i>this</i> taxon is a misapplication
810 * (misapplied name) for at least one other taxon.
811 */
812 // TODO cache as for #hasTaxonomicChildren
813 @Transient
814 public boolean isMisapplication(){
815 return computeMisapliedNameRelations() > 0;
816 }
817
818 /**
819 * Counts the number of misapplied name relationships (including pro parte and partial
820 * misapplied names) where this taxon represents the
821 * misapplied name for another taxon.
822 * @return
823 */
824 private int computeMisapliedNameRelations(){
825 int count = 0;
826 for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
827 if (rel.getType().isAnyMisappliedName()){
828 count++;
829 }
830 }
831 return count;
832 }
833
834 /**
835 * Returns the boolean value indicating whether <i>this</i> taxon is a misapplication
836 * (misapplied name) for at least one other taxon.
837 */
838 // TODO cache as for #hasTaxonomicChildren
839 @Transient
840 public boolean isProparteSynonym(){
841 return computeProparteSynonymRelations() > 0;
842 }
843
844 /**
845 * Counts the number of misapplied name relationships (including pro parte misapplied
846 * names) where this taxon represents the
847 * misapplied name for another taxon.
848 * @return
849 */
850 private int computeProparteSynonymRelations(){
851 int count = 0;
852 for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
853 if (rel.getType().isAnySynonym()){
854 count++;
855 }
856 }
857 return count;
858 }
859
860 /**
861 * Returns the boolean value indicating whether <i>this</i> taxon is a invalid designation
862 * for at least one other taxon.
863 */
864 // TODO cache as for #hasTaxonomicChildren
865 @Transient
866 public boolean isInvalidDesignation(){
867 return computeInvalidDesignationRelations() > 0;
868 }
869
870 /**
871 * Counts the number of invalid designation relationships where this taxon represents the
872 * invalid designation for another taxon.
873 * @return
874 */
875 private int computeInvalidDesignationRelations(){
876 int count = 0;
877 for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
878 if (rel.getType().isInvalidDesignation()){
879 count++;
880 }
881 }
882 return count;
883 }
884
885 /**
886 * Returns the boolean value indicating whether <i>this</i> taxon is a related
887 * concept for at least one other taxon.
888 */
889 @Transient
890 public boolean isRelatedConcept(){
891 return computeConceptRelations() > 0;
892 }
893
894 /**
895 * Counts the number of concept relationships where this taxon represents the
896 * related concept for another taxon.
897 * @return
898 */
899 private int computeConceptRelations(){
900 int count = 0;
901 for (TaxonRelationship rel: this.getRelationsFromThisTaxon()){
902 TaxonRelationshipType type = rel.getType();
903 if (type.isConceptRelationship()){
904 count++;
905 }
906 }
907 return count;
908 }
909
910 /**
911 * Returns the boolean value indicating whether <i>this</i> taxon has at least one
912 * {@link Synonym synonym} (true) or not (false). If true the {@link #getSynonyms() set of synonyms}
913 * belonging to <i>this</i> ("accepted/valid") taxon is not empty .
914 *
915 * @see #getSynonyms()
916 * @see #getSynonymNames()
917 * @see #removeSynonym(Synonym)
918 */
919 @Transient
920 public boolean hasSynonyms(){
921 return this.getSynonyms().size() > 0;
922 }
923
924 /**
925 * Returns the boolean value indicating whether <i>this</i> taxon is at least
926 * involved in one {@link #getTaxonRelations() taxon relationship} between
927 * two taxa (true), either as a source or as a target, or not (false).
928 *
929 * @see #getTaxonRelations()
930 * @see #getRelationsToThisTaxon()
931 * @see #getRelationsFromThisTaxon()
932 * @see #removeTaxonRelation(TaxonRelationship)
933 * @see TaxonRelationship
934 */
935 public boolean hasTaxonRelationships(){
936 return this.getTaxonRelations().size() > 0;
937 }
938
939 /*
940 * MISAPPLIED NAMES
941 */
942 /**
943 * Returns the set of taxa playing the source role in {@link TaxonRelationship taxon relationships}
944 * (with {@link TaxonRelationshipType taxon relationship type} "misapplied name for") where
945 * <i>this</i> taxon plays the target role. A misapplied name is a taxon the
946 * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} of which has been erroneously used
947 * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
948 * as the one meant by <i>this</i> ("accepted/correct") taxon.
949 *
950 * @see #getTaxonRelations()
951 * @see #getRelationsToThisTaxon()
952 * @see #addMisappliedName(Taxon, Reference, String)
953 * @param includeNonCongruent if <code>true</code> also those taxa are returned that are related
954 * via a non congruent relationship like {@link TaxonRelationshipType#PRO_PARTE_MISAPPLIED_NAME_FOR()
955 * pro parte misapplied name}
956 */
957 @Transient
958 public Set<Taxon> getMisappliedNames(boolean includeNonCongruent){
959 Set<Taxon> taxa = new HashSet<>();
960 Set<TaxonRelationship> rels = this.getRelationsToThisTaxon();
961 for (TaxonRelationship rel: rels){
962 TaxonRelationshipType relType = rel.getType();
963 if ( (includeNonCongruent && relType.isAnyMisappliedName())
964 || relType.equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
965 taxa.add(rel.getFromTaxon());
966 }
967 }
968 return taxa;
969 }
970
971 /**
972 * Returns the set of misapplied name relationships in which this taxon
973 * plays the role of the correctly accepted taxon (target). A misapplied name is a taxon the
974 * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} of which has been erroneously used
975 * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
976 * as the one meant by <i>this</i> ("accepted/correct") taxon.
977 */
978 @Transient
979 public Set<TaxonRelationship> getMisappliedNameRelations(){
980 Set<TaxonRelationship> result = new HashSet<>();
981 Set<TaxonRelationship> rels = this.getRelationsToThisTaxon();
982 for (TaxonRelationship rel: rels){
983 TaxonRelationshipType relType = rel.getType();
984 if (relType.isAnyMisappliedName()){
985 result.add(rel);
986 }
987 }
988 return result;
989 }
990
991 /**
992 * Returns the set of taxa playing the target role in {@link TaxonRelationship taxon relationships}
993 * (with {@link TaxonRelationshipType taxon relationship type} "misapplied name for"
994 * or "pro parte misapplied name for") where
995 * <i>this</i> taxon plays the source role. A misapplied name is a taxon the
996 * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} of which has been erroneously used
997 * by its {@link TaxonBase#getSec() taxon reference} to denominate the same real taxon
998 * as the one meant by <i>this</i> ("accepted/correct") taxon.
999
1000 * @param includeNonCongruent if <code>true</code> also those taxa are returned that are related
1001 * via a non congruent relationship like {@link TaxonRelationshipType#PRO_PARTE_MISAPPLIED_NAME_FOR()
1002 * pro parte misapplied name}
1003 *
1004 * @see #getTaxonRelations()
1005 * @see #getRelationsToThisTaxon()
1006 * @see #addMisappliedName(Taxon, Reference, String)
1007 * @see #addProParteMisappliedName(Taxon, Reference, String)
1008 */
1009 @Transient
1010 public Set<Taxon> getTaxaForMisappliedName(boolean includeNonCongruent){
1011 Set<Taxon> taxa = new HashSet<>();
1012 Set<TaxonRelationship> rels = this.getRelationsFromThisTaxon();
1013 for (TaxonRelationship rel: rels){
1014 TaxonRelationshipType relType = rel.getType();
1015 if ( (includeNonCongruent && relType.isAnyMisappliedName())
1016 || relType.equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
1017 taxa.add(rel.getToTaxon());
1018 }
1019 }
1020 return taxa;
1021 }
1022
1023 /**
1024 * Returns the set of pro parte or partial synonym relationships in which this taxon
1025 * plays the role of the "correctly" accepted taxon (target).
1026 *
1027 * @see #getProParteAndPartialSynonyms()
1028 * @see #getMisappliedNameRelations()
1029 */
1030 @Transient
1031 public Set<TaxonRelationship> getProParteAndPartialSynonymRelations(){
1032 Set<TaxonRelationship> result = new HashSet<>();
1033 Set<TaxonRelationship> rels = this.getRelationsToThisTaxon();
1034 for (TaxonRelationship rel: rels){
1035 TaxonRelationshipType relType = rel.getType();
1036 if (relType.isAnySynonym()){
1037 result.add(rel);
1038 }
1039 }
1040 return result;
1041 }
1042
1043 /**
1044 * Returns the set of pro parte or partial synonyms in which this taxon
1045 * plays the role of the "correctly" accepted taxon (target).
1046 *
1047 * @see #getProParteAndPartialSynonymRelations()
1048 * @see #getMisappliedNames(boolean)
1049 */
1050 @Transient
1051 public Set<Taxon> getProParteAndPartialSynonyms(){
1052 Set<Taxon> synonyms = new HashSet<>();
1053 Set<TaxonRelationship> rels = this.getProParteAndPartialSynonymRelations();
1054 for (TaxonRelationship rel: rels){
1055 synonyms.add(rel.getFromTaxon());
1056 }
1057 return synonyms;
1058 }
1059
1060 /**
1061 * Returns the set of all {@link TaxonName taxon names} used as {@link Synonym synonyms}
1062 * of <i>this</i> ("accepted/valid") taxon.
1063 *
1064 * @see #getSynonyms()
1065 * @see #getSynonymsSortedByType()
1066 * @see #addSynonymName(TaxonName, SynonymType)
1067 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1068 * @see #removeSynonym(Synonym)
1069 */
1070 @Transient
1071 public Set<TaxonName> getSynonymNames(){
1072 Set<TaxonName> names = new HashSet<>();
1073 for (Synonym syn: this.getSynonyms()){
1074 names.add(syn.getName());
1075 }
1076 return names;
1077 }
1078
1079 /**
1080 * Might be public in future. For the moment protected to ensure that
1081 * synonym type is always set after refactoring.
1082 *
1083 * @param synonym
1084 */
1085 protected void addSynonym(Synonym synonym){
1086 if (! this.equals(synonym.getAcceptedTaxon())){
1087 synonym.setAcceptedTaxon(this);
1088 }
1089 if (!synonyms.contains(synonym)){
1090 synonyms.add(synonym);
1091 }
1092 }
1093
1094 /**
1095 * Adds the given {@link Synonym synonym} to <code>this</code> taxon
1096 * and changes the {@link SynonymType
1097 * synonym type} before.
1098 *
1099 * @param synonym the synonym to be added
1100 * @param synonymType the synonym type of the synonym to be added. If not <code>null</code>
1101 * and if the synonym already has a type the existing type will be overwritten.<BR>
1102 * If synonymType is {@link SynonymType#HOMOTYPIC_SYNONYM_OF()}
1103 * the homotypic group of the synonym is changed to that of <code>this</code> taxon.<BR>
1104 * To explicitly set the type to <code>null</code> use {@link Synonym#setType(SynonymType)}
1105 * @see #addSynonym(Synonym)
1106 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1107 * @see #addSynonymName(TaxonName, SynonymType)
1108 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1109 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1110 * @see #addHeterotypicSynonymName(TaxonName)
1111 * @see #addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1112 * @see #getSynonyms()
1113 * @see #removeSynonym(Synonym)
1114 * @see Synonym#getAcceptedTaxon()
1115 */
1116 public void addSynonym(Synonym synonym, SynonymType synonymType){
1117 synonym.setType(synonymType); //must be set before as otherwise merging of homotypical groups may not work correctly in Synonym.checkHomotypic()
1118 addSynonym(synonym);
1119 }
1120
1121 /**
1122 * Adds the given {@link Synonym synonym} with the given {@link SynonymType
1123 * synonym relationship type}
1124 *
1125 * @param synonym the synonym to be added
1126 * @param synonymType the synonym type of the synonym to be added. If not null
1127 * and if the synonym already has a type the existing type will be overwritten.
1128 // * @param citation the reference source for the new synonym relationship
1129 // * @param microcitation the string with the details describing the exact localization within the reference
1130 * @see #addSynonym(Synonym)
1131 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1132 * @see #addSynonymName(TaxonName, SynonymType)
1133 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1134 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1135 * @see #addHeterotypicSynonymName(TaxonName)
1136 * @see #addHeterotypicSynonymName(TaxonName, HomotypicalGroup, Reference, String)
1137 * @see #getSynonyms()
1138 * @see #removeSynonym(Synonym)
1139 * @see Synonym#getAcceptedTaxon()
1140 */
1141 private void addSynonym(Synonym synonym, SynonymType synonymType, Reference newSecReference, String newSecMicroReference){
1142 if (newSecReference != null){
1143 synonym.setSec(newSecReference);
1144 }
1145 if (newSecMicroReference != null){
1146 synonym.setSecMicroReference(newSecMicroReference);
1147 }
1148 addSynonym(synonym, synonymType);
1149 return;
1150 }
1151
1152 /**
1153 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the
1154 * given {@link TaxonName synonym name} and with the given
1155 * {@link SynonymType synonym type}. If the later is
1156 * {@link SynonymType#HOMOTYPIC_SYNONYM_OF() homotypic synonym}
1157 * the name will be added to the same {@link HomotypicalGroup homotypical group}
1158 * as the <code>this</code> accepted taxon.<BR>
1159 * The secundum reference of the new synonym is taken from <code>this</code> taxon.
1160 * A secundum detail is not set.
1161 *
1162 * @param synonymName the taxon name to be used as a synonym to be added
1163 * to <i>this</i> taxon's set of synonyms
1164 * @param synonymType the synonym type of the synonym
1165 * relationship to be added
1166 * @return the created synonym
1167 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1168 * @see #addSynonym(Synonym, SynonymType)
1169 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1170 * @see #addHomotypicSynonym(Synonym, Reference, String)
1171 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1172 * @see #addHeterotypicSynonymName(TaxonName)
1173 * @see #addHeterotypicSynonymName(TaxonName, HomotypicalGroup, Reference, String)
1174 * @see #getSynonyms()
1175 * @see #removeSynonym(Synonym)
1176 */
1177 public Synonym addSynonymName(TaxonName synonymName, SynonymType synonymType){
1178 return addSynonymName(synonymName, null, null, synonymType);
1179 }
1180
1181 /**
1182 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the
1183 * given {@link TaxonName synonym name} and with the given
1184 * {@link SynonymType synonym type}. If the later is
1185 * {@link SynonymType#HOMOTYPIC_SYNONYM_OF() homotypic synonym}
1186 * the name will be added to the same {@link HomotypicalGroup homotypical group}
1187 * as the <code>this</code> accepted taxon.<BR>
1188 *
1189 * If secReference is not <code>null</code>, the new synonym will have this as
1190 * secundum reference. Otherwise <code>this</code> taxons sec reference is taken
1191 * as secundum reference for the synonym. SecDetail will be the secMicroReference of the
1192 * new synonym.<BR>
1193 *
1194 * @param synonymName the taxon name to be used as a synonym to be added
1195 * to <i>this</i> taxon's set of synonyms
1196 * @param secReference the secundum reference for the new synonym (if <code>null</code>
1197 * <code>this</code> taxon's secundum reference is taken.
1198 * @param secMicroReference the secundum micro reference of the new synonym
1199 * @param synonymType the synonym type of the synonym to be added
1200 *
1201 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1202 * @see #addSynonym(Synonym, SynonymType)
1203 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1204 * @see #addHomotypicSynonym(Synonym, Reference, String)
1205 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1206 * @see #addHeterotypicSynonymName(TaxonName)
1207 * @see #addHeterotypicSynonymName(TaxonName, HomotypicalGroup, Reference, String)
1208 * @see #getSynonyms()
1209 * @see #removeSynonym(Synonym)
1210 */
1211 public Synonym addSynonymName(TaxonName synonymName, Reference secReference, String secMicroReference, SynonymType synonymType){
1212 Synonym synonym = Synonym.NewInstance(synonymName, this.getSec()); //default sec
1213 addSynonym(synonym, synonymType, secReference, secMicroReference);
1214 return synonym;
1215 }
1216
1217 /**
1218 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the given
1219 * {@link TaxonName synonym name}. The synonym will have the synonym type
1220 * {@link SynonymType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of"}.<BR>
1221 * The secundum reference is taken from <code>this</code> taxon.
1222 * No secMicroReference will be set for the new synonym.<BR>
1223 * The synonym will keep it's old homotypical group.<BR>
1224 *
1225 * @param synonymName the taxon name to be used as an heterotypic synonym
1226 * to be added to <i>this</i> taxon's set of synonyms
1227 * @return the created synonym
1228 * @see #addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1229 * @see #addSynonymName(TaxonName, SynonymType)
1230 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1231 * @see #addSynonym(Synonym, SynonymType)
1232 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1233 * @see #addHomotypicSynonym(Synonym, Reference, String)
1234 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1235 * @see #getSynonyms()
1236 * @see #removeSynonym(Synonym)
1237 */
1238 public Synonym addHeterotypicSynonymName(TaxonName synonymName){
1239 return addHeterotypicSynonymName(synonymName, null, null, null);
1240 }
1241
1242 /**
1243 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the given
1244 * {@link TaxonName synonym name}. The synonym will have the synonym type
1245 * {@link SynonymType#HETEROTYPIC_SYNONYM_OF() "is heterotypic synonym of"}.<BR>
1246 *
1247 * If secReference is not <code>null</code>, the new synonym will have this as
1248 * secundum reference. Otherwise <code>this</code> taxons sec reference is taken
1249 * as secundum reference for the synonym. SecDetail will be the secMicroReference of the
1250 * new synonym.<BR>
1251 * Furthermore the taxon name used as synonym will be added
1252 * to the given {@link name.HomotypicalGroup homotypical group} (if not <code>null</code>).<BR>
1253 *
1254 * @param synonymName the taxon name to be used as an heterotypic synonym
1255 * to be added to <i>this</i> taxon's set of synonyms
1256 * @param secReference the secundum reference for the new synonym
1257 * @param secDetail the secundum detil for the new synonym
1258 * @param homotypicalGroup the homotypical group to which the taxon name
1259 * of the synonym will be added. If <code>null</code>
1260 * the homotypical group of synonymName is not changed
1261 * @return the created synonym
1262 * @see #addHeterotypicSynonymName(TaxonName)
1263 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1264 * @see #addSynonymName(TaxonName, SynonymType)
1265 * @see #addSynonym(Synonym, SynonymType)
1266 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1267 * @see #addHomotypicSynonym(Synonym, Reference, String)
1268 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1269 * @see #getSynonyms()
1270 * @see #removeSynonym(Synonym)
1271 */
1272 public Synonym addHeterotypicSynonymName(TaxonName synonymName, Reference secReference, String secDetail, HomotypicalGroup homotypicalGroup){
1273 Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
1274 if (homotypicalGroup != null){
1275 homotypicalGroup.addTypifiedName(synonymName);
1276 }
1277 addSynonym(synonym, SynonymType.HETEROTYPIC_SYNONYM_OF(), secReference, secDetail);
1278 return synonym;
1279 }
1280
1281 /**
1282 * Creates a new {@link Synonym synonym} to <code>this</code> {@link Taxon taxon}) using the given
1283 * {@link TaxonName synonym name}. The synonym will have the synonym type
1284 * {@link SynonymType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of"}.<BR>
1285 * The secundum reference is taken from <code>this</code> taxon.
1286 * No secMicroReference will be set for the new synonym.<BR>
1287 * The synonym's homotypic group will be changed to <code>this</code> taxon's group.<BR>
1288 *
1289 * @param synonymName the taxon name to be used as an homotypic synonym
1290 * to be added to <i>this</i> taxon's set of synonyms
1291 * @return the created synonym
1292 * @see #addHomotypicSynonym(Synonym, Reference, String)
1293 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1294 * @see #addSynonymName(TaxonName, SynonymType)
1295 * @see #addSynonym(Synonym, SynonymType)
1296 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1297 * @see #addHeterotypicSynonymName(TaxonName)
1298 * @see #addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1299 * @see #getSynonyms()
1300 * @see #removeSynonym(Synonym)
1301 */
1302 public Synonym addHomotypicSynonymName(TaxonName synonymName){
1303 Synonym synonym = Synonym.NewInstance(synonymName, this.getSec());
1304 addHomotypicSynonym(synonym);
1305 return synonym;
1306 }
1307
1308 /**
1309 * Adds the given {@link Synonym synonym} to <code>this</code> taxon,
1310 * with the {@link SynonymType#HOMOTYPIC_SYNONYM_OF() "is homotypic synonym of"
1311 * relationship type} and returns it.
1312 * Furthermore the {@link TaxonName taxon name}
1313 * used as synonym will be added to the same {@link HomotypicalGroup homotypic group}
1314 * to which the taxon name of <i>this</i> taxon belongs.<BR>
1315 *
1316 * @param synonym the synonym added to <i>this</i> taxon's synonym set
1317 * @see #addHomotypicSynonymName(TaxonName, Reference, String)
1318 * @see #addSynonym(Synonym, SynonymType)
1319 * @see #addSynonym(Synonym, SynonymType, Reference, String)
1320 * @see #addSynonymName(TaxonName, SynonymType, Reference, String)
1321 * @see #addSynonymName(TaxonName, SynonymType)
1322 * @see #addHeterotypicSynonymName(TaxonName)
1323 * @see #addHeterotypicSynonymName(TaxonName, Reference, String, HomotypicalGroup)
1324 * @see #getSynonyms()
1325 * @see #removeSynonym(Synonym)
1326 */
1327 public void addHomotypicSynonym(Synonym synonym){
1328 if (!this.getSynonyms().contains(synonym)){
1329 addSynonym(synonym, SynonymType.HOMOTYPIC_SYNONYM_OF());
1330 } else{
1331 logger.warn("Tried to add a synonym to an accepted taxon that already is a synonym of this taxon.");
1332 }
1333 return;
1334 }
1335
1336 /**
1337 * Like {@link #removeSynonym(Synonym, boolean)} with <code>removeSynonymNameFromHomotypicalGroup</code> set to true.
1338 * @see #removeSynonym(Synonym, boolean)
1339 */
1340 public void removeSynonym(Synonym synonym){
1341 removeSynonym(synonym, true);
1342 }
1343
1344
1345 /**
1346 * Removes one element from the set of {@link Synonym synonyms} assigned
1347 * to <i>this</i> (accepted/valid) taxon.
1348 *
1349 * @param synonym the synonym to be removed
1350 * @param removeSynonymNameFromHomotypicalGroup
1351 * if <code>true</code> the synonym name will also be deleted from its homotypical group if the
1352 * group contains other names
1353 * @see #getSynonyms()
1354 * @see #removeSynonym(Synonym)
1355 */
1356 public void removeSynonym(Synonym synonym, boolean removeSynonymNameFromHomotypicalGroup) {
1357 if (synonym != null && this.equals(synonym.getAcceptedTaxon())){
1358 if(removeSynonymNameFromHomotypicalGroup){
1359 HomotypicalGroup synHG = synonym.getName().getHomotypicalGroup();
1360 if (synHG.getTypifiedNames().size() > 1){
1361 synHG.removeTypifiedName(synonym.getName(), false);
1362 }
1363 }
1364 this.synonyms.remove(synonym);
1365 synonym.setAcceptedTaxon(null);
1366 }
1367 }
1368
1369 /**
1370 * @see #getHomotypicSynonymsByHomotypicGroup(TaxonComparator)
1371 */
1372 @Transient
1373 public List<Synonym> getHomotypicSynonymsByHomotypicGroup(){
1374 return getHomotypicSynonymsByHomotypicGroup(null);
1375 }
1376
1377 /**
1378 * Retrieves the ordered list (depending on the date of publication) of
1379 * homotypic {@link Synonym synonyms} (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1380 * as for <i>this</i> taxon) under the condition that the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon names}
1381 * of these synonyms and the taxon name of <i>this</i> taxon belong to the
1382 * same {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group}.
1383 *
1384 * @param comparator the taxon comparator to use, if <code>null</code> the default comparator is taken.
1385 * @return the ordered list of homotypic synonyms
1386 * @see #getHomotypicSynonymsByHomotypicSynonymType()
1387 * @see #getSynonyms()
1388 * @see #getHomotypicSynonymyGroups()
1389 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup
1390 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup#getSynonymsInGroup(Reference)
1391 */
1392 @Transient
1393 public List<Synonym> getHomotypicSynonymsByHomotypicGroup(TaxonComparator comparator){
1394 if (this.getHomotypicGroup() == null){
1395 return null;
1396 }else if (comparator == null){
1397 return this.getSynonymsInGroup(this.getHomotypicGroup());
1398 }else{
1399 return this.getSynonymsInGroup(this.getHomotypicGroup(), comparator);
1400 }
1401 }
1402
1403 /**
1404 * Retrieves the list of homotypic {@link Synonym synonyms}
1405 * (according to the same {@link eu.etaxonomy.cdm.model.reference.Reference reference}
1406 * as for <i>this</i> taxon) under the condition that these synonyms and
1407 * <i>this</i> taxon are involved in {@link SynonymRelationship synonym relationships} with an
1408 * "is homotypic synonym of" {@link SynonymType#HOMOTYPIC_SYNONYM_OF() synonym relationship type}.
1409 *
1410 * @return the ordered list of homotypic synonyms
1411 * @see #getHomotypicSynonymsByHomotypicGroup()
1412 * @see #getSynonyms()
1413 * @see #getHomotypicSynonymyGroups()
1414 * @see SynonymType
1415 * @deprecated as the method currently returns data not matching the original description of the method
1416 * as an ordered list (according to date of publication) of synonyms with same secundum as <i>this</i> taxon.
1417 * In future this method will either be removed or semantics may change.
1418 */
1419 @Deprecated
1420 @Transient
1421 public List<Synonym> getHomotypicSynonymsByHomotypicSynonymType(){
1422 Set<Synonym> synonyms = this.getSynonyms();
1423 List<Synonym> result = new ArrayList<>();
1424 for(Synonym synonym : synonyms) {
1425 if(synonym.getType().equals(SynonymType.HOMOTYPIC_SYNONYM_OF())){
1426 result.add(synonym);
1427 }
1428 }
1429 return result;
1430 }
1431
1432 /**
1433 * Returns the ordered list of all {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups} {@link Synonym synonyms} of
1434 * <i>this</i> taxon belong to. {@link eu.etaxonomy.cdm.model.name.TaxonName Taxon names} of homotypic synonyms
1435 * belong to the same homotypical group as the taxon name of <i>this</i>
1436 * taxon. Taxon names of heterotypic synonyms belong to at least one other
1437 * homotypical group. <BR>
1438 * The list returned is ordered according to the date of publication of the
1439 * first published name within each homotypical group.
1440 *
1441 * @see #getHeterotypicSynonymyGroups()
1442 * @see #getSynonyms()
1443 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup
1444 */
1445 @Transient
1446 public List<HomotypicalGroup> getHomotypicSynonymyGroups(){
1447 List<HomotypicalGroup> result = new ArrayList<>();
1448 HomotypicalGroup myGroup = this.getHomotypicGroup();
1449 if (myGroup != null){ //if taxon has no name HG might be null
1450 result.add(myGroup);
1451 }
1452 for (TaxonName taxonName :this.getSynonymNames()){
1453 if (taxonName != null) {
1454 if (!result.contains(taxonName.getHomotypicalGroup())){
1455 result.add(taxonName.getHomotypicalGroup());
1456 }
1457 }
1458 }
1459 return result;
1460 }
1461
1462 /**
1463 * {@inheritDoc}.
1464 * <BR>Also returns <code>false</code> if it is a misapplied name or has a similar concept relationship that
1465 * is similar to synonym relationship (shows up in the synonymy of applications)
1466 */
1467 @Override
1468 @Transient
1469 public boolean isOrphaned() {
1470
1471 if(taxonNodes == null || taxonNodes.isEmpty()) {
1472 if(getRelationsFromThisTaxon().isEmpty()) {
1473 return true;
1474 }else{
1475 for (TaxonRelationship rel : getRelationsFromThisTaxon()){
1476 if (rel.getType() != null && ! rel.getType().isConceptRelationship()){
1477 return false; //a synonym relationship type similar relationship exists => not orphaned
1478 }
1479 }
1480 return true; //all relations are real concept relations and therefore not relevant
1481 }
1482 }else{
1483 return false;
1484 }
1485 }
1486
1487 /**
1488 * Returns the ordered list of all
1489 * {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical groups}
1490 * that contain {@link Synonym synonyms} that are heterotypic to <i>this</i> taxon.<BR>
1491 *
1492 * {@link eu.etaxonomy.cdm.model.name.TaxonName Taxon names} of heterotypic synonyms
1493 * belong to a homotypical group which cannot be the homotypical group to which the
1494 * taxon name of <i>this</i> taxon belongs.
1495 * This method returns the same
1496 * list as the {@link #getHomotypicSynonymyGroups() getHomotypicSynonymyGroups} method
1497 * but without the homotypical group to which the taxon name of <i>this</i> taxon
1498 * belongs.<BR>
1499 * The list returned is <B>ordered</B> according to the rules defined for
1500 * the {@link HomotypicGroupTaxonComparator} which includes 1) grouping of
1501 * basionym groups, 2) replaced synonym relationships, 3) publication date,
1502 * 4) ranks and 5) alphabetical order.
1503 *
1504 * @see #getHeterotypicSynonymyGroups()
1505 * @see #getSynonyms()
1506 * @see SynonymType#HETEROTYPIC_SYNONYM_OF()
1507 * @see eu.etaxonomy.cdm.model.name.HomotypicalGroup
1508 */
1509 @Transient
1510 public List<HomotypicalGroup> getHeterotypicSynonymyGroups(){
1511 List<HomotypicalGroup> list = getHomotypicSynonymyGroups();
1512 //remove homotypic group
1513 list.remove(this.getHomotypicGroup());
1514 //sort
1515 Map<Synonym, HomotypicalGroup> map = new HashMap<>();
1516 for (HomotypicalGroup homotypicalGroup: list){
1517 List<Synonym> synonymList = getSynonymsInGroup(homotypicalGroup);
1518 if (synonymList.size() > 0){
1519 //select the first synonym in the group
1520 map.put(synonymList.get(0), homotypicalGroup);
1521 }
1522 }
1523 List<Synonym> keyList = new ArrayList<>();
1524 keyList.addAll(map.keySet());
1525 //order by first synonym
1526 Collections.sort(keyList, defaultTaxonComparator);
1527
1528 List<HomotypicalGroup> result = new ArrayList<>();
1529 for(Synonym synonym: keyList){
1530 //"replace" synonyms by homotypic groups
1531 result.add(map.get(synonym));
1532 }
1533 //sort end
1534 return result;
1535 }
1536
1537 /**
1538 * Retrieves the ordered list (depending on the rules defined for
1539 * the {@link HomotypicGroupTaxonComparator}) of
1540 * {@link taxon.Synonym synonyms} (according to a given reference)
1541 * the {@link TaxonName taxon names} of which belong to the homotypical group.
1542 * If other names are part of the group that are not considered synonyms of
1543 * <i>this</i> taxon, then they will not be included in
1544 * the result set.
1545 *
1546 * @param homotypicGroup
1547 * @see #getHeterotypicSynonymyGroups()
1548 * @see TaxonName#getSynonyms()
1549 * @see TaxonName#getTaxa()
1550 * @see taxon.Synonym
1551 */
1552 @Transient
1553 public List<Synonym> getSynonymsInGroup(HomotypicalGroup homotypicGroup){
1554 return getSynonymsInGroup(homotypicGroup, new HomotypicGroupTaxonComparator(this));
1555 }
1556
1557 /**
1558 * @param homotypicGroup
1559 * @param comparator
1560 * @return
1561 * @see #getSynonymsInGroup(HomotypicalGroup)
1562 * @see #getHeterotypicSynonymyGroups()
1563 */
1564 @Transient
1565 public List<Synonym> getSynonymsInGroup(HomotypicalGroup homotypicGroup, TaxonComparator comparator){
1566 List<Synonym> result = new ArrayList<>();
1567 if (homotypicGroup == null){
1568 return result; //always empty
1569 }
1570
1571 for (Synonym synonym : this.getSynonyms()){
1572 if (homotypicGroup.equals(synonym.getHomotypicGroup())){
1573 result.add(synonym);
1574 }
1575 }
1576
1577 Collections.sort(result, comparator);
1578 return result;
1579 }
1580
1581 /**
1582 * @see #getSynonymsGroups()
1583 */
1584 @Transient
1585 public List<Taxon> getAllMisappliedNames(){
1586 List<Taxon> result = new ArrayList<>();
1587
1588 for (TaxonRelationship rel : this.getRelationsToThisTaxon()){
1589 if (rel.getType().isAnyMisappliedName() ){
1590 result.add(rel.getFromTaxon());
1591 }
1592 }
1593 sortBySimpleTitleCacheComparator(result);
1594 return result;
1595 }
1596
1597 /**
1598 * @see #getSynonymsGroups()
1599 */
1600 @Transient
1601 public List<Taxon> getInvalidDesignations(){
1602 List<Taxon> result = new ArrayList<>();
1603 for (TaxonRelationship rel : this.getRelationsToThisTaxon()){
1604 if (rel.getType().isInvalidDesignation()){
1605 result.add(rel.getFromTaxon());
1606 }
1607 }
1608 sortBySimpleTitleCacheComparator(result);
1609 return result;
1610 }
1611
1612 /**
1613 * @see #getSynonymsGroups()
1614 */
1615 @Transient
1616 public List<Taxon> getAllProParteSynonyms(){
1617 List<Taxon> result = new ArrayList<>();
1618
1619 for (TaxonRelationship rel : this.getRelationsToThisTaxon()){
1620 if (rel.getType().isAnySynonym()){
1621 result.add(rel.getFromTaxon());
1622 }
1623 }
1624 sortBySimpleTitleCacheComparator(result);
1625 return result;
1626 }
1627
1628 private void sortBySimpleTitleCacheComparator(List<Taxon> result) {
1629
1630 Comparator<Taxon> taxonComparator = new Comparator<Taxon>(){
1631
1632 @Override
1633 public int compare(Taxon o1, Taxon o2) {
1634
1635 if (o1.getTitleCache() == o2.getTitleCache()){
1636 return 0;
1637 }
1638 if (o1.getTitleCache() == null){
1639 return -1;
1640 }
1641 if (o2.getTitleCache() == null){
1642 return 1;
1643 }
1644 return o1.getTitleCache().compareTo(o2.getTitleCache());
1645
1646 }
1647 };
1648 Collections.sort(result, taxonComparator);
1649 }
1650
1651 /**
1652 * Returns the image gallery description. If no image gallery exists, a new one is created using the
1653 * defined title and adds the string "-Image Gallery" to the title.</BR>
1654 * If multiple image galleries exist an arbitrary one is choosen.
1655 * @param title
1656 * @return
1657 */
1658 public TaxonDescription getOrCreateImageGallery(String title){
1659 return getOrCreateImageGallery(title, true, false);
1660 }
1661
1662 /**
1663 * Returns the image gallery description. If no image gallery exists, a new one is created using the
1664 * defined title.</BR>
1665 * If onlyTitle == true we look only for an image gallery with this title, create a new one otherwise.
1666 * If multiple image galleries exist that match the conditions an arbitrary one is choosen.
1667 * @param title
1668 * @param onlyTitle
1669 * @param if true, the String "Image Gallery
1670 * @return
1671 */
1672 @Transient
1673 public TaxonDescription getOrCreateImageGallery(String title, boolean addImageGalleryToTitle, boolean onlyTitle){
1674 TaxonDescription result = null;
1675 String titleCache = (title == null) ? "Image Gallery" : title;
1676 if (title != null && addImageGalleryToTitle){
1677 titleCache = titleCache+ "-Image Gallery";
1678 }
1679 Set<TaxonDescription> descriptionSet = this.getDescriptions();
1680 for (TaxonDescription desc: descriptionSet){
1681 if (desc.isImageGallery()){
1682 if (onlyTitle && ! titleCache.equals(desc.getTitleCache())){
1683 continue;
1684 }
1685 result = desc;
1686 if (onlyTitle && titleCache.equals(desc.getTitleCache())){
1687 break;
1688 }
1689 }
1690 }
1691 if (result == null){
1692 result = TaxonDescription.NewInstance();
1693 result.setTitleCache(titleCache, true);
1694 this.addDescription(result);
1695 result.setImageGallery(true);
1696 }
1697 return result;
1698 }
1699
1700
1701 /**
1702 * Clones <i>this</i> taxon. This is a shortcut that enables to create
1703 * a new instance that differs only slightly from <i>this</i> taxon by
1704 * modifying only some of the attributes.<BR><BR>
1705 * The TaxonNodes are not cloned, the list is empty.<BR>
1706 * (CAUTION: this behaviour needs to be discussed and may change in future).<BR><BR>
1707 * The taxon relationships and synonym relationships are cloned <BR>
1708 *
1709 * @see eu.etaxonomy.cdm.model.taxon.TaxonBase#clone()
1710 * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
1711 * @see java.lang.Object#clone()
1712 */
1713 @Override
1714 public Object clone() {
1715 Taxon result;
1716 result = (Taxon)super.clone();
1717
1718 result.setRelationsFromThisTaxon(new HashSet<>());
1719
1720 for (TaxonRelationship fromRelationship : this.getRelationsFromThisTaxon()){
1721 TaxonRelationship newRelationship = (TaxonRelationship)fromRelationship.clone();
1722 newRelationship.setRelatedFrom(result);
1723 result.relationsFromThisTaxon.add(newRelationship);
1724 }
1725
1726 result.setRelationsToThisTaxon(new HashSet<>());
1727 for (TaxonRelationship toRelationship : this.getRelationsToThisTaxon()){
1728 TaxonRelationship newRelationship = (TaxonRelationship)toRelationship.clone();
1729 newRelationship.setRelatedTo(result);
1730 result.relationsToThisTaxon.add(newRelationship);
1731 }
1732
1733 //clone synonyms (is this wanted or should we remove synonyms
1734 result.synonyms = new HashSet<>();
1735 for (Synonym synonym : this.getSynonyms()){
1736 Synonym newSyn = (Synonym)synonym.clone();
1737 newSyn.setAcceptedTaxon(result);
1738 }
1739
1740 result.descriptions = new HashSet<>();
1741 for (TaxonDescription description : this.getDescriptions()){
1742 TaxonDescription newDescription = (TaxonDescription)description.clone();
1743 result.addDescription(newDescription);
1744 }
1745
1746 result.taxonNodes = new HashSet<>();
1747
1748 /*for (TaxonNode taxonNode : this.getTaxonNodes()){
1749 TaxonNode newTaxonNode = (TaxonNode)taxonNode.clone();
1750 newTaxonNode.setTaxon(result);
1751 result.addTaxonNode(newTaxonNode);
1752 }*/
1753
1754 return result;
1755
1756 }
1757
1758 public void clearDescriptions() {
1759 this.descriptions = new HashSet<>();
1760 }
1761
1762
1763 }