Project

General

Profile

Download (40.7 KB) Statistics
| Branch: | Tag: | Revision:
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
    @Override
848
    @Transactional
849
    public UpdateResult createNewTaxonNode(UUID parentNodeUuid, UUID taxonUuid, Reference ref, String microref){
850
        UpdateResult result = new UpdateResult();
851
        TaxonNode parent = dao.load(parentNodeUuid);
852
        Taxon taxon = (Taxon) taxonService.load(taxonUuid);
853
        TaxonNode child = null;
854
        try{
855
            child = parent.addChildTaxon(taxon, parent.getReference(), parent.getMicroReference());
856
        }catch(Exception e){
857
            result.addException(e);
858
            result.setError();
859
            return result;
860
        }
861
//        child = dao.save(child);
862

    
863
//        dao.saveOrUpdate(child);
864
        result.addUpdatedObject(parent);
865
        if (child != null){
866
            result.setCdmEntity(child);
867
        }
868
        return result;
869

    
870
    }
871

    
872
    @Override
873
    @Transactional
874
    public UpdateResult addTaxonNodeAgentRelation(UUID taxonNodeUUID, UUID agentUUID, DefinedTerm relationshipType){
875
        UpdateResult result = new UpdateResult();
876
        TaxonNode node = dao.load(taxonNodeUUID);
877
        TeamOrPersonBase<?> agent = (TeamOrPersonBase<?>) agentService.load(agentUUID);
878
        node.addAgentRelation(relationshipType, agent);
879
        try{
880
            dao.merge(node, true);
881
        }catch (Exception e){
882
            result.setError();
883
            result.addException(e);
884
        }
885
        result.setCdmEntity(node);
886
        return result;
887
    }
888

    
889
    @Override
890
    @Transactional(readOnly=false)
891
    public UpdateResult setSecundumForSubtree(SecundumForSubtreeConfigurator config) {
892
        UpdateResult result = new UpdateResult();
893
        IProgressMonitor monitor = config.getMonitor();
894

    
895
        if (monitor == null){
896
            monitor = DefaultProgressMonitor.NewInstance();
897
        }
898
        TaxonNode subTree = load(config.getSubtreeUuid());
899
        TreeIndex subTreeIndex = null;
900
        Reference newSec = null;
901
        if (config.getNewSecundum() != null){
902
            newSec = refService.load(config.getNewSecundum().getUuid());
903
        }
904

    
905
        if (config.getSubtreeUuid() == null){
906
            result.setError();
907
            result.addException(new NullPointerException("No subtree given"));
908
            monitor.done();
909
            return result;
910
        }
911

    
912
        if (subTree == null){
913
            result.setError();
914
            result.addException(new NullPointerException("Subtree does not exist"));
915
            monitor.done();
916
            return result;
917
        }else{
918
            subTreeIndex = TreeIndex.NewInstance(subTree.treeIndex());
919
            int count = config.isIncludeAcceptedTaxa() ? dao.countSecundumForSubtreeAcceptedTaxa(subTreeIndex, newSec, config.isOverwriteExistingAccepted(), config.isIncludeSharedTaxa(), config.isEmptySecundumDetail()):0;
920
            count += config.isIncludeSynonyms() ? dao.countSecundumForSubtreeSynonyms(subTreeIndex, newSec, config.isOverwriteExistingSynonyms(), config.isIncludeSharedTaxa() , config.isEmptySecundumDetail()) :0;
921
            monitor.beginTask("Update Secundum Reference", count);
922
        }
923

    
924
        //Reference ref = config.getNewSecundum();
925
        if (config.isIncludeAcceptedTaxa()){
926
            monitor.subTask("Update Accepted Taxa");
927

    
928
            Set<TaxonBase> updatedTaxa = dao.setSecundumForSubtreeAcceptedTaxa(subTreeIndex, newSec, config.isOverwriteExistingAccepted(), config.isIncludeSharedTaxa(), config.isEmptySecundumDetail(), monitor);
929
            result.addUpdatedObjects(updatedTaxa);
930
        }
931
        if (config.isIncludeSynonyms()){
932
           monitor.subTask("Update Synonyms");
933
           Set<TaxonBase> updatedSynonyms = dao.setSecundumForSubtreeSynonyms(subTreeIndex, newSec, config.isOverwriteExistingSynonyms(), config.isIncludeSharedTaxa() , config.isEmptySecundumDetail(), monitor);
934
           result.addUpdatedObjects(updatedSynonyms);
935
        }
936

    
937
        monitor.done();
938
        return result;
939
    }
940

    
941

    
942
    /**
943
     * {@inheritDoc}
944
     */
945
    @Override
946
    @Transactional(readOnly=false)
947
    public UpdateResult setPublishForSubtree(UUID subtreeUuid, boolean publish, boolean includeAcceptedTaxa,
948
            boolean includeSynonyms, boolean includeSharedTaxa, IProgressMonitor monitor) {
949
        UpdateResult result = new UpdateResult();
950
        if (monitor == null){
951
            monitor = DefaultProgressMonitor.NewInstance();
952
        }
953
        TreeIndex subTreeIndex = null;
954

    
955
        if (subtreeUuid == null){
956
            result.setError();
957
            result.addException(new NullPointerException("No subtree given"));
958
            monitor.done();
959
            return result;
960
        }
961
        TaxonNode subTree = find(subtreeUuid);
962
        if (subTree == null){
963
            result.setError();
964
            result.addException(new NullPointerException("Subtree does not exist"));
965
            monitor.done();
966
            return result;
967
        }else{
968
            subTreeIndex = TreeIndex.NewInstance(subTree.treeIndex());
969
            int count = includeAcceptedTaxa ? dao.countPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa):0;
970
            count += includeSynonyms ? dao.countPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa):0;
971
            monitor.beginTask("Update publish flag", count);
972
        }
973

    
974

    
975
        if (includeAcceptedTaxa){
976
            monitor.subTask("Update Accepted Taxa");
977
            Set<TaxonBase> updatedTaxa = dao.setPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa, monitor);
978
            result.addUpdatedObjects(updatedTaxa);
979
        }
980
        if (includeSynonyms){
981
            monitor.subTask("Update Synonyms");
982
            Set<TaxonBase> updatedSynonyms = dao.setPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa, monitor);
983
            result.addUpdatedObjects(updatedSynonyms);
984
        }
985

    
986
        monitor.done();
987
        return result;
988
    }
989

    
990

    
991
    @Override
992
    public long count(TaxonNodeFilter filter){
993
        return nodeFilterDao.count(filter);
994
    }
995

    
996
    @Override
997
    public List<UUID> uuidList(TaxonNodeFilter filter){
998
        return nodeFilterDao.listUuids(filter);
999
    }
1000

    
1001
    @Override
1002
    public List<Integer> idList(TaxonNodeFilter filter){
1003
        return nodeFilterDao.idList(filter);
1004
    }
1005

    
1006
    @Override
1007
    public TaxonNodeDto findCommonParentDto(Collection<TaxonNodeDto> nodes) {
1008
        TaxonNodeDto commonParent = null;
1009
        List<String> treePath = null;
1010
        for (TaxonNodeDto nodeDto : nodes) {
1011
            String nodeTreeIndex = nodeDto.getTreeIndex();
1012
            nodeTreeIndex = nodeTreeIndex.replaceFirst("#", "");
1013
            String[] split = nodeTreeIndex.split("#");
1014
            if(treePath == null){
1015
                treePath = Arrays.asList(split);
1016
            }
1017
            else{
1018
                List<String> match = new ArrayList<>();
1019
                for(int i=0;i<treePath.size();i++){
1020
                    if(i>=split.length){
1021
                        //current tree index is shorter so break
1022
                        break;
1023
                    }
1024
                    else if(split[i].equals(treePath.get(i))){
1025
                        //match found
1026
                        match.add(treePath.get(i));
1027
                    }
1028
                    else{
1029
                        //first mismatch found
1030
                        break;
1031
                    }
1032
                }
1033
                treePath = match;
1034
                if(treePath.isEmpty()){
1035
                    //no common parent found for at least two nodes
1036
                    //-> they belong to a different classification
1037
                    break;
1038
                }
1039
            }
1040
        }
1041
        if(treePath!=null && !treePath.isEmpty()) {
1042
            //get last index
1043
            int nodeId = Integer.parseInt(treePath.get(treePath.size()-1));
1044
            TaxonNode taxonNode = dao.load(nodeId, null);
1045
            commonParent = new TaxonNodeDto(taxonNode);
1046
        }
1047
        return commonParent;
1048
    }
1049

    
1050
    @Override
1051
    public List<TaxonDistributionDTO> getTaxonDistributionDTOForSubtree(UUID parentNodeUuid, List<String> propertyPaths){
1052
        List<TaxonNode> nodes = listChildrenOf(load(parentNodeUuid), null, null,
1053
               true, true, propertyPaths);
1054
        List<TaxonDistributionDTO> result = new ArrayList<>();
1055
        for(TaxonNode node:nodes){
1056
            if (node.getTaxon() != null){
1057
                TaxonDistributionDTO dto = new TaxonDistributionDTO(node.getTaxon());
1058
                result.add(dto);
1059
            }
1060

    
1061
        }
1062

    
1063
        return result;
1064
    }
1065

    
1066

    
1067

    
1068
}
(96-96/103)