X-Git-Url: https://dev.e-taxonomy.eu/gitweb/cdmlib.git/blobdiff_plain/bb60e565c58c36c9e0b8226d831db82408eb4d80..e1c0af0a692e6a2d9668ea7096e2b04665c0d590:/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/TaxonServiceImpl.java diff --git a/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/TaxonServiceImpl.java b/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/TaxonServiceImpl.java index a42d970e3c..dcb0b8f279 100644 --- a/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/TaxonServiceImpl.java +++ b/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/TaxonServiceImpl.java @@ -21,6 +21,8 @@ import java.util.Map; import java.util.Set; import java.util.UUID; +import javax.persistence.EntityNotFoundException; + import org.apache.log4j.Logger; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.queryParser.ParseException; @@ -35,11 +37,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase; import eu.etaxonomy.cdm.api.service.config.IFindTaxaAndNamesConfigurator; +import eu.etaxonomy.cdm.api.service.config.IncludedTaxonConfiguration; import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator; import eu.etaxonomy.cdm.api.service.config.SynonymDeletionConfigurator; import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator; import eu.etaxonomy.cdm.api.service.config.TaxonNodeDeletionConfigurator.ChildHandling; +import eu.etaxonomy.cdm.api.service.dto.IncludedTaxaDTO; import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException; import eu.etaxonomy.cdm.api.service.exception.HomotypicalGroupChangeException; import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException; @@ -91,13 +96,16 @@ import eu.etaxonomy.cdm.model.molecular.DnaSample; import eu.etaxonomy.cdm.model.molecular.Sequence; import eu.etaxonomy.cdm.model.molecular.SingleRead; import eu.etaxonomy.cdm.model.name.HomotypicalGroup; +import eu.etaxonomy.cdm.model.name.NameRelationship; import eu.etaxonomy.cdm.model.name.Rank; import eu.etaxonomy.cdm.model.name.TaxonNameBase; import eu.etaxonomy.cdm.model.name.ZoologicalName; import eu.etaxonomy.cdm.model.occurrence.DerivedUnit; +import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent; import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase; import eu.etaxonomy.cdm.model.reference.Reference; import eu.etaxonomy.cdm.model.taxon.Classification; +import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode; import eu.etaxonomy.cdm.model.taxon.Synonym; import eu.etaxonomy.cdm.model.taxon.SynonymRelationship; import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType; @@ -111,6 +119,7 @@ import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao; import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer; import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao; import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao; +import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao; import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao; import eu.etaxonomy.cdm.persistence.fetch.CdmFetch; import eu.etaxonomy.cdm.persistence.query.MatchMode; @@ -142,6 +151,9 @@ public class TaxonServiceImpl extends IdentifiableServiceBase getAllSynonyms(int limit, int start) { - return dao.getAllSynonyms(limit, start); - } - - /** - * FIXME Candidate for harmonization - * list(Taxon.class, ...) - * (non-Javadoc) - * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int) - */ - @Override - public List getAllTaxa(int limit, int start) { - return dao.getAllTaxa(limit, start); - } - /** * FIXME Candidate for harmonization * merge with getRootTaxa(Reference sec, ..., ...) @@ -212,18 +205,11 @@ public class TaxonServiceImpl extends IdentifiableServiceBase getRootTaxa(Rank rank, Reference sec, boolean onlyWithChildren,boolean withMisapplications, List propertyPaths) { return dao.getRootTaxa(rank, sec, null, onlyWithChildren, withMisapplications, propertyPaths); } - /* (non-Javadoc) - * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int) - */ @Override public List getAllRelationships(int limit, int start){ return dao.getAllRelationships(limit, start); @@ -271,11 +257,11 @@ public class TaxonServiceImpl extends IdentifiableServiceBase acceptedName = acceptedTaxon.getName(); TaxonNameBase synonymName = synonym.getName(); HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup(); @@ -290,10 +276,12 @@ public class TaxonServiceImpl extends IdentifiableServiceBase heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup); + Set basionymsAndReplacedSynonyms = synonymHomotypicGroup.getBasionymAndReplacedSynonymRelations(); for (Synonym heteroSynonym : heteroSynonyms){ if (synonym.equals(heteroSynonym)){ acceptedTaxon.removeSynonym(heteroSynonym, false); + }else{ //move synonyms in same homotypic group to new accepted taxon heteroSynonym.replaceAcceptedTaxon(newAcceptedTaxon, relTypeForGroup, copyCitationInfo, citation, microCitation); @@ -301,12 +289,14 @@ public class TaxonServiceImpl extends IdentifiableServiceBase synonymName = synonym.getName(); - // remove synonym from taxon + /* // remove synonym from taxon toTaxon.removeSynonym(synonym); - +*/ // Create a taxon with synonym name Taxon fromTaxon = Taxon.NewInstance(synonymName, null); @@ -334,7 +324,8 @@ public class TaxonServiceImpl extends IdentifiableServiceBase(pageNumber, numberOfResults, pageSize, results); } + @Override + public List listAcceptedTaxaFor(UUID synonymUuid, UUID classificationUuid, Integer pageSize, Integer pageNumber, + List orderHints, List propertyPaths){ + return pageAcceptedTaxaFor(synonymUuid, classificationUuid, pageSize, pageNumber, orderHints, propertyPaths).getRecords(); + } + + @Override + public Pager pageAcceptedTaxaFor(UUID synonymUuid, UUID classificationUuid, Integer pageSize, Integer pageNumber, + List orderHints, List propertyPaths){ + + List list = new ArrayList(); + Long count = 0l; + + Synonym synonym = null; + + try { + synonym = (Synonym) dao.load(synonymUuid); + } catch (ClassCastException e){ + throw new EntityNotFoundException("The TaxonBase entity referenced by " + synonymUuid + " is not a Synonmy"); + } catch (NullPointerException e){ + throw new EntityNotFoundException("No TaxonBase entity found for " + synonymUuid); + } + + Classification classificationFilter = null; + if(classificationUuid != null){ + try { + classificationFilter = classificationDao.load(classificationUuid); + } catch (NullPointerException e){ + throw new EntityNotFoundException("No Classification entity found for " + classificationUuid); + } + if(classificationFilter == null){ + + } + } + + count = dao.countAcceptedTaxaFor(synonym, classificationFilter) ; + if(count > (pageSize * pageNumber)){ + list = dao.listAcceptedTaxaFor(synonym, classificationFilter, pageSize, pageNumber, orderHints, propertyPaths); + } + + return new DefaultPagerImpl(pageNumber, count.intValue(), pageSize, list); + } + + /** * @param taxon * @param includeRelationships @@ -544,6 +579,10 @@ public class TaxonServiceImpl extends IdentifiableServiceBase(pageNumber, numberOfResults, pageSize, results); } + /* (non-Javadoc) + * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List) + */ + @Override + public List> getSynonymsByHomotypicGroup(Taxon taxon, List propertyPaths){ + List> result = new ArrayList>(); + Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths); + + //homotypic + result.add(t.getHomotypicSynonymsByHomotypicGroup()); + + //heterotypic + List homotypicalGroups = t.getHeterotypicSynonymyGroups(); + for(HomotypicalGroup homotypicalGroup : homotypicalGroups){ + result.add(t.getSynonymsInGroup(homotypicalGroup)); + } + + return result; + + } + /* (non-Javadoc) * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List) */ @@ -732,10 +792,8 @@ public class TaxonServiceImpl extends IdentifiableServiceBase configurator.getPageSize() * configurator.getPageNumber()){ - List commonNameResults = dao.getTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas(), configurator.getPageSize(), configurator.getPageNumber(), configurator.getTaxonPropertyPath()); - for( Object[] entry : commonNameResults ) { - taxa.add((TaxonBase) entry[0]); - } + List commonNameResults = dao.getTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas(), configurator.getPageSize(), configurator.getPageNumber(), configurator.getTaxonPropertyPath()); + taxa.addAll(commonNameResults); } if(taxa != null){ results.addAll(taxa); @@ -791,21 +849,26 @@ public class TaxonServiceImpl extends IdentifiableServiceBase propertyPath) { + logger.trace("listMedia() - START"); + Set taxa = new HashSet(); List taxonMedia = new ArrayList(); + List nonImageGalleryImages = new ArrayList(); if (limitToGalleries == null) { limitToGalleries = false; } // --- resolve related taxa - if (includeRelationships != null) { + if (includeRelationships != null && ! includeRelationships.isEmpty()) { + logger.trace("listMedia() - resolve related taxa"); taxa = listRelatedTaxa(taxon, includeRelationships, null, null, null, null); } taxa.add((Taxon) dao.load(taxon.getUuid())); if(includeTaxonDescriptions != null && includeTaxonDescriptions){ + logger.trace("listMedia() - includeTaxonDescriptions"); List taxonDescriptions = new ArrayList(); // --- TaxonDescriptions for (Taxon t : taxa) { @@ -815,14 +878,23 @@ public class TaxonServiceImpl extends IdentifiableServiceBase specimensOrObservations = new HashSet(); // --- Specimens for (Taxon t : taxa) { @@ -876,6 +948,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase nameDescriptions = new HashSet(); for (Taxon t : taxa) { @@ -893,7 +966,12 @@ public class TaxonServiceImpl extends IdentifiableServiceBase 0){ - String message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion or define a classification where it should be deleted or adapt the taxon deletion configurator."; - throw new ReferencedObjectUndeletableException(message); - } - }else{ - if (taxon.getTaxonNodes().size() != 0){ - Set nodes = taxon.getTaxonNodes(); - Iterator iterator = nodes.iterator(); - TaxonNode node = null; - boolean deleteChildren; - if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){ - deleteChildren = true; - }else { - deleteChildren = false; - } - boolean success = true; - if (!config.isDeleteInAllClassifications() && !(classification == null)){ - while (iterator.hasNext()){ - node = iterator.next(); - if (node.getClassification().equals(classification)){ - break; - } - node = null; - } - if (node != null){ - success =taxon.removeTaxonNode(node, deleteChildren); - } else { - String message = "Taxon is not used in defined classification"; - throw new DataChangeNoRollbackException(message); - } - } else if (config.isDeleteInAllClassifications()){ - success = taxon.removeTaxonNodes(deleteChildren); - } - if (!success){ - String message = "The taxon node could not be deleted."; - throw new DataChangeNoRollbackException(message); - } - } - } - + List referencedObjects = isDeletable(taxon, config); - // SynonymRelationShip + if (referencedObjects.isEmpty()){ + // --- DeleteSynonymRelations if (config.isDeleteSynonymRelations()){ boolean removeSynonymNameFromHomotypicalGroup = false; - for (SynonymRelationship synRel : taxon.getSynonymRelations()){ + // use tmp Set to avoid concurrent modification + Set synRelsToDelete = new HashSet(); + synRelsToDelete.addAll(taxon.getSynonymRelations()); + for (SynonymRelationship synRel : synRelsToDelete){ Synonym synonym = synRel.getSynonym(); + // taxon.removeSynonymRelation will set the accepted taxon and the synonym to NULL + // this will cause hibernate to delete the relationship since + // the SynonymRelationship field on both is annotated with removeOrphan + // so no further explicit deleting of the relationship should be done here taxon.removeSynonymRelation(synRel, removeSynonymNameFromHomotypicalGroup); + + // --- DeleteSynonymsIfPossible if (config.isDeleteSynonymsIfPossible()){ //TODO which value boolean newHomotypicGroupIfNeeded = true; SynonymDeletionConfigurator synConfig = new SynonymDeletionConfigurator(); - deleteSynonym(synonym, taxon, synConfig); - }else{ - deleteSynonymRelationships(synonym, taxon); } + // relationship will be deleted by hibernate automatically, + // see comment above and http://dev.e-taxonomy.eu/trac/ticket/3797 + // else{ + // deleteSynonymRelationships(synonym, taxon); + // } } } - // TaxonRelationship + // --- DeleteTaxonRelationships if (! config.isDeleteTaxonRelationships()){ if (taxon.getTaxonRelations().size() > 0){ - String message = "Taxon can't be deleted as it is related to another taxon. Remove taxon from all relations to other taxa prior to deletion."; - throw new ReferencedObjectUndeletableException(message); + String message = "Taxon can't be deleted as it is related to another taxon. " + + "Remove taxon from all relations to other taxa prior to deletion."; + // throw new ReferencedObjectUndeletableException(message); } } else{ - for (TaxonRelationship taxRel: taxon.getTaxonRelations()){ - - - - if (config.isDeleteMisappliedNamesAndInvalidDesignations()){ - if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR()) || taxRel.getType().equals(TaxonRelationshipType.INVALID_DESIGNATION_FOR())){ - if (taxon.equals(taxRel.getToTaxon())){ - this.deleteTaxon(taxRel.getFromTaxon(), config, classification); - } - } - } - taxon.removeTaxonRelation(taxRel); - /*if (taxFrom.equals(taxon)){ - try{ - this.deleteTaxon(taxTo, taxConf, classification); - } catch(DataChangeNoRollbackException e){ - logger.debug("A related taxon will not be deleted." + e.getMessage()); - } - } else { - try{ - this.deleteTaxon(taxFrom, taxConf, classification); - } catch(DataChangeNoRollbackException e){ - logger.debug("A related taxon will not be deleted." + e.getMessage()); - } - - }*/ - } - } + for (TaxonRelationship taxRel: taxon.getTaxonRelations()){ + if (config.isDeleteMisappliedNamesAndInvalidDesignations()){ + if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR()) || taxRel.getType().equals(TaxonRelationshipType.INVALID_DESIGNATION_FOR())){ + if (taxon.equals(taxRel.getToTaxon())){ + this.deleteTaxon(taxRel.getFromTaxon(), config, classification); + } + } + } + taxon.removeTaxonRelation(taxRel); + /*if (taxFrom.equals(taxon)){ + try{ + this.deleteTaxon(taxTo, taxConf, classification); + } catch(DataChangeNoRollbackException e){ + logger.debug("A related taxon will not be deleted." + e.getMessage()); + } + } else { + try{ + this.deleteTaxon(taxFrom, taxConf, classification); + } catch(DataChangeNoRollbackException e){ + logger.debug("A related taxon will not be deleted." + e.getMessage()); + } + }*/ + } + } - - // TaxonDescription - Set descriptions = taxon.getDescriptions(); - - for (TaxonDescription desc: descriptions){ - if (config.isDeleteDescriptions()){ - //TODO use description delete configurator ? - //FIXME check if description is ALWAYS deletable - taxon.removeDescription(desc); - descriptionService.delete(desc); - }else{ - if (desc.getDescribedSpecimenOrObservation() != null){ - String message = "Taxon can't be deleted as it is used in a TaxonDescription" + - " which also describes specimens or abservations"; - throw new ReferencedObjectUndeletableException(message); + if (config.isDeleteDescriptions()){ + Set descriptions = taxon.getDescriptions(); + List removeDescriptions = new ArrayList(); + for (TaxonDescription desc: descriptions){ + //TODO use description delete configurator ? + //FIXME check if description is ALWAYS deletable + if (desc.getDescribedSpecimenOrObservation() != null){ + String message = "Taxon can't be deleted as it is used in a TaxonDescription" + + " which also describes specimens or abservations"; + //throw new ReferencedObjectUndeletableException(message); + } + removeDescriptions.add(desc); + descriptionService.delete(desc); + + } + for (TaxonDescription desc: removeDescriptions){ + taxon.removeDescription(desc); + } + } + + + /* //check references with only reverse mapping + String message = checkForReferences(taxon); + if (message != null){ + //throw new ReferencedObjectUndeletableException(message.toString()); + }*/ + + if (! config.isDeleteTaxonNodes() || (!config.isDeleteInAllClassifications() && classification == null )){ + //if (taxon.getTaxonNodes().size() > 0){ + // message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion or define a classification where it should be deleted or adapt the taxon deletion configurator."; + // throw new ReferencedObjectUndeletableException(message); + //} + }else{ + if (taxon.getTaxonNodes().size() != 0){ + Set nodes = taxon.getTaxonNodes(); + Iterator iterator = nodes.iterator(); + TaxonNode node = null; + boolean deleteChildren; + if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){ + deleteChildren = true; + }else { + deleteChildren = false; + } + boolean success = true; + if (!config.isDeleteInAllClassifications() && !(classification == null)){ + while (iterator.hasNext()){ + node = iterator.next(); + if (node.getClassification().equals(classification)){ + break; + } + node = null; + } + if (node != null){ + success =taxon.removeTaxonNode(node, deleteChildren); + nodeService.delete(node); + } else { + result.setError(); + result.addException(new Exception("The taxon can not be deleted because it is not used in defined classification.")); + } + } else if (config.isDeleteInAllClassifications()){ + Set nodesList = new HashSet(); + nodesList.addAll(taxon.getTaxonNodes()); + + for (ITaxonTreeNode treeNode: nodesList){ + TaxonNode taxonNode = (TaxonNode) treeNode; + if(!deleteChildren){ + /* Object[] childNodes = taxonNode.getChildNodes().toArray(); + //nodesList.addAll(taxonNode.getChildNodes()); + for (Object childNode: childNodes){ + TaxonNode childNodeCast = (TaxonNode) childNode; + deleteTaxon(childNodeCast.getTaxon(), config, classification); + + } + + /*for (TaxonNode childNode: taxonNode.getChildNodes()){ + deleteTaxon(childNode.getTaxon(), config, classification); + + } + // taxon.removeTaxonNode(taxonNode); + //nodeService.delete(taxonNode); + } else{ + */ + Object[] childNodes = taxonNode.getChildNodes().toArray(); + for (Object childNode: childNodes){ + TaxonNode childNodeCast = (TaxonNode) childNode; + taxonNode.getParent().addChildNode(childNodeCast, childNodeCast.getReference(), childNodeCast.getMicroReference()); + } + + //taxon.removeTaxonNode(taxonNode); } } + config.getTaxonNodeConfig().setDeleteTaxon(false); + DeleteResult resultNodes = nodeService.deleteTaxonNodes(nodesList, config); + if (!resultNodes.isOk()){ + result.addExceptions(resultNodes.getExceptions()); + result.setStatus(resultNodes.getStatus()); } + } + if (!success){ + result.setError(); + result.addException(new Exception("The taxon can not be deleted because the taxon node can not be removed.")); + } + } + } - //check references with only reverse mapping - String message = checkForReferences(taxon); - if (message != null){ - throw new ReferencedObjectUndeletableException(message.toString()); - } + //PolytomousKey TODO //TaxonNameBase if (config.isDeleteNameIfPossible()){ - try { - - //TaxonNameBase name = nameService.find(taxon.getName().getUuid()); - TaxonNameBase name = (TaxonNameBase)HibernateProxyHelper.deproxy(taxon.getName()); - //check whether taxon will be deleted or not - if (taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0){ - taxon = (Taxon) HibernateProxyHelper.deproxy(taxon); - name.removeTaxonBase(taxon); - nameService.save(name); - nameService.delete(name, config.getNameDeletionConfig()); - } - } catch (ReferencedObjectUndeletableException e) { - //do nothing - if (logger.isDebugEnabled()){logger.debug("Name could not be deleted");} - - } + + + //TaxonNameBase name = nameService.find(taxon.getName().getUuid()); + TaxonNameBase name = (TaxonNameBase)HibernateProxyHelper.deproxy(taxon.getName()); + //check whether taxon will be deleted or not + if ((taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0) && name != null ){ + taxon = (Taxon) HibernateProxyHelper.deproxy(taxon); + name.removeTaxonBase(taxon); + nameService.saveOrUpdate(name); + DeleteResult nameResult = new DeleteResult(); + + nameResult = nameService.delete(name, config.getNameDeletionConfig()); + + if (nameResult.isError()){ + //result.setError(); + result.addRelatedObject(name); + result.addExceptions(nameResult.getExceptions()); + } + + } + } - + // TaxonDescription /* Set descriptions = taxon.getDescriptions(); @@ -1095,7 +1229,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase0){ @@ -1105,109 +1239,168 @@ public class TaxonServiceImpl extends IdentifiableServiceBase exceptions = new ArrayList(); + for (String message: referencedObjects){ + ReferencedObjectUndeletableException exception = new ReferencedObjectUndeletableException(message); + exceptions.add(exception); + } + result.addExceptions(exceptions); + result.setError(); + + } + return result; } - + private String checkForReferences(Taxon taxon){ - Set referencingObjects = genericDao.getReferencingObjects(taxon); + Set referencingObjects = genericDao.getReferencingObjects(taxon); for (CdmBase referencingObject : referencingObjects){ //IIdentificationKeys (Media, Polytomous, MultiAccess) if (HibernateProxyHelper.isInstanceOf(referencingObject, IIdentificationKey.class)){ String message = "Taxon" + taxon.getTitleCache() + "can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name"; - + return message; } - //PolytomousKeyNode + /* //PolytomousKeyNode if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){ String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node"; return message; - } + }*/ //TaxonInteraction if (referencingObject.isInstanceOf(TaxonInteraction.class)){ String message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2"; return message; } + + //TaxonInteraction + if (referencingObject.isInstanceOf(DeterminationEvent.class)){ + String message = "Taxon can't be deleted as it is used in a determination event"; + return message; + } + } + + referencingObjects = null; return null; } - + + private boolean checkForPolytomousKeys(Taxon taxon){ + boolean result = false; + List list = genericDao.getCdmBasesByFieldAndClass(PolytomousKeyNode.class, "taxon", taxon); + if (!list.isEmpty()) { + result = true; + } + return result; + } + @Transactional(readOnly = false) public UUID delete(Synonym syn){ - UUID result = syn.getUuid(); - this.deleteSynonym(syn, null); - return result; + UUID result = syn.getUuid(); + this.deleteSynonym(syn, null); + return result; } - + + + /* (non-Javadoc) * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean) */ @Transactional(readOnly = false) @Override - public void deleteSynonym(Synonym synonym, SynonymDeletionConfigurator config) { - deleteSynonym(synonym, null, config); - - } - + public DeleteResult deleteSynonym(Synonym synonym, SynonymDeletionConfigurator config) { + return deleteSynonym(synonym, null, config); + + } - /* (non-Javadoc) + + /* (non-Javadoc) * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean) */ @Transactional(readOnly = false) @Override - public void deleteSynonym(Synonym synonym, Taxon taxon, SynonymDeletionConfigurator config) { - if (synonym == null){ - return; + public DeleteResult deleteSynonym(Synonym synonym, Taxon taxon, SynonymDeletionConfigurator config) { + DeleteResult result = new DeleteResult(); + if (synonym == null){ + result.setAbort(); + return result; } + if (config == null){ - config = new SynonymDeletionConfigurator(); + config = new SynonymDeletionConfigurator(); } - synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class); + List messages = isDeletable(synonym, config); + + + if (messages.isEmpty()){ + synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class); + + //remove synonymRelationship + Set taxonSet = new HashSet(); + if (taxon != null){ + taxonSet.add(taxon); + }else{ + taxonSet.addAll(synonym.getAcceptedTaxa()); + } + for (Taxon relatedTaxon : taxonSet){ + // dao.deleteSynonymRelationships(synonym, relatedTaxon); + relatedTaxon.removeSynonym(synonym, config.isNewHomotypicGroupIfNeeded()); + } + this.saveOrUpdate(synonym); + + //TODO remove name from homotypical group? + + //remove synonym (if necessary) + + UUID uuid = null; + if (synonym.getSynonymRelations().isEmpty()){ + TaxonNameBase name = synonym.getName(); + synonym.setName(null); + uuid = dao.delete(synonym); + + //remove name if possible (and required) + if (name != null && config.isDeleteNameIfPossible()){ + + nameService.delete(name, config.getNameDeletionConfig()); - //remove synonymRelationship - Set taxonSet = new HashSet(); - if (taxon != null){ - taxonSet.add(taxon); - }else{ - taxonSet.addAll(synonym.getAcceptedTaxa()); - } - for (Taxon relatedTaxon : taxonSet){ -// dao.deleteSynonymRelationships(synonym, relatedTaxon); - relatedTaxon.removeSynonym(synonym, config.isNewHomotypicGroupIfNeeded()); - } - this.saveOrUpdate(synonym); - - //TODO remove name from homotypical group? - - //remove synonym (if necessary) - - - if (synonym.getSynonymRelations().isEmpty()){ - TaxonNameBase name = synonym.getName(); - synonym.setName(null); - dao.delete(synonym); - - //remove name if possible (and required) - if (name != null && config.isDeleteNameIfPossible()){ - try{ - nameService.delete(name, config.getNameDeletionConfig()); - }catch (ReferencedObjectUndeletableException ex){ - System.err.println("Name wasn't deleted as it is referenced"); - if (logger.isDebugEnabled()) { - logger.debug("Name wasn't deleted as it is referenced"); - } } + + }else { + result.setError(); + result.addException(new ReferencedObjectUndeletableException("Synonym can not be deleted it is used in a synonymRelationship.")); + return result; } + + return result; + }else{ + List exceptions = new ArrayList(); + for (String message :messages){ + exceptions.add(new ReferencedObjectUndeletableException(message)); + } + result.setError(); + result.addExceptions(exceptions); + return result; } + + } @@ -1506,7 +1699,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase orderHints, List propertyPaths) throws CorruptIndexException, IOException, ParseException { - LuceneSearch luceneSearch = prepareFindByFullTextSearch(clazz, queryString, classification, languages, highlightFragments); + LuceneSearch luceneSearch = prepareFindByFullTextSearch(clazz, queryString, classification, languages, highlightFragments, null); // --- execute search TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber); @@ -1552,18 +1745,21 @@ public class TaxonServiceImpl extends IdentifiableServiceBase clazz, String queryString, Classification classification, List languages, - boolean highlightFragments) { + boolean highlightFragments, SortField[] sortFields) { BooleanQuery finalQuery = new BooleanQuery(); BooleanQuery textQuery = new BooleanQuery(); LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, TaxonBase.class); QueryFactory taxonBaseQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonBase.class); - SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)}; + if(sortFields == null){ + sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)}; + } luceneSearch.setSortFields(sortFields); // ---- search criteria @@ -1596,16 +1792,17 @@ public class TaxonServiceImpl extends IdentifiableServiceBasedirect, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id *
  • inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id
  • *
      - * * @param queryString * @param classification * @param languages * @param highlightFragments + * @param sortFields TODO + * * @return * @throws IOException */ protected LuceneSearch prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge, String queryString, Classification classification, List languages, - boolean highlightFragments) throws IOException { + boolean highlightFragments, SortField[] sortFields) throws IOException { String fromField; String queryTermField; @@ -1634,7 +1831,9 @@ public class TaxonServiceImpl extends IdentifiableServiceBase 0; List luceneSearches = new ArrayList(); @@ -1747,7 +1960,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase orderHints, List propertyPaths) throws CorruptIndexException, IOException, ParseException, LuceneMultiSearchException { LuceneSearch luceneSearchByDescriptionElement = prepareByDescriptionElementFullTextSearch(null, queryString, classification, null, languages, highlightFragments); - LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments); + LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments, null); LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider, luceneSearchByDescriptionElement, luceneSearchByTaxonBase); @@ -2196,7 +2412,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase nodes = taxon.getTaxonNodes(); - List taxonNames = new ArrayList(); + List taxonNames = new ArrayList(); for (TaxonNode node: nodes){ // HashMap synonymsGenus = new HashMap(); // Changed this to be able to store the idInSource to a genusName @@ -2549,8 +2765,6 @@ public class TaxonServiceImpl extends IdentifiableServiceBase sourceReference = syn.getSec(); if (sourceReference == null){ - logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec()); + logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec()); sourceReference = taxon.getSec(); } @@ -2726,7 +2937,6 @@ public class TaxonServiceImpl extends IdentifiableServiceBase listClassifications(TaxonBase taxonBase, Integer limit, Integer start, List propertyPaths) { @@ -2837,9 +3044,257 @@ public class TaxonServiceImpl extends IdentifiableServiceBase synonymName = fromTaxon.getName(); + Synonym synonym = Synonym.NewInstance(synonymName, fromTaxon.getSec()); + + // Remove concept relation from taxon + toTaxon.removeTaxon(fromTaxon, oldRelationshipType); + + + + + // Create a new synonym for the taxon + SynonymRelationship synonymRelationship; + if (synonymRelationshipType != null + && synonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){ + synonymRelationship = toTaxon.addHomotypicSynonym(synonym, null, null); + } else{ + synonymRelationship = toTaxon.addHeterotypicSynonymName(synonymName); + } + + this.saveOrUpdate(toTaxon); + //TODO: configurator and classification + TaxonDeletionConfigurator config = new TaxonDeletionConfigurator(); + config.setDeleteNameIfPossible(false); + this.deleteTaxon(fromTaxon, config, null); + return synonymRelationship.getSynonym(); + + } + @Override + public List isDeletable(TaxonBase taxonBase, DeleteConfiguratorBase config){ + List result = new ArrayList(); + Set references = commonService.getReferencingObjectsForDeletion(taxonBase); + if (taxonBase instanceof Taxon){ + TaxonDeletionConfigurator taxonConfig = (TaxonDeletionConfigurator) config; + result = isDeletableForTaxon(references, taxonConfig); + }else{ + SynonymDeletionConfigurator synonymConfig = (SynonymDeletionConfigurator) config; + result = isDeletableForSynonym(references, synonymConfig); + } + return result; + } + + private List isDeletableForSynonym(Set references, SynonymDeletionConfigurator config){ + String message; + List result = new ArrayList(); + for (CdmBase ref: references){ + if (!(ref instanceof SynonymRelationship || ref instanceof Taxon || ref instanceof TaxonNameBase )){ + message = "The Synonym can't be deleted as long as it is referenced by " + ref.getClass().getSimpleName() + " with id "+ ref.getId(); + result.add(message); + } + } + + return result; + } + private List isDeletableForTaxon(Set references, TaxonDeletionConfigurator config){ + String message; + List result = new ArrayList(); + for (CdmBase ref: references){ + if (!(ref instanceof TaxonNameBase)){ + if (!config.isDeleteSynonymRelations() && (ref instanceof SynonymRelationship)){ + message = "The Taxon can't be deleted as long as it has synonyms."; + result.add(message); + } + if (!config.isDeleteDescriptions() && (ref instanceof DescriptionBase)){ + message = "The Taxon can't be deleted as long as it has factual data."; + result.add(message); + } + + if (!config.isDeleteTaxonNodes() && (ref instanceof TaxonNode)){ + message = "The Taxon can't be deleted as long as it belongs to a taxon node."; + result.add(message); + } + if (!config.isDeleteTaxonRelationships() && (ref instanceof TaxonNode)){ + if (!config.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship)ref).getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship)ref).getType().equals(TaxonRelationshipType.INVALID_DESIGNATION_FOR()))){ + message = "The Taxon can't be deleted as long as it has misapplied names or invalid designations."; + result.add(message); + } else{ + message = "The Taxon can't be deleted as long as it belongs to a taxon node."; + result.add(message); + } + } + if (ref instanceof PolytomousKeyNode){ + message = "The Taxon can't be deleted as long as it is referenced by a polytomous key node."; + result.add(message); + } + + if (HibernateProxyHelper.isInstanceOf(ref, IIdentificationKey.class)){ + message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name"; + result.add(message); + + } + + + /* //PolytomousKeyNode + if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){ + String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node"; + return message; + }*/ + + //TaxonInteraction + if (ref.isInstanceOf(TaxonInteraction.class)){ + message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2"; + result.add(message); + } + + //TaxonInteraction + if (ref.isInstanceOf(DeterminationEvent.class)){ + message = "Taxon can't be deleted as it is used in a determination event"; + result.add(message); + } + + } + + } + + return result; + } + + @Override + public IncludedTaxaDTO listIncludedTaxa(UUID taxonUuid, IncludedTaxonConfiguration config) { + IncludedTaxaDTO result = new IncludedTaxaDTO(taxonUuid); + //preliminary implementation + + Set taxa = new HashSet(); + TaxonBase taxonBase = find(taxonUuid); + if (taxonBase == null){ + return new IncludedTaxaDTO(); + }else if (taxonBase.isInstanceOf(Taxon.class)){ + Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class); + taxa.add(taxon); + }else if (taxonBase.isInstanceOf(Synonym.class)){ + //TODO partial synonyms ?? + //TODO synonyms in general + Synonym syn = CdmBase.deproxy(taxonBase, Synonym.class); + taxa.addAll(syn.getAcceptedTaxa()); + }else{ + throw new IllegalArgumentException("Unhandled class " + taxonBase.getClass().getSimpleName()); + } + Set related = makeRelatedIncluded(taxa, result, config); + int i = 0; + while((! related.isEmpty()) && i++ < 100){ //to avoid + related = makeRelatedIncluded(related, result, config); + } + return result; + } + + /** + * Computes all children and conceptually congruent and included taxa and adds them to the existingTaxa + * data structure. + * @return the set of conceptually related taxa for further use + */ + /** + * @param uncheckedTaxa + * @param existingTaxa + * @param config + * @return + */ + private Set makeRelatedIncluded(Set uncheckedTaxa, IncludedTaxaDTO existingTaxa, IncludedTaxonConfiguration config) { + + //children + Set taxonNodes = new HashSet(); + for (Taxon taxon: uncheckedTaxa){ + taxonNodes.addAll(taxon.getTaxonNodes()); + } + + Set children = new HashSet(); + if (! config.onlyCongruent){ + for (TaxonNode node: taxonNodes){ + List childNodes = nodeService.loadChildNodesOfTaxonNode(node, null, true, false); + for (TaxonNode child : childNodes){ + children.add(child.getTaxon()); + } + } + children.remove(null); // just to be on the save side + } + + Iterator it = children.iterator(); + while(it.hasNext()){ + UUID uuid = it.next().getUuid(); + if (existingTaxa.contains(uuid)){ + it.remove(); + }else{ + existingTaxa.addIncludedTaxon(uuid, new ArrayList(), false); + } + } + + //concept relations + Set uncheckedAndChildren = new HashSet(uncheckedTaxa); + uncheckedAndChildren.addAll(children); + + Set relatedTaxa = makeConceptIncludedTaxa(uncheckedAndChildren, existingTaxa, config); + + + Set result = new HashSet(relatedTaxa); + return result; + } + + /** + * Computes all conceptually congruent or included taxa and adds them to the existingTaxa data structure. + * @return the set of these computed taxa + */ + private Set makeConceptIncludedTaxa(Set unchecked, IncludedTaxaDTO existingTaxa, IncludedTaxonConfiguration config) { + Set result = new HashSet(); + + for (Taxon taxon : unchecked){ + Set fromRelations = taxon.getRelationsFromThisTaxon(); + Set toRelations = taxon.getRelationsToThisTaxon(); + + for (TaxonRelationship fromRel : fromRelations){ + if (config.includeDoubtful == false && fromRel.isDoubtful()){ + continue; + } + if (fromRel.getType().equals(TaxonRelationshipType.CONGRUENT_TO()) || + !config.onlyCongruent && fromRel.getType().equals(TaxonRelationshipType.INCLUDES()) || + !config.onlyCongruent && fromRel.getType().equals(TaxonRelationshipType.CONGRUENT_OR_INCLUDES()) + ){ + result.add(fromRel.getToTaxon()); + } + } + + for (TaxonRelationship toRel : toRelations){ + if (config.includeDoubtful == false && toRel.isDoubtful()){ + continue; + } + if (toRel.getType().equals(TaxonRelationshipType.CONGRUENT_TO())){ + result.add(toRel.getFromTaxon()); + } + } + } + + Iterator it = result.iterator(); + while(it.hasNext()){ + UUID uuid = it.next().getUuid(); + if (existingTaxa.contains(uuid)){ + it.remove(); + }else{ + existingTaxa.addIncludedTaxon(uuid, new ArrayList(), false); + } + } + return result; + } + @Override + public List findTaxaByName(MatchingTaxonConfigurator config){ + List taxonList = dao.getTaxaByName(true, false, false, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, 0, config.getPropertyPath()); + return taxonList; + } }