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