+ @Override\r
+ public Synonym changeRelatedTaxonToSynonym(Taxon fromTaxon, Taxon toTaxon, TaxonRelationshipType oldRelationshipType,\r
+ SynonymRelationshipType synonymRelationshipType) throws DataChangeNoRollbackException {\r
+ // Create new synonym using concept name\r
+ TaxonNameBase<?, ?> synonymName = fromTaxon.getName();\r
+ Synonym synonym = Synonym.NewInstance(synonymName, fromTaxon.getSec());\r
+\r
+ // Remove concept relation from taxon\r
+ toTaxon.removeTaxon(fromTaxon, oldRelationshipType);\r
+\r
+\r
+\r
+\r
+ // Create a new synonym for the taxon\r
+ SynonymRelationship synonymRelationship;\r
+ if (synonymRelationshipType != null\r
+ && synonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){\r
+ synonymRelationship = toTaxon.addHomotypicSynonym(synonym, null, null);\r
+ } else{\r
+ synonymRelationship = toTaxon.addHeterotypicSynonymName(synonymName);\r
+ }\r
+\r
+ this.saveOrUpdate(toTaxon);\r
+ //TODO: configurator and classification\r
+ TaxonDeletionConfigurator config = new TaxonDeletionConfigurator();\r
+ config.setDeleteNameIfPossible(false);\r
+ this.deleteTaxon(fromTaxon, config, null);\r
+ return synonymRelationship.getSynonym();\r
+\r
+ }\r
+ @Override\r
+ public List<String> isDeletable(TaxonBase taxonBase, DeleteConfiguratorBase config){\r
+ List<String> result = new ArrayList<String>();\r
+ Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(taxonBase);\r
+ if (taxonBase instanceof Taxon){\r
+ TaxonDeletionConfigurator taxonConfig = (TaxonDeletionConfigurator) config;\r
+ result = isDeletableForTaxon(references, taxonConfig);\r
+ }else{\r
+ SynonymDeletionConfigurator synonymConfig = (SynonymDeletionConfigurator) config;\r
+ result = isDeletableForSynonym(references, synonymConfig);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ private List<String> isDeletableForSynonym(Set<CdmBase> references, SynonymDeletionConfigurator config){\r
+ String message;\r
+ List<String> result = new ArrayList<String>();\r
+ for (CdmBase ref: references){\r
+ if (!(ref instanceof SynonymRelationship || ref instanceof Taxon || ref instanceof TaxonNameBase )){\r
+ message = "The Synonym can't be deleted as long as it is referenced by " + ref.getClass().getSimpleName() + " with id "+ ref.getId();\r
+ result.add(message);\r
+ }\r
+ }\r
+\r
+ return result;\r
+ }\r
+ private List<String> isDeletableForTaxon(Set<CdmBase> references, TaxonDeletionConfigurator config){\r
+ String message;\r
+ List<String> result = new ArrayList<String>();\r
+ for (CdmBase ref: references){\r
+ if (!(ref instanceof TaxonNameBase)){\r
+ if (!config.isDeleteSynonymRelations() && (ref instanceof SynonymRelationship)){\r
+ message = "The Taxon can't be deleted as long as it has synonyms.";\r
+ result.add(message);\r
+ }\r
+ if (!config.isDeleteDescriptions() && (ref instanceof DescriptionBase)){\r
+ message = "The Taxon can't be deleted as long as it has factual data.";\r
+ result.add(message);\r
+ }\r
+\r
+ if (!config.isDeleteTaxonNodes() && (ref instanceof TaxonNode)){\r
+ message = "The Taxon can't be deleted as long as it belongs to a taxon node.";\r
+ result.add(message);\r
+ }\r
+ if (!config.isDeleteTaxonRelationships() && (ref instanceof TaxonNode)){\r
+ if (!config.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship)ref).getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship)ref).getType().equals(TaxonRelationshipType.INVALID_DESIGNATION_FOR()))){\r
+ message = "The Taxon can't be deleted as long as it has misapplied names or invalid designations.";\r
+ result.add(message);\r
+ } else{\r
+ message = "The Taxon can't be deleted as long as it belongs to a taxon node.";\r
+ result.add(message);\r
+ }\r
+ }\r
+ if (ref instanceof PolytomousKeyNode){\r
+ message = "The Taxon can't be deleted as long as it is referenced by a polytomous key node.";\r
+ result.add(message);\r
+ }\r
+\r
+ if (HibernateProxyHelper.isInstanceOf(ref, IIdentificationKey.class)){\r
+ message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";\r
+ result.add(message);\r
+\r
+ }\r
+\r
+\r
+ /* //PolytomousKeyNode\r
+ if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){\r
+ String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";\r
+ return message;\r
+ }*/\r
+\r
+ //TaxonInteraction\r
+ if (ref.isInstanceOf(TaxonInteraction.class)){\r
+ message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";\r
+ result.add(message);\r
+ }\r
+\r
+ //TaxonInteraction\r
+ if (ref.isInstanceOf(DeterminationEvent.class)){\r
+ message = "Taxon can't be deleted as it is used in a determination event";\r
+ result.add(message);\r
+ }\r
+\r
+ }\r
+\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public IncludedTaxaDTO listIncludedTaxa(UUID taxonUuid, IncludedTaxonConfiguration config) {\r
+ IncludedTaxaDTO result = new IncludedTaxaDTO(taxonUuid);\r
+\r
+ //preliminary implementation\r
+\r
+ Set<Taxon> taxa = new HashSet<Taxon>();\r
+ TaxonBase taxonBase = find(taxonUuid);\r
+ if (taxonBase == null){\r
+ return new IncludedTaxaDTO();\r
+ }else if (taxonBase.isInstanceOf(Taxon.class)){\r
+ Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);\r
+ taxa.add(taxon);\r
+ }else if (taxonBase.isInstanceOf(Synonym.class)){\r
+ //TODO partial synonyms ??\r
+ //TODO synonyms in general\r
+ Synonym syn = CdmBase.deproxy(taxonBase, Synonym.class);\r
+ taxa.addAll(syn.getAcceptedTaxa());\r
+ }else{\r
+ throw new IllegalArgumentException("Unhandled class " + taxonBase.getClass().getSimpleName());\r
+ }\r
+\r
+ Set<Taxon> related = makeRelatedIncluded(taxa, result, config);\r
+ int i = 0;\r
+ while((! related.isEmpty()) && i++ < 100){ //to avoid\r
+ related = makeRelatedIncluded(related, result, config);\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Computes all children and conceptually congruent and included taxa and adds them to the existingTaxa\r
+ * data structure.\r
+ * @return the set of conceptually related taxa for further use\r
+ */\r
+ /**\r
+ * @param uncheckedTaxa\r
+ * @param existingTaxa\r
+ * @param config\r
+ * @return\r
+ */\r
+ private Set<Taxon> makeRelatedIncluded(Set<Taxon> uncheckedTaxa, IncludedTaxaDTO existingTaxa, IncludedTaxonConfiguration config) {\r
+\r
+ //children\r
+ Set<TaxonNode> taxonNodes = new HashSet<TaxonNode>();\r
+ for (Taxon taxon: uncheckedTaxa){\r
+ taxonNodes.addAll(taxon.getTaxonNodes());\r
+ }\r
+\r
+ Set<Taxon> children = new HashSet<Taxon>();\r
+ if (! config.onlyCongruent){\r
+ for (TaxonNode node: taxonNodes){\r
+ List<TaxonNode> childNodes = nodeService.loadChildNodesOfTaxonNode(node, null, true, false);\r
+ for (TaxonNode child : childNodes){\r
+ children.add(child.getTaxon());\r
+ }\r
+ }\r
+ children.remove(null); // just to be on the save side\r
+ }\r
+\r
+ Iterator<Taxon> it = children.iterator();\r
+ while(it.hasNext()){\r
+ UUID uuid = it.next().getUuid();\r
+ if (existingTaxa.contains(uuid)){\r
+ it.remove();\r
+ }else{\r
+ existingTaxa.addIncludedTaxon(uuid, new ArrayList<UUID>(), false);\r
+ }\r
+ }\r
+\r
+ //concept relations\r
+ Set<Taxon> uncheckedAndChildren = new HashSet<Taxon>(uncheckedTaxa);\r
+ uncheckedAndChildren.addAll(children);\r
+\r
+ Set<Taxon> relatedTaxa = makeConceptIncludedTaxa(uncheckedAndChildren, existingTaxa, config);\r
+\r
+\r
+ Set<Taxon> result = new HashSet<Taxon>(relatedTaxa);\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Computes all conceptually congruent or included taxa and adds them to the existingTaxa data structure.\r
+ * @return the set of these computed taxa\r
+ */\r
+ private Set<Taxon> makeConceptIncludedTaxa(Set<Taxon> unchecked, IncludedTaxaDTO existingTaxa, IncludedTaxonConfiguration config) {\r
+ Set<Taxon> result = new HashSet<Taxon>();\r
+\r
+ for (Taxon taxon : unchecked){\r
+ Set<TaxonRelationship> fromRelations = taxon.getRelationsFromThisTaxon();\r
+ Set<TaxonRelationship> toRelations = taxon.getRelationsToThisTaxon();\r
+\r
+ for (TaxonRelationship fromRel : fromRelations){\r
+ if (config.includeDoubtful == false && fromRel.isDoubtful()){\r
+ continue;\r
+ }\r
+ if (fromRel.getType().equals(TaxonRelationshipType.CONGRUENT_TO()) ||\r
+ !config.onlyCongruent && fromRel.getType().equals(TaxonRelationshipType.INCLUDES()) ||\r
+ !config.onlyCongruent && fromRel.getType().equals(TaxonRelationshipType.CONGRUENT_OR_INCLUDES())\r
+ ){\r
+ result.add(fromRel.getToTaxon());\r
+ }\r
+ }\r
+\r
+ for (TaxonRelationship toRel : toRelations){\r
+ if (config.includeDoubtful == false && toRel.isDoubtful()){\r
+ continue;\r
+ }\r
+ if (toRel.getType().equals(TaxonRelationshipType.CONGRUENT_TO())){\r
+ result.add(toRel.getFromTaxon());\r
+ }\r
+ }\r
+ }\r
+\r
+ Iterator<Taxon> it = result.iterator();\r
+ while(it.hasNext()){\r
+ UUID uuid = it.next().getUuid();\r
+ if (existingTaxa.contains(uuid)){\r
+ it.remove();\r
+ }else{\r
+ existingTaxa.addIncludedTaxon(uuid, new ArrayList<UUID>(), false);\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+ @Override\r
+ public List<TaxonBase> findTaxaByName(MatchingTaxonConfigurator config){\r
+ List<TaxonBase> taxonList = dao.getTaxaByName(true, false, false, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, 0, config.getPropertyPath());\r
+ return taxonList;\r
+ }\r
+\r
+\r
+}\r