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