package eu.etaxonomy.cdm.api.service;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import eu.etaxonomy.cdm.api.service.UpdateResult.Status;
import eu.etaxonomy.cdm.api.service.config.NodeDeletionConfigurator.ChildHandling;
+import eu.etaxonomy.cdm.api.service.config.PublishForSubtreeConfigurator;
import eu.etaxonomy.cdm.api.service.config.SecundumForSubtreeConfigurator;
import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
import eu.etaxonomy.cdm.api.service.config.TaxonNodeDeletionConfigurator;
import eu.etaxonomy.cdm.api.service.dto.CdmEntityIdentifier;
+import eu.etaxonomy.cdm.api.service.dto.TaxonDistributionDTO;
import eu.etaxonomy.cdm.api.service.pager.Pager;
import eu.etaxonomy.cdm.api.service.pager.PagerUtils;
+import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
import eu.etaxonomy.cdm.common.monitor.DefaultProgressMonitor;
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
import eu.etaxonomy.cdm.model.common.CdmBase;
-import eu.etaxonomy.cdm.model.common.DefinedTerm;
+import eu.etaxonomy.cdm.model.common.Language;
+import eu.etaxonomy.cdm.model.common.LanguageString;
import eu.etaxonomy.cdm.model.common.TreeIndex;
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
+import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
+import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
import eu.etaxonomy.cdm.model.description.TaxonDescription;
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
import eu.etaxonomy.cdm.model.name.HybridRelationship;
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
import eu.etaxonomy.cdm.model.taxon.TaxonNodeAgentRelation;
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
+import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
+import eu.etaxonomy.cdm.model.term.DefinedTerm;
+import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeFilterDao;
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
+import eu.etaxonomy.cdm.persistence.query.OrderHint;
/**
* @author n.hoffmann
@Autowired
private ITaxonService taxonService;
+ @Autowired
+ private IReferenceService referenceService;
+
+ @Autowired
+ private IDescriptiveDataSetService dataSetService;
+
@Autowired
private IAgentService agentService;
return childNodes;
}
+ @Override
+ public List<TaxonNode> listChildrenOf(TaxonNode node, Integer pageSize, Integer pageIndex,
+ boolean recursive, boolean includeUnpublished, List<String> propertyPaths){
+ return dao.listChildrenOf(node, pageSize, pageIndex, recursive, includeUnpublished, propertyPaths);
+ }
+
/**
* {@inheritDoc}
*/
return null;
}
+ @Override
+ public TaxonNodeDto dto(UUID taxonNodeUuid) {
+ TaxonNode taxonNode = dao.load(taxonNodeUuid);
+ if(taxonNode.getParent() != null) {
+ return new TaxonNodeDto(taxonNode);
+ }
+ return null;
+ }
+
@Override
@Autowired
protected void setDao(ITaxonNodeDao dao) {
@Override
@Transactional(readOnly = false)
public DeleteResult makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode, TaxonNode newAcceptedTaxonNode,
- SynonymType synonymType, Reference citation, String citationMicroReference) {
+ SynonymType synonymType, Reference citation, String citationMicroReference, boolean setNameInSource) {
// TODO at the moment this method only moves synonym-, concept relations and descriptions to the new accepted taxon
// in a future version we also want to move cdm data like annotations, marker, so., but we will need a policy for that
taxonRelationship.setFromTaxon(null);
}
-
//Move descriptions to new taxon
List<TaxonDescription> descriptions = new ArrayList<TaxonDescription>( oldTaxon.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())
for(TaxonDescription description : descriptions){
String message = "Description copied from former accepted taxon: %s (Old title: %s)";
message = String.format(message, oldTaxon.getTitleCache(), description.getTitleCache());
description.setTitleCache(message, true);
+ if (setNameInSource) {
+ for (DescriptionElementBase element: description.getElements()){
+ for (DescriptionElementSource source: element.getSources()){
+ if (source.getNameUsedInSource() == null){
+ source.setNameUsedInSource(newSynonymName);
+ }
+ }
+ }
+ }
//oldTaxon.removeDescription(description, false);
newAcceptedTaxon.addDescription(description);
}
UUID newAcceptedTaxonNodeUUIDs,
SynonymType synonymType,
Reference citation,
- String citationMicroReference) {
+ String citationMicroReference,
+ boolean setNameInSource) {
UpdateResult result = new UpdateResult();
for (UUID nodeUuid: oldTaxonNodeUuids) {
- result.includeResult(makeTaxonNodeASynonymOfAnotherTaxonNode(nodeUuid, newAcceptedTaxonNodeUUIDs, synonymType, citation, citationMicroReference));
+ result.includeResult(makeTaxonNodeASynonymOfAnotherTaxonNode(nodeUuid, newAcceptedTaxonNodeUUIDs, synonymType, citation, citationMicroReference, setNameInSource));
}
return result;
}
UUID newAcceptedTaxonNodeUUID,
SynonymType synonymType,
Reference citation,
- String citationMicroReference) {
+ String citationMicroReference,
+ boolean setNameInSource) {
TaxonNode oldTaxonNode = dao.load(oldTaxonNodeUuid);
TaxonNode oldTaxonParentNode = oldTaxonNode.getParent();
newTaxonNode,
synonymType,
citation,
- citationMicroReference);
+ citationMicroReference, setNameInSource);
result.addUpdatedCdmId(new CdmEntityIdentifier(oldTaxonParentNode.getId(), TaxonNode.class));
result.addUpdatedCdmId(new CdmEntityIdentifier(newTaxonNode.getId(), TaxonNode.class));
result.setCdmEntity(oldTaxonParentNode);
result.includeResult(deleteTaxonNodes(node.getChildNodes(), config));
}
+ //remove node from DescriptiveDataSet
+ commonService.getReferencingObjects(node).stream()
+ .filter(obj->obj instanceof DescriptiveDataSet)
+ .forEach(dataSet->{
+ ((DescriptiveDataSet)dataSet).removeTaxonSubtree(node);
+ dataSetService.saveOrUpdate((DescriptiveDataSet) dataSet);
+ });
+
if (taxon != null){
if (config.getTaxonNodeConfig().isDeleteTaxon() && (config.isDeleteInAllClassifications() || taxon.getTaxonNodes().size() == 1)){
result = taxonService.deleteTaxon(taxon.getUuid(), config, node.getClassification().getUuid());
result.addException(new Exception("The moving type "+ movingType +" is not supported."));
}
-
taxonNode = newParent.addChildNode(taxonNode, sortIndex, taxonNode.getReference(), taxonNode.getMicroReference());
-// result.addUpdatedObject(newParent);
result.addUpdatedObject(taxonNode);
-// result.setCdmEntity(taxonNode);
-
-
-
return result;
}
TaxonNode parent = dao.load(parentNodeUuid);
TaxonNode child = null;
try{
- child = parent.addChildTaxon(newTaxon, parent.getReference(), parent.getMicroReference());
+ child = parent.addChildTaxon(newTaxon,ref, microref);
}catch(Exception e){
result.addException(e);
result.setError();
return result;
}
-// child = dao.save(child);
+ child = dao.save(child);
- dao.saveOrUpdate(parent);
result.addUpdatedObject(parent);
if (child != null){
result.setCdmEntity(child);
return result;
}
+
+
+ @Override
+ @Transactional
+ public UpdateResult saveNewTaxonNode(TaxonNode newTaxonNode){
+ UpdateResult result = new UpdateResult();
+ UUID parentUuid = newTaxonNode.getParent().getUuid();
+ Taxon taxon = null;
+
+ if (newTaxonNode.getTaxon().getId() != 0){
+ taxon = (Taxon)taxonService.load(newTaxonNode.getTaxon().getUuid());
+ //newTaxonNode.setTaxon(taxon);
+ }else if (newTaxonNode.getTaxon().getName().getId() != 0){
+ TaxonName name = nameService.load(newTaxonNode.getTaxon().getName().getUuid());
+ taxon = newTaxonNode.getTaxon();
+ taxon.setName(name);
+ }else{
+ for (HybridRelationship rel : newTaxonNode.getTaxon().getName().getHybridChildRelations()){
+ if (!rel.getHybridName().isPersited()) {
+ nameService.save(rel.getHybridName());
+ }
+ if (!rel.getParentName().isPersited()) {
+ nameService.save(rel.getParentName());
+ }
+ }
+ }
+ if (taxon == null){
+ taxon = newTaxonNode.getTaxon();
+ }
+ taxon.removeTaxonNode(newTaxonNode);
+
+ if (taxon.getSec() != null && taxon.getSec().isPersited()){
+ Reference sec = referenceService.load(taxon.getSec().getUuid());
+ taxon.setSec(sec);
+ }
+ if (taxon.getId() == 0){
+ UUID taxonUUID = taxonService.saveOrUpdate(taxon);
+ taxon = (Taxon) taxonService.load(taxonUUID);
+
+ }
+
+
+ TaxonNode parent = dao.load(parentUuid);
+ TaxonNode child = null;
+ try{
+ child = parent.addChildTaxon(taxon, newTaxonNode.getReference(), newTaxonNode.getMicroReference());
+
+ }catch(Exception e){
+ result.addException(e);
+ result.setError();
+ return result;
+ }
+
+ child.setUnplaced(newTaxonNode.isUnplaced());
+ child.setExcluded(newTaxonNode.isExcluded());
+ child.setDoubtful(newTaxonNode.isDoubtful());
+ for (TaxonNodeAgentRelation agentRel :newTaxonNode.getAgentRelations()){
+ child.addAgentRelation(agentRel.getType(), agentRel.getAgent());
+ }
+ for (Entry<Language, LanguageString> entry: newTaxonNode.getExcludedNote().entrySet()){
+ child.putExcludedNote(entry.getKey(), entry.getValue().getText());
+ }
+
+ newTaxonNode = null;
+ dao.saveOrUpdate(child);
+
+ result.addUpdatedObject(child.getParent());
+ if (child != null){
+ result.setCdmEntity(child);
+ }
+ return result;
+
+
+ }
+
+
+
@Override
@Transactional
public UpdateResult createNewTaxonNode(UUID parentNodeUuid, UUID taxonUuid, Reference ref, String microref){
}
- /**
- * {@inheritDoc}
- */
@Override
@Transactional(readOnly=false)
- public UpdateResult setPublishForSubtree(UUID subtreeUuid, boolean publish, boolean includeAcceptedTaxa,
- boolean includeSynonyms, boolean includeSharedTaxa, IProgressMonitor monitor) {
+ public UpdateResult setPublishForSubtree(PublishForSubtreeConfigurator config){
UpdateResult result = new UpdateResult();
+ IProgressMonitor monitor = config.getMonitor();
if (monitor == null){
monitor = DefaultProgressMonitor.NewInstance();
}
TreeIndex subTreeIndex = null;
- if (subtreeUuid == null){
+ if (config.getSubtreeUuid() == null){
result.setError();
result.addException(new NullPointerException("No subtree given"));
monitor.done();
return result;
}
- TaxonNode subTree = find(subtreeUuid);
+ TaxonNode subTree = find(config.getSubtreeUuid());
+ boolean includeAcceptedTaxa = config.isIncludeAcceptedTaxa();
+ boolean publish = config.isPublish();
+ boolean includeSynonyms = config.isIncludeSynonyms();
+ boolean includeSharedTaxa = config.isIncludeSharedTaxa();
+ boolean includeHybrids = config.isIncludeHybrids();
+ boolean includeRelatedTaxa = config.isIncludeProParteSynonyms() || config.isIncludeMisapplications();
if (subTree == null){
result.setError();
result.addException(new NullPointerException("Subtree does not exist"));
return result;
}else{
subTreeIndex = TreeIndex.NewInstance(subTree.treeIndex());
- int count = includeAcceptedTaxa ? dao.countPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa):0;
- count += includeSynonyms ? dao.countPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa):0;
+ int count = includeAcceptedTaxa ? dao.countPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa, includeHybrids):0;
+ count += includeSynonyms ? dao.countPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa, includeHybrids):0;
+ count += includeRelatedTaxa ? dao.countPublishForSubtreeRelatedTaxa(subTreeIndex, publish, includeSharedTaxa, includeHybrids):0;
monitor.beginTask("Update publish flag", count);
}
if (includeAcceptedTaxa){
monitor.subTask("Update Accepted Taxa");
- Set<TaxonBase> updatedTaxa = dao.setPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa, monitor);
+ Set<TaxonBase> updatedTaxa = dao.setPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa, includeHybrids, monitor);
result.addUpdatedObjects(updatedTaxa);
}
if (includeSynonyms){
monitor.subTask("Update Synonyms");
- Set<TaxonBase> updatedSynonyms = dao.setPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa, monitor);
+ Set<TaxonBase> updatedSynonyms = dao.setPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa, includeHybrids, monitor);
result.addUpdatedObjects(updatedSynonyms);
}
+ if (includeRelatedTaxa){
+ monitor.subTask("Update Related Taxa");
+ Set<UUID> relationTypes = new HashSet<>();
+ if (config.isIncludeMisapplications()){
+ relationTypes.addAll(TaxonRelationshipType.misappliedNameUuids());
+ }
+ if (config.isIncludeProParteSynonyms()){
+ relationTypes.addAll(TaxonRelationshipType.proParteOrPartialSynonymUuids());
+ }
+ Set<TaxonBase> updatedTaxa = dao.setPublishForSubtreeRelatedTaxa(subTreeIndex, publish,
+ relationTypes, includeSharedTaxa, includeHybrids, monitor);
+ result.addUpdatedObjects(updatedTaxa);
+ }
monitor.done();
return result;
}
-
@Override
public long count(TaxonNodeFilter filter){
return nodeFilterDao.count(filter);
return nodeFilterDao.idList(filter);
}
-// @Override
-// @Transactional(readOnly=false)
-// public UUID monitSetSecundum(final SecundumForSubtreeConfigurator configurator) {
-// RemotingProgressMonitorThread monitorThread = new RemotingProgressMonitorThread() {
-// @Override
-// public Serializable doRun(IRemotingProgressMonitor monitor) {
-// configurator.setMonitor(monitor);
-// UpdateResult result = setSecundumForSubtree(configurator);
-// return result;
-// }
-// };
-// UUID uuid = progressMonitorService.registerNewRemotingMonitor(monitorThread);
-// monitorThread.setPriority(3);
-// monitorThread.start();
-// return uuid;
-// }
+ @Override
+ public TaxonNodeDto findCommonParentDto(Collection<TaxonNodeDto> nodes) {
+ TaxonNodeDto commonParent = null;
+ List<String> treePath = null;
+ for (TaxonNodeDto nodeDto : nodes) {
+ String nodeTreeIndex = nodeDto.getTreeIndex();
+ nodeTreeIndex = nodeTreeIndex.replaceFirst("#", "");
+ String[] split = nodeTreeIndex.split("#");
+ if(treePath == null){
+ treePath = Arrays.asList(split);
+ }
+ else{
+ List<String> match = new ArrayList<>();
+ for(int i=0;i<treePath.size();i++){
+ if(i>=split.length){
+ //current tree index is shorter so break
+ break;
+ }
+ else if(split[i].equals(treePath.get(i))){
+ //match found
+ match.add(treePath.get(i));
+ }
+ else{
+ //first mismatch found
+ break;
+ }
+ }
+ treePath = match;
+ if(treePath.isEmpty()){
+ //no common parent found for at least two nodes
+ //-> they belong to a different classification
+ break;
+ }
+ }
+ }
+ if(treePath!=null && !treePath.isEmpty()) {
+ //get last index
+ int nodeId = Integer.parseInt(treePath.get(treePath.size()-1));
+ TaxonNode taxonNode = dao.load(nodeId, null);
+ commonParent = new TaxonNodeDto(taxonNode);
+ }
+ return commonParent;
+ }
+
+ @Override
+ public List<TaxonDistributionDTO> getTaxonDistributionDTOForSubtree(UUID parentNodeUuid, List<String> propertyPaths){
+ List<TaxonNode> nodes = listChildrenOf(load(parentNodeUuid), null, null,
+ true, true, propertyPaths);
+ List<TaxonDistributionDTO> result = new ArrayList<>();
+ for(TaxonNode node:nodes){
+ if (node.getTaxon() != null){
+ try{
+ TaxonDistributionDTO dto = new TaxonDistributionDTO(node.getTaxon());
+ result.add(dto);
+ }catch(Exception e){
+ System.err.println(node.getTaxon().getTitleCache());
+ }
+
+ }
+
+ }
+
+ return result;
+ }
+
+ @Override
+ public <S extends TaxonNode> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
+ Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
+ return page(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, INCLUDE_UNPUBLISHED);
+ }
+
+ @Override
+ public <S extends TaxonNode> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
+ Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths, boolean includeUnpublished) {
+
+ List<S> records;
+ long resultSize = dao.count(clazz, restrictions);
+ if(AbstractPagerImpl.hasResultsInRange(resultSize, pageIndex, pageSize)){
+ records = dao.list(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, includeUnpublished);
+ } else {
+ records = new ArrayList<>();
+ }
+ Pager<S> pager = new DefaultPagerImpl<>(pageIndex, resultSize, pageSize, records);
+ return pager;
+ }
+
}