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