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