Merge branch 'release/5.5.0'
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TaxonNodeServiceImpl.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.api.service;
11
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.Comparator;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Set;
21 import java.util.UUID;
22
23 import org.apache.log4j.Logger;
24 import org.springframework.beans.factory.annotation.Autowired;
25 import org.springframework.stereotype.Service;
26 import org.springframework.transaction.annotation.Transactional;
27 import org.springframework.transaction.interceptor.TransactionAspectSupport;
28
29 import eu.etaxonomy.cdm.api.service.UpdateResult.Status;
30 import eu.etaxonomy.cdm.api.service.config.NodeDeletionConfigurator.ChildHandling;
31 import eu.etaxonomy.cdm.api.service.config.SecundumForSubtreeConfigurator;
32 import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
33 import eu.etaxonomy.cdm.api.service.config.TaxonNodeDeletionConfigurator;
34 import eu.etaxonomy.cdm.api.service.dto.CdmEntityIdentifier;
35 import eu.etaxonomy.cdm.api.service.dto.TaxonDistributionDTO;
36 import eu.etaxonomy.cdm.api.service.pager.Pager;
37 import eu.etaxonomy.cdm.api.service.pager.PagerUtils;
38 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
39 import eu.etaxonomy.cdm.common.monitor.DefaultProgressMonitor;
40 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
41 import eu.etaxonomy.cdm.filter.TaxonNodeFilter;
42 import eu.etaxonomy.cdm.hibernate.HHH_9751_Util;
43 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
44 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
45 import eu.etaxonomy.cdm.model.common.CdmBase;
46 import eu.etaxonomy.cdm.model.common.DefinedTerm;
47 import eu.etaxonomy.cdm.model.common.TreeIndex;
48 import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
49 import eu.etaxonomy.cdm.model.description.TaxonDescription;
50 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
51 import eu.etaxonomy.cdm.model.name.HybridRelationship;
52 import eu.etaxonomy.cdm.model.name.TaxonName;
53 import eu.etaxonomy.cdm.model.reference.Reference;
54 import eu.etaxonomy.cdm.model.taxon.Classification;
55 import eu.etaxonomy.cdm.model.taxon.HomotypicGroupTaxonComparator;
56 import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
57 import eu.etaxonomy.cdm.model.taxon.Synonym;
58 import eu.etaxonomy.cdm.model.taxon.SynonymType;
59 import eu.etaxonomy.cdm.model.taxon.Taxon;
60 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
61 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
62 import eu.etaxonomy.cdm.model.taxon.TaxonNodeAgentRelation;
63 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
64 import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
65 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
66 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeFilterDao;
67 import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
68 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
69
70 /**
71 * @author n.hoffmann
72 * @since Apr 9, 2010
73 */
74 @Service
75 @Transactional(readOnly = true)
76 public class TaxonNodeServiceImpl
77 extends AnnotatableServiceBase<TaxonNode, ITaxonNodeDao>
78 implements ITaxonNodeService{
79 private static final Logger logger = Logger.getLogger(TaxonNodeServiceImpl.class);
80
81 @Autowired
82 private IBeanInitializer defaultBeanInitializer;
83
84 @Autowired
85 private ITaxonService taxonService;
86
87 @Autowired
88 private IDescriptiveDataSetService dataSetService;
89
90 @Autowired
91 private IAgentService agentService;
92
93 @Autowired
94 private INameService nameService;
95
96 @Autowired
97 private IReferenceService refService;
98
99 @Autowired
100 private ITaxonNodeFilterDao nodeFilterDao;
101
102 @Autowired
103 IProgressMonitorService progressMonitorService;
104
105
106 @Override
107 public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
108 List<String> propertyPaths, boolean recursive, boolean includeUnpublished,
109 NodeSortMode sortMode) {
110
111 getSession().refresh(taxonNode);
112 List<TaxonNode> childNodes;
113 if (recursive == true){
114 childNodes = dao.listChildrenOf(taxonNode, null, null, recursive, includeUnpublished, null);
115 }else if (includeUnpublished){
116 childNodes = new ArrayList<>(taxonNode.getChildNodes());
117 }else{
118 childNodes = new ArrayList<>();
119 for (TaxonNode node:taxonNode.getChildNodes()){
120 if (node.getTaxon().isPublish()){
121 childNodes.add(node);
122 }
123 }
124 }
125
126 HHH_9751_Util.removeAllNull(childNodes);
127
128 if (sortMode != null){
129 Comparator<TaxonNode> comparator = sortMode.newComparator();
130 Collections.sort(childNodes, comparator);
131 }
132 defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
133 return childNodes;
134 }
135
136 @Override
137 public List<TaxonNode> listChildrenOf(TaxonNode node, Integer pageSize, Integer pageIndex,
138 boolean recursive, boolean includeUnpublished, List<String> propertyPaths){
139 return dao.listChildrenOf(node, pageSize, pageIndex, recursive, includeUnpublished, propertyPaths);
140 }
141
142 /**
143 * {@inheritDoc}
144 */
145 @Override
146 public UuidAndTitleCache<TaxonNode> getParentUuidAndTitleCache(ITaxonTreeNode child) {
147 UUID uuid = child.getUuid();
148 int id = child.getId();
149 UuidAndTitleCache<TaxonNode> uuidAndTitleCache = new UuidAndTitleCache<>(uuid, id, null);
150 return getParentUuidAndTitleCache(uuidAndTitleCache);
151 }
152
153 /**
154 * {@inheritDoc}
155 */
156 @Override
157 public UuidAndTitleCache<TaxonNode> getParentUuidAndTitleCache(UuidAndTitleCache<TaxonNode> child) {
158 return dao.getParentUuidAndTitleCache(child);
159 }
160
161 /**
162 * {@inheritDoc}
163 */
164 @Override
165 public List<TaxonNodeDto> listChildNodesAsTaxonNodeDto(UuidAndTitleCache<TaxonNode> parent) {
166 return dao.listChildNodesAsTaxonNodeDto(parent);
167 }
168
169 /**
170 * {@inheritDoc}
171 */
172 @Override
173 public List<UuidAndTitleCache<TaxonNode>> listChildNodesAsUuidAndTitleCache(UuidAndTitleCache<TaxonNode> parent) {
174 return dao.listChildNodesAsUuidAndTitleCache(parent);
175 }
176
177
178 /**
179 * {@inheritDoc}
180 */
181 @Override
182 public List<UuidAndTitleCache<TaxonNode>> getUuidAndTitleCache(Integer limit, String pattern, UUID classificationUuid) {
183 return dao.getUuidAndTitleCache(limit, pattern, classificationUuid);
184 }
185
186 /**
187 * {@inheritDoc}
188 */
189 @Override
190 public List<TaxonNodeDto> listChildNodesAsTaxonNodeDto(ITaxonTreeNode parent) {
191 UUID uuid = parent.getUuid();
192 int id = parent.getId();
193 UuidAndTitleCache<TaxonNode> uuidAndTitleCache = new UuidAndTitleCache<>(uuid, id, null);
194 return listChildNodesAsTaxonNodeDto(uuidAndTitleCache);
195 }
196
197 @Override
198 public List<UuidAndTitleCache<TaxonNode>> listChildNodesAsUuidAndTitleCache(ITaxonTreeNode parent) {
199 UUID uuid = parent.getUuid();
200 int id = parent.getId();
201 UuidAndTitleCache<TaxonNode> uuidAndTitleCache = new UuidAndTitleCache<>(uuid, id, null);
202 return listChildNodesAsUuidAndTitleCache(uuidAndTitleCache);
203 }
204
205 /**
206 * {@inheritDoc}
207 */
208 @Override
209 public Pager<TaxonNodeDto> pageChildNodesDTOs(UUID taxonNodeUuid, boolean recursive, boolean includeUnpublished,
210 boolean doSynonyms, NodeSortMode sortMode,
211 Integer pageSize, Integer pageIndex) {
212
213 TaxonNode parentNode = dao.load(taxonNodeUuid);
214
215 List<CdmBase> allRecords = new ArrayList<>();
216
217 //acceptedTaxa
218 List<TaxonNode> childNodes = loadChildNodesOfTaxonNode(parentNode, null, recursive, includeUnpublished, sortMode);
219 allRecords.addAll(childNodes);
220
221 //add synonyms if pager is not yet full synonyms
222 if (doSynonyms){
223 List<Synonym> synList = new ArrayList<>(parentNode.getTaxon().getSynonyms());
224 Collections.sort(synList, new HomotypicGroupTaxonComparator(null));
225 //TODO: test sorting
226
227 allRecords.addAll(synList);
228 }
229
230 List<TaxonNodeDto> dtos = new ArrayList<>(pageSize==null?25:pageSize);
231 long totalCount = Long.valueOf(allRecords.size());
232
233 TaxonName parentName = null;
234
235 for(CdmBase record : PagerUtils.pageList(allRecords, pageIndex, pageSize)) {
236 if (record.isInstanceOf(TaxonNode.class)){
237 dtos.add(new TaxonNodeDto(CdmBase.deproxy(record, TaxonNode.class)));
238 }else if (record.isInstanceOf(Synonym.class)){
239 Synonym synonym = CdmBase.deproxy(record, Synonym.class);
240 parentName = parentName == null? parentNode.getTaxon().getName(): parentName;
241 boolean isHomotypic = synonym.getName().isHomotypic(parentName);
242 dtos.add(new TaxonNodeDto(synonym, isHomotypic));
243 }
244 }
245 return new DefaultPagerImpl<>(pageIndex, totalCount, pageSize , dtos);
246 }
247
248 @Override
249 public TaxonNodeDto parentDto(UUID taxonNodeUuid) {
250 TaxonNode taxonNode = dao.load(taxonNodeUuid);
251 if(taxonNode.getParent() != null) {
252 return new TaxonNodeDto(taxonNode.getParent());
253 }
254 return null;
255 }
256
257 @Override
258 public TaxonNodeDto dto(UUID taxonNodeUuid) {
259 TaxonNode taxonNode = dao.load(taxonNodeUuid);
260 if(taxonNode.getParent() != null) {
261 return new TaxonNodeDto(taxonNode);
262 }
263 return null;
264 }
265
266 @Override
267 @Autowired
268 protected void setDao(ITaxonNodeDao dao) {
269 this.dao = dao;
270 }
271
272 @Override
273 @Transactional(readOnly = false)
274 public DeleteResult makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode, TaxonNode newAcceptedTaxonNode,
275 SynonymType synonymType, Reference citation, String citationMicroReference) {
276
277 // TODO at the moment this method only moves synonym-, concept relations and descriptions to the new accepted taxon
278 // in a future version we also want to move cdm data like annotations, marker, so., but we will need a policy for that
279 if (oldTaxonNode == null || newAcceptedTaxonNode == null || oldTaxonNode.getTaxon().getName() == null){
280 throw new IllegalArgumentException("A mandatory parameter was null.");
281 }
282
283 if(oldTaxonNode.equals(newAcceptedTaxonNode)){
284 throw new IllegalArgumentException("Taxon can not be made synonym of its own.");
285 }
286
287 Classification classification = oldTaxonNode.getClassification();
288 Taxon oldTaxon = HibernateProxyHelper.deproxy(oldTaxonNode.getTaxon());
289 Taxon newAcceptedTaxon = (Taxon)this.taxonService.find(newAcceptedTaxonNode.getTaxon().getUuid());
290 newAcceptedTaxon = HibernateProxyHelper.deproxy(newAcceptedTaxon);
291 // Move oldTaxon to newTaxon
292 //TaxonName synonymName = oldTaxon.getName();
293 TaxonName newSynonymName = CdmBase.deproxy(oldTaxon.getName());
294 HomotypicalGroup group = CdmBase.deproxy(newSynonymName.getHomotypicalGroup());
295 if (synonymType == null){
296 if (newSynonymName.isHomotypic(newAcceptedTaxon.getName())){
297 synonymType = SynonymType.HOMOTYPIC_SYNONYM_OF();
298 }else{
299 synonymType = SynonymType.HETEROTYPIC_SYNONYM_OF();
300 }
301 }
302
303 //set homotypic group
304 TaxonName newAcceptedTaxonName = HibernateProxyHelper.deproxy(newAcceptedTaxon.getName(), TaxonName.class);
305 newAcceptedTaxon.setName(newAcceptedTaxonName);
306 // Move Synonym Relations to new Taxon
307 newAcceptedTaxon.addSynonymName(newSynonymName, citation, citationMicroReference, synonymType);
308 // Move Synonyms to new Taxon
309 // From ticket 3163 we can move taxon with accepted name having homotypic synonyms
310 List<Synonym> synonymsInHomotypicalGroup = null;
311
312 //the synonyms of the homotypical group of the old taxon
313 if (synonymType.equals(SynonymType.HOMOTYPIC_SYNONYM_OF())){
314 synonymsInHomotypicalGroup = oldTaxon.getSynonymsInGroup(group);
315 }
316
317 Set<Synonym> syns = new HashSet<>(oldTaxon.getSynonyms());
318 for(Synonym synonym : syns){
319 SynonymType srt;
320 if(synonym.getHomotypicGroup()!= null
321 && synonym.getHomotypicGroup().equals(newAcceptedTaxonName.getHomotypicalGroup())) {
322 srt = SynonymType.HOMOTYPIC_SYNONYM_OF();
323 } else if(synonym.getType() != null && synonym.getType().equals(SynonymType.HOMOTYPIC_SYNONYM_OF())) {
324 if (synonymType.equals(SynonymType.HOMOTYPIC_SYNONYM_OF())){
325 srt = SynonymType.HOMOTYPIC_SYNONYM_OF();
326 } else{
327 srt = SynonymType.HETEROTYPIC_SYNONYM_OF();
328 }
329 } else {
330 if (synonymsInHomotypicalGroup != null && synonymsInHomotypicalGroup.contains(synonym)){
331 srt = SynonymType.HOMOTYPIC_SYNONYM_OF();
332 }else{
333 srt = synonym.getType();
334 }
335
336 }
337
338 newAcceptedTaxon.addSynonym(synonym, srt);
339
340
341 /*if (synonymsInHomotypicalGroup.contains(synRelation.getSynonym()) && srt.equals(SynonymType.HETEROTYPIC_SYNONYM_OF())){
342 homotypicalGroupAcceptedTaxon.addTypifiedName(synRelation.getSynonym().getName());
343 }*/
344
345 }
346
347
348 // CHILD NODES
349 if(oldTaxonNode.getChildNodes() != null && oldTaxonNode.getChildNodes().size() != 0){
350 List<TaxonNode> childNodes = new ArrayList<>();
351 for (TaxonNode childNode : oldTaxonNode.getChildNodes()){
352 childNodes.add(childNode);
353 }
354 for(TaxonNode childNode :childNodes){
355 newAcceptedTaxonNode.addChildNode(childNode, childNode.getReference(), childNode.getMicroReference()); // childNode.getSynonymToBeUsed()
356 }
357 }
358
359 //Move Taxon RelationShips to new Taxon
360 for(TaxonRelationship taxonRelationship : oldTaxon.getTaxonRelations()){
361 Taxon fromTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getFromTaxon());
362 Taxon toTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getToTaxon());
363 if (fromTaxon == oldTaxon){
364 newAcceptedTaxon.addTaxonRelation(taxonRelationship.getToTaxon(), taxonRelationship.getType(),
365 taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
366
367 }else if(toTaxon == oldTaxon){
368 fromTaxon.addTaxonRelation(newAcceptedTaxon, taxonRelationship.getType(),
369 taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
370 taxonService.saveOrUpdate(fromTaxon);
371
372 }else{
373 logger.warn("Taxon is not part of its own Taxonrelationship");
374 }
375 // Remove old relationships
376
377 fromTaxon.removeTaxonRelation(taxonRelationship);
378 toTaxon.removeTaxonRelation(taxonRelationship);
379 taxonRelationship.setToTaxon(null);
380 taxonRelationship.setFromTaxon(null);
381 }
382
383
384 //Move descriptions to new taxon
385 List<TaxonDescription> descriptions = new ArrayList<TaxonDescription>( oldTaxon.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())
386 for(TaxonDescription description : descriptions){
387 String message = "Description copied from former accepted taxon: %s (Old title: %s)";
388 message = String.format(message, oldTaxon.getTitleCache(), description.getTitleCache());
389 description.setTitleCache(message, true);
390 //oldTaxon.removeDescription(description, false);
391 newAcceptedTaxon.addDescription(description);
392 }
393 oldTaxon.clearDescriptions();
394
395 taxonService.saveOrUpdate(newAcceptedTaxon);
396
397 taxonService.saveOrUpdate(oldTaxon);
398 taxonService.getSession().flush();
399
400 TaxonDeletionConfigurator conf = new TaxonDeletionConfigurator();
401 conf.setDeleteSynonymsIfPossible(false);
402 conf.setDeleteNameIfPossible(false);
403 DeleteResult result = taxonService.isDeletable(oldTaxon.getUuid(), conf);
404
405
406 if (result.isOk()){
407 result = taxonService.deleteTaxon(oldTaxon.getUuid(), conf, classification.getUuid());
408
409 }else{
410 result.setStatus(Status.OK);
411 TaxonNodeDeletionConfigurator config = new TaxonNodeDeletionConfigurator();
412 config.setDeleteElement(false);
413 conf.setTaxonNodeConfig(config);
414 result.includeResult(deleteTaxonNode(oldTaxonNode, conf));
415 }
416
417 result.addUpdatedObject(newAcceptedTaxon);
418
419
420 //oldTaxonNode.delete();
421 return result;
422 }
423 @Override
424 @Transactional(readOnly = false)
425 public UpdateResult makeTaxonNodeSynonymsOfAnotherTaxonNode( Set<UUID> oldTaxonNodeUuids,
426 UUID newAcceptedTaxonNodeUUIDs,
427 SynonymType synonymType,
428 Reference citation,
429 String citationMicroReference) {
430 UpdateResult result = new UpdateResult();
431 for (UUID nodeUuid: oldTaxonNodeUuids) {
432 result.includeResult(makeTaxonNodeASynonymOfAnotherTaxonNode(nodeUuid, newAcceptedTaxonNodeUUIDs, synonymType, citation, citationMicroReference));
433 }
434 return result;
435 }
436
437 @Override
438 @Transactional(readOnly = false)
439 public UpdateResult makeTaxonNodeASynonymOfAnotherTaxonNode(UUID oldTaxonNodeUuid,
440 UUID newAcceptedTaxonNodeUUID,
441 SynonymType synonymType,
442 Reference citation,
443 String citationMicroReference) {
444
445 TaxonNode oldTaxonNode = dao.load(oldTaxonNodeUuid);
446 TaxonNode oldTaxonParentNode = oldTaxonNode.getParent();
447 TaxonNode newTaxonNode = dao.load(newAcceptedTaxonNodeUUID);
448
449 UpdateResult result = makeTaxonNodeASynonymOfAnotherTaxonNode(oldTaxonNode,
450 newTaxonNode,
451 synonymType,
452 citation,
453 citationMicroReference);
454 result.addUpdatedCdmId(new CdmEntityIdentifier(oldTaxonParentNode.getId(), TaxonNode.class));
455 result.addUpdatedCdmId(new CdmEntityIdentifier(newTaxonNode.getId(), TaxonNode.class));
456 result.setCdmEntity(oldTaxonParentNode);
457 return result;
458 }
459
460 @Override
461 @Transactional(readOnly = false)
462 public DeleteResult deleteTaxonNodes(List<TaxonNode> list, TaxonDeletionConfigurator config) {
463
464 if (config == null){
465 config = new TaxonDeletionConfigurator();
466 }
467 DeleteResult result = new DeleteResult();
468 Classification classification = null;
469 List<TaxonNode> taxonNodes = new ArrayList<>(list);
470
471 for (TaxonNode treeNode:taxonNodes){
472 if (treeNode != null){
473
474 TaxonNode taxonNode;
475 taxonNode = HibernateProxyHelper.deproxy(treeNode, TaxonNode.class);
476 TaxonNode parent = taxonNode.getParent();
477 //check whether the node has children or the children are already deleted
478 if(taxonNode.hasChildNodes()) {
479 List<TaxonNode> children = new ArrayList<TaxonNode> ();
480 List<TaxonNode> childNodesList = taxonNode.getChildNodes();
481 children.addAll(childNodesList);
482 //To avoid NPE when child is also in list of taxonNodes, remove it from the list
483 Iterator<TaxonNode> it = taxonNodes.iterator();
484 for (TaxonNode child: children) {
485 while (it.hasNext()) {
486 if (it.next().equals(child)) {
487 it.remove();
488 }
489 }
490 }
491 int compare = config.getTaxonNodeConfig().getChildHandling().compareTo(ChildHandling.DELETE);
492 boolean childHandling = (compare == 0)? true: false;
493 if (childHandling){
494 boolean changeDeleteTaxon = false;
495 if (!config.getTaxonNodeConfig().isDeleteTaxon()){
496 config.getTaxonNodeConfig().setDeleteTaxon(true);
497 changeDeleteTaxon = true;
498 }
499 DeleteResult resultNodes = deleteTaxonNodes(children, config);
500 if (!resultNodes.isOk()){
501 result.addExceptions(resultNodes.getExceptions());
502 result.setStatus(resultNodes.getStatus());
503 }
504 if (changeDeleteTaxon){
505 config.getTaxonNodeConfig().setDeleteTaxon(false);
506 }
507
508 } else {
509 //move the children to the parent
510
511 for (TaxonNode child: childNodesList){
512 parent.addChildNode(child, child.getReference(), child.getMicroReference());
513 }
514
515 }
516 }
517
518 classification = taxonNode.getClassification();
519
520 if (classification.getRootNode().equals(taxonNode)){
521 classification.removeRootNode();
522 classification = null;
523 }else if (classification.getChildNodes().contains(taxonNode)){
524 Taxon taxon = taxonNode.getTaxon();
525 classification.deleteChildNode(taxonNode);
526
527 //node is rootNode
528 if (taxon != null){
529
530 if (config.getTaxonNodeConfig().isDeleteTaxon()){
531 taxonService.saveOrUpdate(taxon);
532 saveOrUpdate(taxonNode);
533
534 TaxonDeletionConfigurator configNew = new TaxonDeletionConfigurator();
535 DeleteResult resultTaxon = taxonService.deleteTaxon(taxon.getUuid(), configNew, classification.getUuid());
536 if (!resultTaxon.isOk()){
537 result.addExceptions(resultTaxon.getExceptions());
538 result.setStatus(resultTaxon.getStatus());
539 }
540
541 }
542 }
543 classification = null;
544
545 } else {
546 classification = null;
547 Taxon taxon = taxonNode.getTaxon();
548 taxon = HibernateProxyHelper.deproxy(taxon, Taxon.class);
549 if (taxon != null){
550 taxon.removeTaxonNode(taxonNode);
551 if (config.getTaxonNodeConfig().isDeleteTaxon()){
552 TaxonDeletionConfigurator configNew = new TaxonDeletionConfigurator();
553 saveOrUpdate(taxonNode);
554 taxonService.saveOrUpdate(taxon);
555 DeleteResult resultTaxon = taxonService.deleteTaxon(taxon.getUuid(), configNew, null);
556
557 if (!resultTaxon.isOk()){
558 result.addExceptions(resultTaxon.getExceptions());
559 result.setStatus(resultTaxon.getStatus());
560 }
561 }
562 }
563
564 }
565
566 result.addUpdatedObject(parent);
567 if(result.getCdmEntity() == null){
568 result.setCdmEntity(taxonNode);
569 }
570 UUID uuid = dao.delete(taxonNode);
571 logger.debug("Deleted node " +uuid.toString());
572
573 }
574 }
575 /*if (classification != null){
576 result.addUpdatedObject(classification);
577 DeleteResult resultClassification = classService.delete(classification);
578 if (!resultClassification.isOk()){
579 result.addExceptions(resultClassification.getExceptions());
580 result.setStatus(resultClassification.getStatus());
581 }
582 }*/
583 return result;
584 }
585
586
587 @Override
588 @Transactional(readOnly = false)
589 public DeleteResult deleteTaxonNodes(Collection<UUID> nodeUuids, TaxonDeletionConfigurator config) {
590 List<TaxonNode> nodes = new ArrayList<>();
591 for(UUID nodeUuid : nodeUuids) {
592 nodes.add(dao.load(nodeUuid));
593 }
594 return deleteTaxonNodes(nodes, config);
595 }
596
597
598 @Override
599 @Transactional(readOnly = false)
600 public DeleteResult deleteTaxonNode(UUID nodeUUID, TaxonDeletionConfigurator config) {
601
602 TaxonNode node = HibernateProxyHelper.deproxy(dao.load(nodeUUID), TaxonNode.class);
603 return deleteTaxonNode(node, config);
604 }
605
606 @Override
607 @Transactional(readOnly = false)
608 public DeleteResult deleteTaxonNode(TaxonNode node, TaxonDeletionConfigurator config) {
609 DeleteResult result = new DeleteResult();
610 if (node == null){
611 result.setAbort();
612 result.addException(new Exception("The TaxonNode was already deleted."));
613 return result;
614 }
615 Taxon taxon = null;
616 try{
617 taxon = HibernateProxyHelper.deproxy(node.getTaxon());
618 }catch(NullPointerException e){
619 result.setAbort();
620 result.addException(new Exception("The Taxon was already deleted."));
621
622 }
623
624
625 TaxonNode parent = HibernateProxyHelper.deproxy(node.getParent(), TaxonNode.class);
626 if (config == null){
627 config = new TaxonDeletionConfigurator();
628 }
629
630
631 if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.MOVE_TO_PARENT)){
632 Object[] children = node.getChildNodes().toArray();
633 TaxonNode childNode;
634 for (Object child: children){
635 childNode = (TaxonNode) child;
636 parent.addChildNode(childNode, childNode.getReference(), childNode.getMicroReference());
637
638 }
639 }else{
640 result.includeResult(deleteTaxonNodes(node.getChildNodes(), config));
641 }
642
643 //remove node from DescriptiveDataSet
644 commonService.getReferencingObjects(node).stream()
645 .filter(obj->obj instanceof DescriptiveDataSet)
646 .forEach(dataSet->{
647 ((DescriptiveDataSet)dataSet).removeTaxonSubtree(node);
648 dataSetService.saveOrUpdate((DescriptiveDataSet) dataSet);
649 });
650
651 if (taxon != null){
652 if (config.getTaxonNodeConfig().isDeleteTaxon() && (config.isDeleteInAllClassifications() || taxon.getTaxonNodes().size() == 1)){
653 result = taxonService.deleteTaxon(taxon.getUuid(), config, node.getClassification().getUuid());
654 result.addUpdatedObject(parent);
655 if (result.isOk()){
656 return result;
657 }
658 } else {
659 result.addUpdatedObject(taxon);
660 }
661 }
662 result.setCdmEntity(node);
663 boolean success = true;
664 if (taxon != null){
665 success = taxon.removeTaxonNode(node);
666 taxonService.saveOrUpdate(taxon);
667 }
668 dao.saveOrUpdate(parent);
669
670 result.addUpdatedObject(parent);
671
672 if (success){
673 result.setStatus(Status.OK);
674 if (parent != null){
675 parent = HibernateProxyHelper.deproxy(parent, TaxonNode.class);
676 int index = parent.getChildNodes().indexOf(node);
677 if (index > -1){
678 parent.removeChild(index);
679 }
680 }
681 if (!dao.delete(node, config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)).equals(null)){
682 result.getUpdatedObjects().remove(node);
683 result.addDeletedObject(node);
684 return result;
685 } else {
686 result.setError();
687 return result;
688 }
689 }else{
690 if (dao.findByUuid(node.getUuid()) != null){
691 result.setError();
692 result.addException(new Exception("The node can not be removed from the taxon."));
693 }
694 return result;
695 }
696 }
697
698
699 @Override
700 public List<TaxonNode> listAllNodesForClassification(Classification classification, Integer start, Integer end) {
701 return dao.getTaxonOfAcceptedTaxaByClassification(classification, start, end);
702 }
703
704 @Override
705 public int countAllNodesForClassification(Classification classification) {
706 return dao.countTaxonOfAcceptedTaxaByClassification(classification);
707 }
708
709 @Override
710 @Transactional
711 public UpdateResult moveTaxonNode(UUID taxonNodeUuid, UUID targetNodeUuid, int movingType){
712 TaxonNode taxonNode = HibernateProxyHelper.deproxy(dao.load(taxonNodeUuid), TaxonNode.class);
713 TaxonNode targetNode = HibernateProxyHelper.deproxy(dao.load(targetNodeUuid), TaxonNode.class);
714 UpdateResult result = moveTaxonNode(taxonNode, targetNode, movingType);
715 return result;
716 }
717
718 @Override
719 @Transactional
720 public UpdateResult moveTaxonNode(TaxonNode taxonNode, TaxonNode newParent, int movingType){
721 UpdateResult result = new UpdateResult();
722
723 TaxonNode parentParent = HibernateProxyHelper.deproxy(newParent.getParent());
724 TaxonNode oldParent = HibernateProxyHelper.deproxy(taxonNode.getParent());
725 Integer sortIndex = -1;
726 if (movingType == 0){
727 sortIndex = 0;
728 }else if (movingType == 1){
729 sortIndex = newParent.getSortIndex();
730 newParent = parentParent;
731 } else if (movingType == 2){
732 sortIndex = newParent.getSortIndex() +1;
733 newParent = parentParent;
734 } else{
735 result.setAbort();
736 result.addException(new Exception("The moving type "+ movingType +" is not supported."));
737 }
738
739
740 taxonNode = newParent.addChildNode(taxonNode, sortIndex, taxonNode.getReference(), taxonNode.getMicroReference());
741 // result.addUpdatedObject(newParent);
742 result.addUpdatedObject(taxonNode);
743 // result.setCdmEntity(taxonNode);
744
745
746
747
748 return result;
749 }
750
751
752
753 @Override
754 @Transactional
755 public UpdateResult moveTaxonNodes(Set<UUID> taxonNodeUuids, UUID newParentNodeUuid, int movingType, IProgressMonitor monitor){
756
757 if (monitor == null){
758 monitor = DefaultProgressMonitor.NewInstance();
759 }
760 UpdateResult result = new UpdateResult();
761
762 TaxonNode targetNode = dao.load(newParentNodeUuid);
763 List<TaxonNode> nodes = dao.list(taxonNodeUuids, null, null, null, null);
764 boolean hasPermission = true;
765
766 monitor.beginTask("Move Taxonnodes", nodes.size()*2);
767 monitor.subTask("move taxon nodes");
768 for (TaxonNode node: nodes){
769 if (!monitor.isCanceled()){
770 if (!nodes.contains(node.getParent())){
771 result.includeResult(moveTaxonNode(node,targetNode, movingType));
772 }
773 monitor.worked(1);
774 }else{
775 monitor.done();
776 result.setAbort();
777 break;
778 }
779 }
780 if (!monitor.isCanceled()){
781 monitor.subTask("saving and reindex");
782 dao.saveOrUpdateAll(nodes);
783 }else{
784 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
785 }
786
787 monitor.done();
788 return result;
789 }
790
791 @Override
792 public Pager<TaxonNodeAgentRelation> pageTaxonNodeAgentRelations(UUID taxonUuid, UUID classificationUuid,
793 UUID agentUuid, UUID rankUuid, UUID relTypeUuid, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
794
795
796 List<TaxonNodeAgentRelation> records = null;
797
798 long count = dao.countTaxonNodeAgentRelations(taxonUuid, classificationUuid, agentUuid, rankUuid, relTypeUuid);
799 if(PagerUtils.hasResultsInRange(count, pageIndex, pageSize)) {
800 records = dao.listTaxonNodeAgentRelations(taxonUuid, classificationUuid,
801 agentUuid, rankUuid, relTypeUuid, PagerUtils.startFor(pageSize, pageIndex), PagerUtils.limitFor(pageSize), propertyPaths);
802 }
803
804 Pager<TaxonNodeAgentRelation> pager = new DefaultPagerImpl<>(pageIndex, count, pageSize, records);
805 return pager;
806 }
807
808 @Override
809 @Transactional
810 public UpdateResult createNewTaxonNode(UUID parentNodeUuid, Taxon newTaxon, Reference ref, String microref){
811 UpdateResult result = new UpdateResult();
812 if (newTaxon.getName().getId() != 0){
813 TaxonName name = nameService.load(newTaxon.getName().getUuid());
814 newTaxon.setName(name);
815 }else{
816 for (HybridRelationship rel : newTaxon.getName().getHybridChildRelations()){
817 if (!rel.getHybridName().isPersited()) {
818 nameService.save(rel.getHybridName());
819 }
820 if (!rel.getParentName().isPersited()) {
821 nameService.save(rel.getParentName());
822 }
823 }
824 }
825 UUID taxonUUID = taxonService.saveOrUpdate(newTaxon);
826 newTaxon = (Taxon) taxonService.load(taxonUUID);
827
828 TaxonNode parent = dao.load(parentNodeUuid);
829 TaxonNode child = null;
830 try{
831 child = parent.addChildTaxon(newTaxon, parent.getReference(), parent.getMicroReference());
832 }catch(Exception e){
833 result.addException(e);
834 result.setError();
835 return result;
836 }
837 // child = dao.save(child);
838
839 dao.saveOrUpdate(parent);
840 result.addUpdatedObject(parent);
841 if (child != null){
842 result.setCdmEntity(child);
843 }
844 return result;
845
846 }
847
848
849 @Override
850 @Transactional
851 public UpdateResult saveNewTaxonNode(TaxonNode newTaxonNode){
852 UpdateResult result = new UpdateResult();
853 if (newTaxonNode.getTaxon().getName().getId() != 0){
854 TaxonName name = nameService.load(newTaxonNode.getTaxon().getName().getUuid());
855 newTaxonNode.getTaxon().setName(name);
856 }else{
857 for (HybridRelationship rel : newTaxonNode.getTaxon().getName().getHybridChildRelations()){
858 if (!rel.getHybridName().isPersited()) {
859 nameService.save(rel.getHybridName());
860 }
861 if (!rel.getParentName().isPersited()) {
862 nameService.save(rel.getParentName());
863 }
864 }
865 }
866 UUID taxonUUID = taxonService.saveOrUpdate(newTaxonNode.getTaxon());
867 UUID childUUID = dao.saveOrUpdate(newTaxonNode);
868
869 TaxonNode parent = dao.load(newTaxonNode.getParent().getUuid());
870 TaxonNode child = dao.load(childUUID);
871 result.addUpdatedObject(parent);
872 if (child != null){
873 result.setCdmEntity(child);
874 }
875 return result;
876
877 }
878
879
880
881 @Override
882 @Transactional
883 public UpdateResult createNewTaxonNode(UUID parentNodeUuid, UUID taxonUuid, Reference ref, String microref){
884 UpdateResult result = new UpdateResult();
885 TaxonNode parent = dao.load(parentNodeUuid);
886 Taxon taxon = (Taxon) taxonService.load(taxonUuid);
887 TaxonNode child = null;
888 try{
889 child = parent.addChildTaxon(taxon, parent.getReference(), parent.getMicroReference());
890 }catch(Exception e){
891 result.addException(e);
892 result.setError();
893 return result;
894 }
895 // child = dao.save(child);
896
897 // dao.saveOrUpdate(child);
898 result.addUpdatedObject(parent);
899 if (child != null){
900 result.setCdmEntity(child);
901 }
902 return result;
903
904 }
905
906 @Override
907 @Transactional
908 public UpdateResult addTaxonNodeAgentRelation(UUID taxonNodeUUID, UUID agentUUID, DefinedTerm relationshipType){
909 UpdateResult result = new UpdateResult();
910 TaxonNode node = dao.load(taxonNodeUUID);
911 TeamOrPersonBase<?> agent = (TeamOrPersonBase<?>) agentService.load(agentUUID);
912 node.addAgentRelation(relationshipType, agent);
913 try{
914 dao.merge(node, true);
915 }catch (Exception e){
916 result.setError();
917 result.addException(e);
918 }
919 result.setCdmEntity(node);
920 return result;
921 }
922
923 @Override
924 @Transactional(readOnly=false)
925 public UpdateResult setSecundumForSubtree(SecundumForSubtreeConfigurator config) {
926 UpdateResult result = new UpdateResult();
927 IProgressMonitor monitor = config.getMonitor();
928
929 if (monitor == null){
930 monitor = DefaultProgressMonitor.NewInstance();
931 }
932 TaxonNode subTree = load(config.getSubtreeUuid());
933 TreeIndex subTreeIndex = null;
934 Reference newSec = null;
935 if (config.getNewSecundum() != null){
936 newSec = refService.load(config.getNewSecundum().getUuid());
937 }
938
939 if (config.getSubtreeUuid() == null){
940 result.setError();
941 result.addException(new NullPointerException("No subtree given"));
942 monitor.done();
943 return result;
944 }
945
946 if (subTree == null){
947 result.setError();
948 result.addException(new NullPointerException("Subtree does not exist"));
949 monitor.done();
950 return result;
951 }else{
952 subTreeIndex = TreeIndex.NewInstance(subTree.treeIndex());
953 int count = config.isIncludeAcceptedTaxa() ? dao.countSecundumForSubtreeAcceptedTaxa(subTreeIndex, newSec, config.isOverwriteExistingAccepted(), config.isIncludeSharedTaxa(), config.isEmptySecundumDetail()):0;
954 count += config.isIncludeSynonyms() ? dao.countSecundumForSubtreeSynonyms(subTreeIndex, newSec, config.isOverwriteExistingSynonyms(), config.isIncludeSharedTaxa() , config.isEmptySecundumDetail()) :0;
955 monitor.beginTask("Update Secundum Reference", count);
956 }
957
958 //Reference ref = config.getNewSecundum();
959 if (config.isIncludeAcceptedTaxa()){
960 monitor.subTask("Update Accepted Taxa");
961
962 Set<TaxonBase> updatedTaxa = dao.setSecundumForSubtreeAcceptedTaxa(subTreeIndex, newSec, config.isOverwriteExistingAccepted(), config.isIncludeSharedTaxa(), config.isEmptySecundumDetail(), monitor);
963 result.addUpdatedObjects(updatedTaxa);
964 }
965 if (config.isIncludeSynonyms()){
966 monitor.subTask("Update Synonyms");
967 Set<TaxonBase> updatedSynonyms = dao.setSecundumForSubtreeSynonyms(subTreeIndex, newSec, config.isOverwriteExistingSynonyms(), config.isIncludeSharedTaxa() , config.isEmptySecundumDetail(), monitor);
968 result.addUpdatedObjects(updatedSynonyms);
969 }
970
971 monitor.done();
972 return result;
973 }
974
975
976 /**
977 * {@inheritDoc}
978 */
979 @Override
980 @Transactional(readOnly=false)
981 public UpdateResult setPublishForSubtree(UUID subtreeUuid, boolean publish, boolean includeAcceptedTaxa,
982 boolean includeSynonyms, boolean includeSharedTaxa, IProgressMonitor monitor) {
983 UpdateResult result = new UpdateResult();
984 if (monitor == null){
985 monitor = DefaultProgressMonitor.NewInstance();
986 }
987 TreeIndex subTreeIndex = null;
988
989 if (subtreeUuid == null){
990 result.setError();
991 result.addException(new NullPointerException("No subtree given"));
992 monitor.done();
993 return result;
994 }
995 TaxonNode subTree = find(subtreeUuid);
996 if (subTree == null){
997 result.setError();
998 result.addException(new NullPointerException("Subtree does not exist"));
999 monitor.done();
1000 return result;
1001 }else{
1002 subTreeIndex = TreeIndex.NewInstance(subTree.treeIndex());
1003 int count = includeAcceptedTaxa ? dao.countPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa):0;
1004 count += includeSynonyms ? dao.countPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa):0;
1005 monitor.beginTask("Update publish flag", count);
1006 }
1007
1008
1009 if (includeAcceptedTaxa){
1010 monitor.subTask("Update Accepted Taxa");
1011 Set<TaxonBase> updatedTaxa = dao.setPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa, monitor);
1012 result.addUpdatedObjects(updatedTaxa);
1013 }
1014 if (includeSynonyms){
1015 monitor.subTask("Update Synonyms");
1016 Set<TaxonBase> updatedSynonyms = dao.setPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa, monitor);
1017 result.addUpdatedObjects(updatedSynonyms);
1018 }
1019
1020 monitor.done();
1021 return result;
1022 }
1023
1024
1025 @Override
1026 public long count(TaxonNodeFilter filter){
1027 return nodeFilterDao.count(filter);
1028 }
1029
1030 @Override
1031 public List<UUID> uuidList(TaxonNodeFilter filter){
1032 return nodeFilterDao.listUuids(filter);
1033 }
1034
1035 @Override
1036 public List<Integer> idList(TaxonNodeFilter filter){
1037 return nodeFilterDao.idList(filter);
1038 }
1039
1040 @Override
1041 public TaxonNodeDto findCommonParentDto(Collection<TaxonNodeDto> nodes) {
1042 TaxonNodeDto commonParent = null;
1043 List<String> treePath = null;
1044 for (TaxonNodeDto nodeDto : nodes) {
1045 String nodeTreeIndex = nodeDto.getTreeIndex();
1046 nodeTreeIndex = nodeTreeIndex.replaceFirst("#", "");
1047 String[] split = nodeTreeIndex.split("#");
1048 if(treePath == null){
1049 treePath = Arrays.asList(split);
1050 }
1051 else{
1052 List<String> match = new ArrayList<>();
1053 for(int i=0;i<treePath.size();i++){
1054 if(i>=split.length){
1055 //current tree index is shorter so break
1056 break;
1057 }
1058 else if(split[i].equals(treePath.get(i))){
1059 //match found
1060 match.add(treePath.get(i));
1061 }
1062 else{
1063 //first mismatch found
1064 break;
1065 }
1066 }
1067 treePath = match;
1068 if(treePath.isEmpty()){
1069 //no common parent found for at least two nodes
1070 //-> they belong to a different classification
1071 break;
1072 }
1073 }
1074 }
1075 if(treePath!=null && !treePath.isEmpty()) {
1076 //get last index
1077 int nodeId = Integer.parseInt(treePath.get(treePath.size()-1));
1078 TaxonNode taxonNode = dao.load(nodeId, null);
1079 commonParent = new TaxonNodeDto(taxonNode);
1080 }
1081 return commonParent;
1082 }
1083
1084 @Override
1085 public List<TaxonDistributionDTO> getTaxonDistributionDTOForSubtree(UUID parentNodeUuid, List<String> propertyPaths){
1086 List<TaxonNode> nodes = listChildrenOf(load(parentNodeUuid), null, null,
1087 true, true, propertyPaths);
1088 List<TaxonDistributionDTO> result = new ArrayList<>();
1089 for(TaxonNode node:nodes){
1090 if (node.getTaxon() != null){
1091 TaxonDistributionDTO dto = new TaxonDistributionDTO(node.getTaxon());
1092 result.add(dto);
1093 }
1094
1095 }
1096
1097 return result;
1098 }
1099
1100
1101
1102 }