Project

General

Profile

Download (59.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
package eu.etaxonomy.cdm.api.service;
10

    
11
import java.util.ArrayList;
12
import java.util.Arrays;
13
import java.util.Collection;
14
import java.util.Collections;
15
import java.util.Comparator;
16
import java.util.HashSet;
17
import java.util.Iterator;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.Set;
21
import java.util.UUID;
22
import java.util.stream.Collectors;
23

    
24
import org.apache.log4j.Logger;
25
import org.springframework.beans.factory.annotation.Autowired;
26
import org.springframework.security.core.Authentication;
27
import org.springframework.stereotype.Service;
28
import org.springframework.transaction.annotation.Transactional;
29
import org.springframework.transaction.interceptor.TransactionAspectSupport;
30

    
31
import eu.etaxonomy.cdm.api.service.UpdateResult.Status;
32
import eu.etaxonomy.cdm.api.service.config.ForSubtreeConfiguratorBase;
33
import eu.etaxonomy.cdm.api.service.config.NodeDeletionConfigurator.ChildHandling;
34
import eu.etaxonomy.cdm.api.service.config.PublishForSubtreeConfigurator;
35
import eu.etaxonomy.cdm.api.service.config.SecundumForSubtreeConfigurator;
36
import eu.etaxonomy.cdm.api.service.config.SubtreeCloneConfigurator;
37
import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
38
import eu.etaxonomy.cdm.api.service.config.TaxonNodeDeletionConfigurator;
39
import eu.etaxonomy.cdm.api.service.dto.CdmEntityIdentifier;
40
import eu.etaxonomy.cdm.api.service.dto.CreateTaxonDTO;
41
import eu.etaxonomy.cdm.api.service.dto.TaxonDistributionDTO;
42
import eu.etaxonomy.cdm.api.service.pager.Pager;
43
import eu.etaxonomy.cdm.api.service.pager.PagerUtils;
44
import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
45
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
46
import eu.etaxonomy.cdm.common.monitor.DefaultProgressMonitor;
47
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
48
import eu.etaxonomy.cdm.common.monitor.SubProgressMonitor;
49
import eu.etaxonomy.cdm.compare.taxon.HomotypicGroupTaxonComparator;
50
import eu.etaxonomy.cdm.compare.taxon.TaxonNodeSortMode;
51
import eu.etaxonomy.cdm.filter.TaxonNodeFilter;
52
import eu.etaxonomy.cdm.hibernate.HHH_9751_Util;
53
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
54
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
55
import eu.etaxonomy.cdm.model.common.CdmBase;
56
import eu.etaxonomy.cdm.model.common.Language;
57
import eu.etaxonomy.cdm.model.common.LanguageString;
58
import eu.etaxonomy.cdm.model.common.TreeIndex;
59
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
60
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
61
import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
62
import eu.etaxonomy.cdm.model.description.TaxonDescription;
63
import eu.etaxonomy.cdm.model.metadata.SecReferenceHandlingEnum;
64
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
65
import eu.etaxonomy.cdm.model.name.HybridRelationship;
66
import eu.etaxonomy.cdm.model.name.Rank;
67
import eu.etaxonomy.cdm.model.name.TaxonName;
68
import eu.etaxonomy.cdm.model.permission.Operation;
69
import eu.etaxonomy.cdm.model.reference.NamedSource;
70
import eu.etaxonomy.cdm.model.reference.Reference;
71
import eu.etaxonomy.cdm.model.taxon.Classification;
72
import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
73
import eu.etaxonomy.cdm.model.taxon.Synonym;
74
import eu.etaxonomy.cdm.model.taxon.SynonymType;
75
import eu.etaxonomy.cdm.model.taxon.Taxon;
76
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
77
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
78
import eu.etaxonomy.cdm.model.taxon.TaxonNodeAgentRelation;
79
import eu.etaxonomy.cdm.model.taxon.TaxonNodeStatus;
80
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
81
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
82
import eu.etaxonomy.cdm.model.term.DefinedTerm;
83
import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
84
import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
85
import eu.etaxonomy.cdm.persistence.dao.name.IHomotypicalGroupDao;
86
import eu.etaxonomy.cdm.persistence.dao.reference.IOriginalSourceDao;
87
import eu.etaxonomy.cdm.persistence.dao.reference.IReferenceDao;
88
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
89
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
90
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeFilterDao;
91
import eu.etaxonomy.cdm.persistence.dto.HomotypicGroupDto;
92
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
93
import eu.etaxonomy.cdm.persistence.permission.ICdmPermissionEvaluator;
94
import eu.etaxonomy.cdm.persistence.query.OrderHint;
95

    
96
/**
97
 * @author n.hoffmann
98
 * @since Apr 9, 2010
99
 */
100
@Service
101
@Transactional(readOnly = true)
102
public class TaxonNodeServiceImpl
103
           extends AnnotatableServiceBase<TaxonNode, ITaxonNodeDao>
104
           implements ITaxonNodeService{
105

    
106
    private static final Logger logger = Logger.getLogger(TaxonNodeServiceImpl.class);
107

    
108
    @Autowired
109
    private IBeanInitializer defaultBeanInitializer;
110

    
111
    @Autowired
112
    private ITaxonService taxonService;
113

    
114
//    @Autowired
115
//    private IReferenceService referenceService;
116

    
117
    @Autowired
118
    private IDescriptiveDataSetService dataSetService;
119

    
120
    @Autowired
121
    private IAgentService agentService;
122

    
123
    @Autowired
124
    private INameService nameService;
125

    
126
    @Autowired
127
    private IOriginalSourceDao sourceDao;
128

    
129
    @Autowired
130
    private ITaxonNodeFilterDao nodeFilterDao;
131

    
132
    @Autowired
133
    private IReferenceDao referenceDao;
134

    
135
    @Autowired
136
    private IClassificationDao classificationDao;
137

    
138
    @Autowired
139
    private IHomotypicalGroupDao homotypicalGroupDao;
140

    
141
    @Autowired
142
    IProgressMonitorService progressMonitorService;
143

    
144
    @Autowired
145
    private ICdmPermissionEvaluator permissionEvaluator;
146

    
147
    @Override
148
    public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
149
            List<String> propertyPaths, boolean recursive,  boolean includeUnpublished,
150
            TaxonNodeSortMode sortMode) {
151

    
152
        taxonNode = load(taxonNode.getUuid());
153
        List<TaxonNode> childNodes;
154
        if (recursive == true){
155
            Comparator<TaxonNode> comparator = sortMode == null? null : sortMode.comparator();
156
            childNodes = dao.listChildrenOf(taxonNode, null, null, recursive, includeUnpublished, propertyPaths, comparator);
157
        }else if (includeUnpublished){
158
            childNodes = new ArrayList<>(taxonNode.getChildNodes());
159
        }else{
160
            childNodes = new ArrayList<>();
161
            for (TaxonNode node:taxonNode.getChildNodes()){
162
                if (node.getTaxon().isPublish()){
163
                    childNodes.add(node);
164
                }
165
            }
166
        }
167

    
168
        HHH_9751_Util.removeAllNull(childNodes);
169

    
170
        if (recursive == false && sortMode != null){
171
            Comparator<TaxonNode> comparator = sortMode.comparator();
172
        	Collections.sort(childNodes, comparator);
173
        }
174
        defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
175
        return childNodes;
176
    }
177

    
178
    @Override
179
    public List<TaxonNode> listChildrenOf(TaxonNode node, Integer pageSize, Integer pageIndex,
180
            boolean recursive, boolean includeUnpublished, List<String> propertyPaths){
181
        return dao.listChildrenOf(node, pageSize, pageIndex, recursive, includeUnpublished, propertyPaths, null);
182
    }
183

    
184
    @Override
185
    public TaxonNodeDto getParentUuidAndTitleCache(ITaxonTreeNode child) {
186
        UUID uuid = child.getUuid();
187
        int id = child.getId();
188
        TaxonNodeDto uuidAndTitleCache = new TaxonNodeDto(uuid, id, null);
189
        return getParentUuidAndTitleCache(uuidAndTitleCache);
190
    }
191

    
192
    @Override
193
    public TaxonNodeDto getParentUuidAndTitleCache(TaxonNodeDto child) {
194
        return dao.getParentUuidAndTitleCache(child);
195
    }
196

    
197
    @Override
198
    public List<TaxonNodeDto> listChildNodesAsTaxonNodeDto(TaxonNodeDto parent) {
199
        return dao.listChildNodesAsTaxonNodeDto(parent);
200
    }
201

    
202
    @Override
203
    public List<TaxonNodeDto> getUuidAndTitleCache(Integer limit, String pattern, UUID classificationUuid) {
204
        return dao.getUuidAndTitleCache(limit, pattern, classificationUuid, true);
205
    }
206

    
207
    @Override
208
    public List<TaxonNodeDto> listChildNodesAsTaxonNodeDto(ITaxonTreeNode parent) {
209
        List<String> propertyPaths = new ArrayList<>();
210
        propertyPaths.add("parent");
211
        parent = dao.load(parent.getId(), propertyPaths);
212
        TaxonNodeDto uuidAndTitleCache = new TaxonNodeDto(parent);
213
        return listChildNodesAsTaxonNodeDto(uuidAndTitleCache);
214
    }
215

    
216
    @Override
217
    public List<TaxonNodeDto> taxonNodeDtoParentRank(Classification classification, Rank rank, TaxonName name) {
218
    	return dao.getParentTaxonNodeDtoForRank(classification, rank, name);
219
    }
220

    
221
    @Override
222
    public List<TaxonNodeDto> taxonNodeDtoParentRank(Classification classification, Rank rank, TaxonBase<?> taxonBase) {
223
        return dao.getParentTaxonNodeDtoForRank(classification, rank, taxonBase);
224
    }
225

    
226
    @Override
227
    public Pager<TaxonNodeDto> pageChildNodesDTOs(UUID taxonNodeUuid, boolean recursive,  boolean includeUnpublished,
228
            boolean doSynonyms, TaxonNodeSortMode sortMode,
229
            Integer pageSize, Integer pageIndex) {
230

    
231
        TaxonNode parentNode = dao.load(taxonNodeUuid);
232

    
233
        List<CdmBase> allRecords = new ArrayList<>();
234

    
235
        //acceptedTaxa
236
        List<TaxonNode> childNodes = loadChildNodesOfTaxonNode(parentNode, null, recursive, includeUnpublished, sortMode);
237
        allRecords.addAll(childNodes);
238

    
239
        //add synonyms if pager is not yet full synonyms
240
        if (doSynonyms){
241
            List<Synonym> synList = new ArrayList<>(parentNode.getTaxon().getSynonyms());
242
            Collections.sort(synList, new HomotypicGroupTaxonComparator(null));
243
            //TODO: test sorting
244

    
245
            allRecords.addAll(synList);
246
        }
247

    
248
        List<TaxonNodeDto> dtos = new ArrayList<>(pageSize==null?25:pageSize);
249
        long totalCount = Long.valueOf(allRecords.size());
250

    
251
        TaxonName parentName = null;
252

    
253
        for(CdmBase item : PagerUtils.pageList(allRecords, pageIndex, pageSize)) {
254
            if (item.isInstanceOf(TaxonNode.class)){
255
                dtos.add(new TaxonNodeDto(CdmBase.deproxy(item, TaxonNode.class)));
256
            }else if (item.isInstanceOf(Synonym.class)){
257
                Synonym synonym = CdmBase.deproxy(item, Synonym.class);
258
                parentName = parentName == null? parentNode.getTaxon().getName(): parentName;
259
                boolean isHomotypic = synonym.getName().isHomotypic(parentName);
260
                dtos.add(new TaxonNodeDto(synonym, isHomotypic));
261
            }
262
        }
263
        return new DefaultPagerImpl<>(pageIndex, totalCount, pageSize , dtos);
264
    }
265

    
266
    @Override
267
    public TaxonNodeDto parentDto(UUID taxonNodeUuid) {
268
        if (taxonNodeUuid == null){
269
            return null;
270
        }
271
        TaxonNode taxonNode = dao.load(taxonNodeUuid);
272
        if(taxonNode.getParent() != null) {
273
            return new TaxonNodeDto(taxonNode.getParent());
274
        }
275
        return null;
276
    }
277

    
278
    @Override
279
    public TaxonNodeDto dto(UUID taxonNodeUuid) {
280
        if (taxonNodeUuid == null){
281
            return null;
282
        }
283
        return dao.getTaxonNodeDto(taxonNodeUuid);
284
    }
285

    
286
    @Override
287
    public TaxonNodeDto dto(UUID taxonUuid, UUID classificationUuid) {
288
        if (taxonUuid == null){
289
            return null;
290
        }
291
        List<TaxonNodeDto> taxonNodes = dao.getTaxonNodeForTaxonInClassificationDto(taxonUuid, classificationUuid);
292
        if (!taxonNodes.isEmpty()){
293
            return taxonNodes.get(0);
294
        }
295
        return null;
296
    }
297

    
298
    @Override
299
    @Autowired
300
    protected void setDao(ITaxonNodeDao dao) {
301
        this.dao = dao;
302
    }
303

    
304
    @Override
305
    @Transactional(readOnly = false)
306
    public DeleteResult makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode, TaxonNode newAcceptedTaxonNode,
307
            SynonymType synonymType, Reference citation, String microReference, SecReferenceHandlingEnum secHandling, boolean setNameInSource)  {
308

    
309
        // TODO at the moment this method only moves synonym-, concept relations and descriptions to the new accepted taxon
310
        // in a future version we also want to move cdm data like annotations, marker, so., but we will need a policy for that
311
        if (oldTaxonNode == null || newAcceptedTaxonNode == null || oldTaxonNode.getTaxon().getName() == null){
312
            throw new IllegalArgumentException("A mandatory parameter was null.");
313
        }
314

    
315
        if(oldTaxonNode.equals(newAcceptedTaxonNode)){
316
            throw new IllegalArgumentException("Taxon can not be made synonym of its own.");
317
        }
318

    
319
        Classification classification = oldTaxonNode.getClassification();
320
        Taxon oldTaxon = HibernateProxyHelper.deproxy(oldTaxonNode.getTaxon());
321
        Taxon newAcceptedTaxon = (Taxon)this.taxonService.find(newAcceptedTaxonNode.getTaxon().getUuid());
322
        newAcceptedTaxon = HibernateProxyHelper.deproxy(newAcceptedTaxon);
323
        // Move oldTaxon to newTaxon
324
        //TaxonName synonymName = oldTaxon.getName();
325
        TaxonName newSynonymName = CdmBase.deproxy(oldTaxon.getName());
326
        HomotypicalGroup group = CdmBase.deproxy(newSynonymName.getHomotypicalGroup());
327
        if (synonymType == null){
328
            if (newSynonymName.isHomotypic(newAcceptedTaxon.getName())){
329
                synonymType = SynonymType.HOMOTYPIC_SYNONYM_OF();
330
            }else{
331
                synonymType = SynonymType.HETEROTYPIC_SYNONYM_OF();
332
            }
333
        }
334

    
335
        //set homotypic group
336
        TaxonName newAcceptedTaxonName = HibernateProxyHelper.deproxy(newAcceptedTaxon.getName(), TaxonName.class);
337
        newAcceptedTaxon.setName(newAcceptedTaxonName);
338
        Reference secNewAccepted = newAcceptedTaxon.getSec();
339
        Reference secOldAccepted = oldTaxon.getSec();
340
        boolean uuidsEqual = (secNewAccepted != null && secOldAccepted != null && secNewAccepted.equals(secOldAccepted)) || (secNewAccepted == null && secOldAccepted == null);
341
        Reference newSec = citation;
342
        //keep when same only warns in ui, the sec still
343
        if (secHandling != null &&  secHandling.equals(SecReferenceHandlingEnum.KeepOrWarn) ){
344
            newSec = oldTaxon.getSec();
345
        }
346
        if (secHandling != null && secHandling.equals(SecReferenceHandlingEnum.AlwaysDelete)){
347
            newSec = null;
348
        }
349

    
350
        Synonym newSyn = newAcceptedTaxon.addSynonymName(newSynonymName, newSec, microReference, synonymType);
351
        if (newSec == null){
352
            newSyn.setSec(newSec);
353
        }
354
        newSyn.setPublish(oldTaxon.isPublish());
355

    
356
        // Move Synonyms to new Taxon
357
        // From ticket 3163 we can move taxon with accepted name having homotypic synonyms
358
        List<Synonym> synonymsInHomotypicalGroup = null;
359

    
360
        //the synonyms of the homotypical group of the old taxon
361
        if (synonymType.equals(SynonymType.HOMOTYPIC_SYNONYM_OF())){
362
        	synonymsInHomotypicalGroup = oldTaxon.getSynonymsInGroup(group);
363
        }
364

    
365
        Set<Synonym> syns = new HashSet<>(oldTaxon.getSynonyms());
366
        for(Synonym synonym : syns){
367
            SynonymType srt;
368
            if(synonym.getHomotypicGroup()!= null
369
                    && synonym.getHomotypicGroup().equals(newAcceptedTaxonName.getHomotypicalGroup())) {
370
                srt = SynonymType.HOMOTYPIC_SYNONYM_OF();
371
            } else if(synonym.getType() != null && synonym.getType().equals(SynonymType.HOMOTYPIC_SYNONYM_OF())) {
372
            	if (synonymType.equals(SynonymType.HOMOTYPIC_SYNONYM_OF())){
373
            		srt = SynonymType.HOMOTYPIC_SYNONYM_OF();
374
            	} else{
375
            		srt = SynonymType.HETEROTYPIC_SYNONYM_OF();
376
            	}
377
            } else {
378
                if (synonymsInHomotypicalGroup != null && synonymsInHomotypicalGroup.contains(synonym)){
379
                    srt = SynonymType.HOMOTYPIC_SYNONYM_OF();
380
                }else{
381
                    srt = synonym.getType();
382
                }
383
            }
384
            if (secHandling != null &&  !secHandling.equals(SecReferenceHandlingEnum.KeepOrWarn)){
385
                synonym.setSec(newSec);
386
            }
387
            newAcceptedTaxon.addSynonym(synonym, srt);
388
        }
389

    
390
        // CHILD NODES
391
        if(oldTaxonNode.getChildNodes() != null && oldTaxonNode.getChildNodes().size() != 0){
392
        	List<TaxonNode> childNodes = new ArrayList<>();
393
        	for (TaxonNode childNode : oldTaxonNode.getChildNodes()){
394
        		childNodes.add(childNode);
395
        	}
396
            for(TaxonNode childNode :childNodes){
397
                newAcceptedTaxonNode.addChildNode(childNode, childNode.getReference(), childNode.getMicroReference()); // childNode.getSynonymToBeUsed()
398
            }
399
        }
400

    
401
        //Move Taxon RelationShips to new Taxon
402
        for(TaxonRelationship taxonRelationship : oldTaxon.getTaxonRelations()){
403
            Taxon fromTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getFromTaxon());
404
            Taxon toTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getToTaxon());
405
            if (fromTaxon == oldTaxon){
406
                newAcceptedTaxon.addTaxonRelation(taxonRelationship.getToTaxon(), taxonRelationship.getType(),
407
                        taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
408

    
409
            }else if(toTaxon == oldTaxon){
410
               fromTaxon.addTaxonRelation(newAcceptedTaxon, taxonRelationship.getType(),
411
                        taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
412
               taxonService.saveOrUpdate(fromTaxon);
413

    
414
            }else{
415
                logger.warn("Taxon is not part of its own Taxonrelationship");
416
            }
417
            // Remove old relationships
418

    
419
            fromTaxon.removeTaxonRelation(taxonRelationship);
420
            toTaxon.removeTaxonRelation(taxonRelationship);
421
            taxonRelationship.setToTaxon(null);
422
            taxonRelationship.setFromTaxon(null);
423
        }
424

    
425
        //Move descriptions to new taxon
426
        List<TaxonDescription> descriptions = new ArrayList<TaxonDescription>( oldTaxon.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())
427
        for(TaxonDescription description : descriptions){
428
            String message = "Description copied from former accepted taxon: %s (Old title: %s)";
429
            message = String.format(message, oldTaxon.getTitleCache(), description.getTitleCache());
430
            description.setTitleCache(message, true);
431
            if (setNameInSource) {
432
                for (DescriptionElementBase element: description.getElements()){
433
                    for (DescriptionElementSource source: element.getSources()){
434
                        if (source.getNameUsedInSource() == null){
435
                            source.setNameUsedInSource(newSynonymName);
436
                        }
437
                    }
438
                }
439
            }
440
            //oldTaxon.removeDescription(description, false);
441
            newAcceptedTaxon.addDescription(description);
442
        }
443
        oldTaxon.clearDescriptions();
444

    
445
        taxonService.saveOrUpdate(newAcceptedTaxon);
446

    
447
        taxonService.saveOrUpdate(oldTaxon);
448
        taxonService.getSession().flush();
449

    
450
        TaxonDeletionConfigurator conf = new TaxonDeletionConfigurator();
451
        conf.setDeleteSynonymsIfPossible(false);
452
        conf.setDeleteNameIfPossible(false);
453
        DeleteResult taxonDeleteResult = taxonService.isDeletable(oldTaxon.getUuid(), conf);
454

    
455
        DeleteResult result;
456
        if (taxonDeleteResult.isOk()){
457
        	 result = taxonService.deleteTaxon(oldTaxon.getUuid(), conf, classification.getUuid());
458
        }else{
459
        	TaxonNodeDeletionConfigurator config = new TaxonNodeDeletionConfigurator();
460
        	config.setDeleteElement(false);
461
        	conf.setTaxonNodeConfig(config);
462
        	result = deleteTaxonNode(oldTaxonNode, conf);
463
        	result.getRelatedObjects().addAll(taxonDeleteResult.getRelatedObjects());  //we want to know what causes that the taxon can not be deleted
464
        	result.getExceptions().addAll(taxonDeleteResult.getExceptions()); //same for the exceptions
465
        }
466

    
467
        result.addUpdatedObject(newAcceptedTaxon);
468

    
469
        //oldTaxonNode.delete();
470
        return result;
471
    }
472

    
473
    @Override
474
    @Transactional(readOnly = false)
475
    public DeleteResult makeTaxonNodeSynonymsOfAnotherTaxonNode( Set<UUID> oldTaxonNodeUuids,
476
            UUID newAcceptedTaxonNodeUUIDs,
477
            SynonymType synonymType,
478
            UUID citation,
479
            String microReference,
480
            SecReferenceHandlingEnum secHandling,
481
            boolean setNameInSource) {
482
    	DeleteResult result = new DeleteResult();
483
    	for (UUID nodeUuid: oldTaxonNodeUuids) {
484
    		result.includeResult(makeTaxonNodeASynonymOfAnotherTaxonNode(nodeUuid, newAcceptedTaxonNodeUUIDs, synonymType, citation, microReference, secHandling, setNameInSource));
485
    	}
486
    	return result;
487
    }
488

    
489
    @Override
490
    @Transactional(readOnly = false)
491
    public DeleteResult makeTaxonNodeASynonymOfAnotherTaxonNode(UUID oldTaxonNodeUuid,
492
            UUID newAcceptedTaxonNodeUUID,
493
            SynonymType synonymType,
494
            UUID citationUuid,
495
            String microReference,
496
            SecReferenceHandlingEnum secHandling,
497
            boolean setNameInSource) {
498

    
499
        TaxonNode oldTaxonNode = dao.load(oldTaxonNodeUuid);
500
        TaxonNode oldTaxonParentNode = oldTaxonNode.getParent();
501
        TaxonNode newTaxonNode = dao.load(newAcceptedTaxonNodeUUID);
502
        Reference citation = referenceDao.load(citationUuid);
503

    
504
        switch (secHandling){
505
            case AlwaysDelete:
506
                citation = null;
507
                break;
508
            case UseNewParentSec:
509
                citation = newTaxonNode.getTaxon() != null? newTaxonNode.getTaxon().getSec(): null;
510
                break;
511
            case KeepOrWarn:
512

    
513
                Reference synSec = oldTaxonNode.getTaxon().getSec();
514
                if (synSec != null ){
515
                    citation = CdmBase.deproxy(synSec);
516
                }
517
                break;
518
            case KeepOrSelect:
519

    
520
                break;
521
            default:
522
                break;
523
        }
524

    
525
        DeleteResult result = makeTaxonNodeASynonymOfAnotherTaxonNode(oldTaxonNode,
526
                newTaxonNode,
527
                synonymType,
528
                citation,
529
                microReference,
530
                secHandling, setNameInSource);
531

    
532
        result.addUpdatedCdmId(CdmEntityIdentifier.NewInstance(oldTaxonParentNode));
533
        result.addUpdatedCdmId(CdmEntityIdentifier.NewInstance(newTaxonNode));
534
        result.setCdmEntity(oldTaxonParentNode);
535
        return result;
536
    }
537

    
538
    @Override
539
    @Transactional(readOnly = false)
540
    public DeleteResult deleteTaxonNodes(List<TaxonNode> list, TaxonDeletionConfigurator config) {
541

    
542
        if (config == null){
543
        	config = new TaxonDeletionConfigurator();
544
        }
545
        DeleteResult result = new DeleteResult();
546
        Classification classification = null;
547
        List<TaxonNode> taxonNodes = new ArrayList<>(list);
548

    
549
        for (TaxonNode treeNode:taxonNodes){
550
        	if (treeNode != null){
551

    
552
        		TaxonNode taxonNode;
553
	            taxonNode = CdmBase.deproxy(treeNode);
554
	            TaxonNode parent = taxonNode.getParent();
555
	            	//check whether the node has children or the children are already deleted
556
	            if(taxonNode.hasChildNodes()) {
557
            		List<TaxonNode> children = new ArrayList<> ();
558
            		List<TaxonNode> childNodesList = taxonNode.getChildNodes();
559
        			children.addAll(childNodesList);
560
        			//To avoid NPE when child is also in list of taxonNodes, remove it from the list
561
        			Iterator<TaxonNode> it = taxonNodes.iterator();
562
        			for (TaxonNode child: children) {
563
        				while (it.hasNext()) {
564
        					if (it.next().equals(child)) {
565
        						it.remove();
566
        					}
567
        				}
568
        			}
569
        			int compare = config.getTaxonNodeConfig().getChildHandling().compareTo(ChildHandling.DELETE);
570
        			boolean childHandling = (compare == 0)? true: false;
571
            		if (childHandling){
572
            			boolean changeDeleteTaxon = false;
573
            			if (!config.getTaxonNodeConfig().isDeleteTaxon()){
574
            				config.getTaxonNodeConfig().setDeleteTaxon(true);
575
            				changeDeleteTaxon = true;
576
            			}
577
            			DeleteResult resultNodes = deleteTaxonNodes(children, config);
578
            			if (!resultNodes.isOk()){
579
                            result.addExceptions(resultNodes.getExceptions());
580
                            result.setStatus(resultNodes.getStatus());
581
                        }
582
            			if (changeDeleteTaxon){
583
            				config.getTaxonNodeConfig().setDeleteTaxon(false);
584
            			}
585

    
586
            		} else {
587
            			//move the children to the parent
588

    
589
            			for (TaxonNode child: childNodesList){
590
            				parent.addChildNode(child, child.getReference(), child.getMicroReference());
591
            			}
592
            		}
593
            	}
594

    
595
	            classification = taxonNode.getClassification();
596

    
597
	            if (classification.getRootNode().equals(taxonNode)){
598
	            	classification.removeRootNode();
599
	            	classification = null;
600
	            }else if (classification.getChildNodes().contains(taxonNode)){
601
            		Taxon taxon = taxonNode.getTaxon();
602
            		classification.deleteChildNode(taxonNode);
603

    
604
	            	//node is rootNode
605
	            	if (taxon != null){
606

    
607
	            		if (config.getTaxonNodeConfig().isDeleteTaxon()){
608
	            		    taxonService.saveOrUpdate(taxon);
609
	            		    saveOrUpdate(taxonNode);
610

    
611
			            	TaxonDeletionConfigurator configNew = new TaxonDeletionConfigurator();
612
			            	configNew.setClassificationUuid(classification.getUuid());
613
			            	DeleteResult resultTaxon = taxonService.deleteTaxon(taxon.getUuid(), configNew, classification.getUuid());
614
			            	if (!resultTaxon.isOk()){
615
                                result.addExceptions(resultTaxon.getExceptions());
616
                                result.setStatus(resultTaxon.getStatus());
617
                            }
618

    
619
		            	}
620
	            	}
621
            		classification = null;
622

    
623
	            } else {
624
	            	//classification = null;
625
	            	Taxon taxon = taxonNode.getTaxon();
626
	            	taxon = CdmBase.deproxy(taxon);
627
	            	if (taxon != null){
628
	            		taxon.removeTaxonNode(taxonNode);
629
	            		if (config.getTaxonNodeConfig().isDeleteTaxon()){
630
			            	TaxonDeletionConfigurator configNew = new TaxonDeletionConfigurator();
631
			            	saveOrUpdate(taxonNode);
632
			            	taxonService.saveOrUpdate(taxon);
633
			            	DeleteResult resultTaxon = taxonService.deleteTaxon(taxon.getUuid(), configNew, classification.getUuid());
634

    
635
                            if (!resultTaxon.isOk()){
636
                                result.addExceptions(resultTaxon.getExceptions());
637
                                result.setStatus(resultTaxon.getStatus());
638
                            }
639
		            	}
640
	            	}
641

    
642
	            }
643

    
644
	            result.addUpdatedObject(parent);
645
	            if(result.getCdmEntity() == null){
646
	                result.setCdmEntity(taxonNode);
647
                }
648
	            UUID uuid = dao.delete(taxonNode);
649
	            logger.debug("Deleted node " +uuid.toString());
650

    
651
	        }
652
        }
653
        /*if (classification != null){
654
            result.addUpdatedObject(classification);
655
        	DeleteResult resultClassification = classService.delete(classification);
656
        	 if (!resultClassification.isOk()){
657
                 result.addExceptions(resultClassification.getExceptions());
658
                 result.setStatus(resultClassification.getStatus());
659
             }
660
        }*/
661
        return result;
662
    }
663

    
664

    
665
    @Override
666
    @Transactional(readOnly = false)
667
    public DeleteResult deleteTaxonNodes(Collection<UUID> nodeUuids, TaxonDeletionConfigurator config) {
668
        List<TaxonNode> nodes = new ArrayList<>();
669
        for(UUID nodeUuid : nodeUuids) {
670
            nodes.add(dao.load(nodeUuid));
671
        }
672
        return deleteTaxonNodes(nodes, config);
673
    }
674

    
675

    
676
    @Override
677
    @Transactional(readOnly = false)
678
    public DeleteResult deleteTaxonNode(UUID nodeUUID, TaxonDeletionConfigurator config) {
679

    
680
    	TaxonNode node = CdmBase.deproxy(dao.load(nodeUUID));
681
    	return deleteTaxonNode(node, config);
682
    }
683

    
684
    @Override
685
    @Transactional(readOnly = false)
686
    public DeleteResult deleteTaxonNode(TaxonNode node, TaxonDeletionConfigurator config) {
687
        DeleteResult result = new DeleteResult();
688
        if (node == null){
689
            result.setAbort();
690
            result.addException(new Exception("The TaxonNode was already deleted."));
691
            return result;
692
        }
693
        Taxon taxon = null;
694
        try{
695
            taxon = HibernateProxyHelper.deproxy(node.getTaxon());
696
        }catch(NullPointerException e){
697
            result.setAbort();
698
            result.addException(new Exception("The Taxon was already deleted."));
699
        }
700

    
701
    	TaxonNode parent = HibernateProxyHelper.deproxy(node.getParent(), TaxonNode.class);
702
    	if (config == null){
703
    		config = new TaxonDeletionConfigurator();
704
    	}
705

    
706
    	if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.MOVE_TO_PARENT)){
707
    	   Object[] children = node.getChildNodes().toArray();
708
    	   TaxonNode childNode;
709
    	   for (Object child: children){
710
    	       childNode = (TaxonNode) child;
711
    	       parent.addChildNode(childNode, childNode.getReference(), childNode.getMicroReference());
712
    	   }
713
    	}else{
714
    	    DeleteResult tmpResult = deleteTaxonNodes(node.getChildNodes(), config);
715
    	    result.includeResult(tmpResult);
716
    	}
717

    
718
    	//remove node from DescriptiveDataSet
719
        commonService.getReferencingObjects(node).stream()
720
            .filter(obj->obj instanceof DescriptiveDataSet)
721
            .forEach(dataSet->{
722
                ((DescriptiveDataSet)dataSet).removeTaxonSubtree(node);
723
                dataSetService.saveOrUpdate((DescriptiveDataSet) dataSet);
724
        });
725

    
726
    	if (taxon != null){
727
        	if (config.getTaxonNodeConfig().isDeleteTaxon() && (config.isDeleteInAllClassifications() || taxon.getTaxonNodes().size() == 1)){
728
        		result = taxonService.deleteTaxon(taxon.getUuid(), config, node.getClassification().getUuid());
729
        		result.addUpdatedObject(parent);
730
        		if (result.isOk()){
731
        			return result;
732
        		}
733
        	} else {
734
        	    result.addUpdatedObject(taxon);
735
        	}
736
    	}
737
    	result.setCdmEntity(node);
738
    	boolean success = true;
739
    	if (taxon != null){
740
    	    success = taxon.removeTaxonNode(node);
741
    	    taxonService.saveOrUpdate(taxon);
742
    	}
743
    	dao.saveOrUpdate(parent);
744

    
745
    	result.addUpdatedObject(parent);
746

    
747
    	if (success){
748
			result.setStatus(Status.OK);
749
			if (parent != null){
750
    			parent = HibernateProxyHelper.deproxy(parent, TaxonNode.class);
751
    			int index = parent.getChildNodes().indexOf(node);
752
    			if (index > -1){
753
    			    parent.removeChild(index);
754
    			}
755
			}
756
    		if (!dao.delete(node, config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)).equals(null)){
757
    		    result.getUpdatedObjects().remove(node);
758
    			result.addDeletedObject(node);
759
    			return result;
760
    		} else {
761
    			result.setError();
762
    			return result;
763
    		}
764
    	}else{
765
    	    if (dao.findByUuid(node.getUuid()) != null){
766
        		result.setError();
767
        		result.addException(new Exception("The node can not be removed from the taxon."));
768
    		}
769
    		return result;
770
    	}
771
    }
772

    
773

    
774
    @Override
775
    public List<TaxonNode> listAllNodesForClassification(Classification classification, Integer start, Integer end) {
776
        return dao.getTaxonOfAcceptedTaxaByClassification(classification, start, end);
777
    }
778

    
779
    @Override
780
    public int countAllNodesForClassification(Classification classification) {
781
        return dao.countTaxonOfAcceptedTaxaByClassification(classification);
782
    }
783

    
784
    @Override
785
    @Transactional
786
    public UpdateResult moveTaxonNode(UUID taxonNodeUuid, UUID targetNodeUuid, int movingType, SecReferenceHandlingEnum secHandling, UUID secUuid){
787
        TaxonNode taxonNode = HibernateProxyHelper.deproxy(dao.load(taxonNodeUuid));
788
    	TaxonNode targetNode = HibernateProxyHelper.deproxy(dao.load(targetNodeUuid));
789
    	Reference sec = null;
790
    	if (secUuid != null){
791
    	    sec = HibernateProxyHelper.deproxy(referenceDao.load(secUuid));
792
    	}
793
    	UpdateResult result = moveTaxonNode(taxonNode, targetNode, movingType, secHandling, sec);
794
    	return result;
795
    }
796

    
797
    @Override
798
    @Transactional
799
    public UpdateResult moveTaxonNode(TaxonNode taxonNode, TaxonNode newParent, int movingType, SecReferenceHandlingEnum secHandling, Reference sec){
800
        UpdateResult result = new UpdateResult();
801

    
802
        TaxonNode parentParent = HibernateProxyHelper.deproxy(newParent.getParent());
803
        Integer sortIndex = -1;
804
        if (movingType == 0){
805
            sortIndex = 0;
806
        }else if (movingType == 1){
807
            sortIndex = newParent.getSortIndex();
808
            newParent = parentParent;
809
        } else if (movingType == 2){
810
            sortIndex = newParent.getSortIndex() +1;
811
            newParent = parentParent;
812
        } else{
813
            result.setAbort();
814
            result.addException(new Exception("The moving type "+ movingType +" is not supported."));
815
        }
816

    
817
        if (secHandling.equals(SecReferenceHandlingEnum.AlwaysSelect) || (secHandling.equals(SecReferenceHandlingEnum.KeepOrSelect) && sec != null)){
818
            if (taxonNode.getTaxon() != null){
819
                taxonNode.getTaxon().setSec(sec);
820
            }
821
        }else if (secHandling.equals(SecReferenceHandlingEnum.AlwaysDelete)){
822
            if (taxonNode.getTaxon() != null){
823
                taxonNode.getTaxon().setSec(null);
824
            }
825
        }else if (secHandling.equals(SecReferenceHandlingEnum.UseNewParentSec)){
826
            if (taxonNode.getTaxon() != null && newParent.getTaxon()!= null){
827
                taxonNode.getTaxon().setSec(newParent.getTaxon().getSec());
828
            }
829
        }
830

    
831
        taxonNode = newParent.addChildNode(taxonNode, sortIndex, taxonNode.getReference(),  taxonNode.getMicroReference());
832
        result.addUpdatedObject(taxonNode);
833

    
834
        return result;
835
    }
836

    
837
    @Override
838
    @Transactional
839
    public UpdateResult moveTaxonNodes(Set<UUID> taxonNodeUuids, UUID newParentNodeUuid, int movingType, SecReferenceHandlingEnum secHandling, UUID secUuid, IProgressMonitor monitor){
840

    
841
        if (monitor == null){
842
            monitor = DefaultProgressMonitor.NewInstance();
843
        }
844
        UpdateResult result = new UpdateResult();
845
        List<String> taxonNodePropertyPath = new ArrayList<>();
846
        taxonNodePropertyPath.add("taxon.secSource.*");
847
        taxonNodePropertyPath.add("parent.taxon.secSource.*");
848
        TaxonNode targetNode = dao.load(newParentNodeUuid, taxonNodePropertyPath);
849
        List<TaxonNode> nodes = dao.list(taxonNodeUuids, null, null, null, null);
850
        Reference sec = referenceDao.load(secUuid);
851

    
852
        monitor.beginTask("Move Taxonnodes", nodes.size()*2);
853
        monitor.subTask("move taxon nodes");
854
        for (TaxonNode node: nodes){
855
            if (!monitor.isCanceled()){
856
                if (!nodes.contains(node.getParent())){
857
                    result.includeResult(moveTaxonNode(node, targetNode, movingType, secHandling, sec));
858
                }
859
                monitor.worked(1);
860
            }else{
861
                monitor.done();
862
                result.setAbort();
863
                break;
864
            }
865
        }
866
        if (!monitor.isCanceled()){
867
            monitor.subTask("saving and reindex");
868
            dao.saveOrUpdateAll(nodes);
869
        }else{
870
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
871
        }
872

    
873
        monitor.done();
874
        return result;
875
    }
876

    
877
    @Override
878
    public Pager<TaxonNodeAgentRelation> pageTaxonNodeAgentRelations(UUID taxonUuid, UUID classificationUuid,
879
            UUID agentUuid, UUID rankUuid, UUID relTypeUuid, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
880

    
881
        List<TaxonNodeAgentRelation> records = null;
882

    
883
        long count = dao.countTaxonNodeAgentRelations(taxonUuid, classificationUuid, agentUuid, rankUuid, relTypeUuid);
884
        if(PagerUtils.hasResultsInRange(count, pageIndex, pageSize)) {
885
            records = dao.listTaxonNodeAgentRelations(taxonUuid, classificationUuid,
886
                    agentUuid, rankUuid, relTypeUuid, PagerUtils.startFor(pageSize, pageIndex), PagerUtils.limitFor(pageSize), propertyPaths);
887
        }
888

    
889
        Pager<TaxonNodeAgentRelation> pager = new DefaultPagerImpl<>(pageIndex, count, pageSize, records);
890
        return pager;
891
    }
892

    
893
    @Override
894
    @Transactional
895
    public UpdateResult createNewTaxonNode(UUID parentNodeUuid, CreateTaxonDTO taxonDto,
896
            NamedSource source, String microref,
897
            TaxonNodeStatus status, Map<Language,LanguageString> statusNote){
898

    
899
        UpdateResult result = new UpdateResult();
900
        TaxonNode child = null;
901
        TaxonNode parent = null;
902
        try{
903
            TaxonName name = null;
904
            Taxon taxon = null;
905
            if (taxonDto.getTaxonUuid() != null){
906
                taxon = (Taxon) taxonService.load(taxonDto.getTaxonUuid());
907
                if (taxon == null){
908
                    throw new RuntimeException("Taxon for not found for id " + taxonDto.getTaxonUuid());
909
                }
910
            }else{
911
                if (taxonDto.getNameUuid() != null){
912
                    name = nameService.load(taxonDto.getNameUuid());
913
                    if (name == null){
914
                        throw new RuntimeException("Taxon name not found for id " + taxonDto.getTaxonUuid());
915
                    }
916
                } else {
917
                    UpdateResult tmpResult = nameService.parseName(taxonDto.getTaxonNameString(),
918
                            taxonDto.getCode(), taxonDto.getPreferredRank(),  true);
919
                    result.addUpdatedObjects(tmpResult.getUpdatedObjects());
920
                    name = (TaxonName)tmpResult.getCdmEntity();
921
                }
922
                Reference sec = null;
923
                if (taxonDto.getSecUuid() != null ){
924
                    sec = referenceDao.load(taxonDto.getSecUuid());
925
                }
926
                if (name != null && !name.isPersited()){
927
                    for (HybridRelationship rel : name.getHybridChildRelations()){
928
                        if (!rel.getHybridName().isPersited()) {
929
                            nameService.save(rel.getHybridName());
930
                        }
931
                        if (!rel.getParentName().isPersited()) {
932
                            nameService.save(rel.getParentName());
933
                        }
934
                    }
935
                }
936
                taxon = Taxon.NewInstance(name, sec);
937
                taxon.setPublish(taxonDto.isPublish());
938
            }
939

    
940
            parent = dao.load(parentNodeUuid);
941
            if (source != null){
942
                if (source.isPersited()){
943
                    source = (NamedSource) sourceDao.load(source.getUuid());
944
                }
945
                if (source.getCitation() != null){
946
                    source.setCitation(referenceDao.load(source.getCitation().getUuid()));
947
                }
948
                if (source.getNameUsedInSource() !=null){
949
                    source.setNameUsedInSource(nameService.load(source.getNameUsedInSource().getUuid()));
950
                }
951
            }
952

    
953
            child = parent.addChildTaxon(taxon, source);
954
            child.setStatus(status);
955

    
956
            if (statusNote != null){
957
                child.getStatusNote().putAll(statusNote);
958
            }
959

    
960
        }catch(Exception e){
961
            result.addException(e);
962
            result.setError();
963
            return result;
964
        }
965
        child = dao.save(child);
966

    
967
        result.addUpdatedObject(parent);
968
        if (child != null){
969
            result.setCdmEntity(child);
970
        }
971
        return result;
972
    }
973

    
974
    @Override
975
    @Transactional
976
    public UpdateResult addTaxonNodeAgentRelation(UUID taxonNodeUUID, UUID agentUUID, DefinedTerm relationshipType){
977
        UpdateResult result = new UpdateResult();
978
        TaxonNode node = dao.load(taxonNodeUUID);
979
        TeamOrPersonBase<?> agent = (TeamOrPersonBase<?>) agentService.load(agentUUID);
980
        node.addAgentRelation(relationshipType, agent);
981
        try{
982
            dao.merge(node, true);
983
        }catch (Exception e){
984
            result.setError();
985
            result.addException(e);
986
        }
987
        result.setCdmEntity(node);
988
        return result;
989
    }
990

    
991
    @Override
992
    @Transactional(readOnly=false)
993
    public UpdateResult setSecundumForSubtree(SecundumForSubtreeConfigurator config) {
994
        UpdateResult result = new UpdateResult();
995
        IProgressMonitor monitor = config.getMonitor();
996

    
997
        if (monitor == null){
998
            monitor = DefaultProgressMonitor.NewInstance();
999
        }
1000
        monitor.beginTask("Update secundum reference for subtree", 100);
1001
        monitor.subTask("Check start conditions");
1002

    
1003
        if (config.getSubtreeUuid() == null){
1004
            result.setError();
1005
            result.addException(new NullPointerException("No subtree given"));
1006
            monitor.done();
1007
            return result;
1008
        }
1009
        monitor.worked(1);
1010
        TaxonNode subTree = load(config.getSubtreeUuid());
1011
        if (subTree == null){
1012
            result.setError();
1013
            result.addException(new NullPointerException("Subtree does not exist"));
1014
            monitor.done();
1015
            return result;
1016
        }
1017
        monitor.worked(1);
1018

    
1019
        Reference newSec = null;
1020
        if (config.getNewSecundum() != null){
1021
            newSec = referenceDao.load(config.getNewSecundum().getUuid());
1022
            if (newSec == null){
1023
                result.setError();
1024
                result.addException(new NullPointerException("New secundum reference does not exist"));
1025
                monitor.done();
1026
                return result;
1027
            }
1028
        }
1029
        monitor.worked(1);
1030

    
1031
        monitor.subTask("Count records");
1032
        try {
1033
            boolean includeRelatedTaxa = config.isIncludeProParteSynonyms() || config.isIncludeMisapplications();
1034

    
1035
            TreeIndex subTreeIndex = TreeIndex.NewInstance(subTree.treeIndex());
1036
            int count = config.isIncludeAcceptedTaxa() ? dao.countSecundumForSubtreeAcceptedTaxa(subTreeIndex, newSec, config.isOverwriteExisting(), config.isIncludeSharedTaxa(), config.isEmptySecundumDetail()):0;
1037
            monitor.worked(2);
1038
            count += config.isIncludeSynonyms() ? dao.countSecundumForSubtreeSynonyms(subTreeIndex, newSec, config.isOverwriteExisting(), config.isIncludeSharedTaxa(), config.isEmptySecundumDetail()) :0;
1039
            monitor.worked(3);
1040
            count += includeRelatedTaxa ? dao.countSecundumForSubtreeRelations(subTreeIndex, newSec, config.isOverwriteExisting(), config.isIncludeSharedTaxa(), config.isEmptySecundumDetail()):0;
1041
            monitor.worked(2);
1042
            if (monitor.isCanceled()){
1043
                return result;
1044
            }
1045

    
1046
            SubProgressMonitor subMonitor = SubProgressMonitor.NewStarted(monitor, 90, "Updating secundum for subtree", count * 2);  //*2 1 tick for update and 1 tick for commit
1047
            //Reference ref = config.getNewSecundum();
1048
            if (config.isIncludeAcceptedTaxa()){
1049
                monitor.subTask("Update Accepted Taxa");
1050
                Set<CdmBase> updatedTaxa = dao.setSecundumForSubtreeAcceptedTaxa(subTreeIndex, newSec,
1051
                        config.isOverwriteExisting(), config.isIncludeSharedTaxa(), config.isEmptySecundumDetail(), subMonitor);
1052
                result.addUpdatedObjects(updatedTaxa);
1053
                if (monitor.isCanceled()){
1054
                    return result;
1055
                }
1056
            }
1057
            if (config.isIncludeSynonyms()){
1058
               monitor.subTask("Update Synonyms");
1059
               Set<CdmBase> updatedSynonyms = dao.setSecundumForSubtreeSynonyms(subTreeIndex, newSec,
1060
                       config.isOverwriteExisting(), config.isIncludeSharedTaxa() , config.isEmptySecundumDetail(), subMonitor);
1061
               result.addUpdatedObjects(updatedSynonyms);
1062
               if (monitor.isCanceled()){
1063
                   return result;
1064
               }
1065
            }
1066
            if (includeRelatedTaxa){
1067
                monitor.subTask("Update Related Taxa");
1068
                Set<UUID> relationTypes = getRelationTypesForSubtree(config);
1069
                Set<CdmBase> updatedRels = dao.setSecundumForSubtreeRelations(subTreeIndex, newSec,
1070
                        relationTypes, config.isOverwriteExisting(), config.isIncludeSharedTaxa() , config.isEmptySecundumDetail(), subMonitor);
1071
                result.addUpdatedObjects(updatedRels);
1072
                if (monitor.isCanceled()){
1073
                    return result;
1074
                }
1075
            }
1076
        } catch (Exception e) {
1077
            result.setError();
1078
            result.addException(e);
1079
        }
1080
        monitor.done();
1081
        return result;
1082
    }
1083

    
1084
    @Override
1085
    @Transactional(readOnly=false)
1086
    public UpdateResult setPublishForSubtree(PublishForSubtreeConfigurator config){
1087
        UpdateResult result = new UpdateResult();
1088
        IProgressMonitor monitor = config.getMonitor();
1089
        if (monitor == null){
1090
            monitor = DefaultProgressMonitor.NewInstance();
1091
        }
1092
        monitor.beginTask("Update publish flag for subtree", 100);
1093
        monitor.subTask("Check start conditions");
1094

    
1095
        if (config.getSubtreeUuid() == null){
1096
            result.setError();
1097
            result.addException(new NullPointerException("No subtree given"));
1098
            monitor.done();
1099
            return result;
1100
        }
1101
        monitor.worked(1);
1102

    
1103
        TaxonNode subTree = find(config.getSubtreeUuid());
1104
        if (subTree == null){
1105
            result.setError();
1106
            result.addException(new NullPointerException("Subtree does not exist"));
1107
            monitor.done();
1108
            return result;
1109
        }
1110
        monitor.worked(1);
1111

    
1112
        monitor.subTask("Count records");
1113
        boolean includeAcceptedTaxa = config.isIncludeAcceptedTaxa();
1114
        boolean publish = config.isPublish();
1115
        boolean includeSynonyms = config.isIncludeSynonyms();
1116
        boolean includeSharedTaxa = config.isIncludeSharedTaxa();
1117
        boolean includeHybrids = config.isIncludeHybrids();
1118
        boolean includeRelatedTaxa = config.isIncludeProParteSynonyms() || config.isIncludeMisapplications();
1119
        try {
1120
            TreeIndex subTreeIndex = TreeIndex.NewInstance(subTree.treeIndex());
1121
            int count = includeAcceptedTaxa ? dao.countPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa, includeHybrids):0;
1122
            monitor.worked(3);
1123
            count += includeSynonyms ? dao.countPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa, includeHybrids):0;
1124
            monitor.worked(3);
1125
            count += includeRelatedTaxa ? dao.countPublishForSubtreeRelatedTaxa(subTreeIndex, publish, includeSharedTaxa, includeHybrids):0;
1126
            monitor.worked(2);
1127
            if (monitor.isCanceled()){
1128
                return result;
1129
            }
1130

    
1131
            SubProgressMonitor subMonitor = SubProgressMonitor.NewStarted(monitor, 90, "Updating secundum for subtree", count);
1132
            if (includeAcceptedTaxa){
1133
                monitor.subTask("Update Accepted Taxa");
1134
                @SuppressWarnings("rawtypes")
1135
                Set<TaxonBase> updatedTaxa = dao.setPublishForSubtreeAcceptedTaxa(subTreeIndex,
1136
                        publish, includeSharedTaxa, includeHybrids, subMonitor);
1137
                result.addUpdatedObjects(updatedTaxa);
1138
                if (monitor.isCanceled()){
1139
                    return result;
1140
                }
1141
            }
1142
            if (includeSynonyms){
1143
                monitor.subTask("Update Synonyms");
1144
                @SuppressWarnings("rawtypes")
1145
                Set<TaxonBase> updatedSynonyms = dao.setPublishForSubtreeSynonyms(subTreeIndex,
1146
                        publish, includeSharedTaxa, includeHybrids, subMonitor);
1147
                result.addUpdatedObjects(updatedSynonyms);
1148
                if (monitor.isCanceled()){
1149
                    return result;
1150
                }
1151
            }
1152
            if (includeRelatedTaxa){
1153
                monitor.subTask("Update Related Taxa");
1154
                Set<UUID> relationTypes = getRelationTypesForSubtree(config);
1155
                if (config.isIncludeMisapplications()){
1156
                    relationTypes.addAll(TaxonRelationshipType.misappliedNameUuids());
1157
                }
1158
                if (config.isIncludeProParteSynonyms()){
1159
                    relationTypes.addAll(TaxonRelationshipType.proParteOrPartialSynonymUuids());
1160
                }
1161
                @SuppressWarnings("rawtypes")
1162
                Set<TaxonBase> updatedTaxa = dao.setPublishForSubtreeRelatedTaxa(subTreeIndex, publish,
1163
                        relationTypes, includeSharedTaxa, includeHybrids, subMonitor);
1164
                result.addUpdatedObjects(updatedTaxa);
1165
                if (monitor.isCanceled()){
1166
                    return result;
1167
                }
1168
            }
1169
        } catch (Exception e) {
1170
            result.setError();
1171
            result.addException(e);
1172
        }
1173

    
1174
        monitor.done();
1175
        return result;
1176
    }
1177

    
1178
    private Set<UUID> getRelationTypesForSubtree(ForSubtreeConfiguratorBase config) {
1179
        Set<UUID> relationTypes = new HashSet<>();
1180
        if (config.isIncludeMisapplications()){
1181
            relationTypes.addAll(TaxonRelationshipType.misappliedNameUuids());
1182
        }
1183
        if (config.isIncludeProParteSynonyms()){
1184
            relationTypes.addAll(TaxonRelationshipType.proParteOrPartialSynonymUuids());
1185
        }
1186
        return relationTypes;
1187
    }
1188

    
1189
    @Override
1190
    public long count(TaxonNodeFilter filter){
1191
        return nodeFilterDao.count(filter);
1192
    }
1193

    
1194
    @Override
1195
    public List<UUID> uuidList(TaxonNodeFilter filter){
1196
        return nodeFilterDao.listUuids(filter);
1197
    }
1198

    
1199
    @Override
1200
    public List<Integer> idList(TaxonNodeFilter filter){
1201
        return nodeFilterDao.idList(filter);
1202
    }
1203

    
1204
    @Override
1205
    public TaxonNodeDto findCommonParentDto(Collection<TaxonNodeDto> nodes) {
1206
        TaxonNodeDto commonParent = null;
1207
        List<String> treePath = null;
1208
        for (TaxonNodeDto nodeDto : nodes) {
1209
            if (nodeDto == null){
1210
                continue;
1211
            }
1212
            String nodeTreeIndex = nodeDto.getTreeIndex();
1213
            nodeTreeIndex = nodeTreeIndex.replaceFirst("#", "");
1214
            String[] split = nodeTreeIndex.split("#");
1215
            if(treePath == null){
1216
                treePath = Arrays.asList(split);
1217
            }
1218
            else{
1219
                List<String> match = new ArrayList<>();
1220
                for(int i=0;i<treePath.size();i++){
1221
                    if(i>=split.length){
1222
                        //current tree index is shorter so break
1223
                        break;
1224
                    }
1225
                    else if(split[i].equals(treePath.get(i))){
1226
                        //match found
1227
                        match.add(treePath.get(i));
1228
                    }
1229
                    else{
1230
                        //first mismatch found
1231
                        break;
1232
                    }
1233
                }
1234
                treePath = match;
1235
                if(treePath.isEmpty()){
1236
                    //no common parent found for at least two nodes
1237
                    //-> they belong to a different classification
1238
                    break;
1239
                }
1240
            }
1241
        }
1242
        if(treePath!=null && !treePath.isEmpty()) {
1243
            //get last index
1244
            int nodeId = Integer.parseInt(treePath.get(treePath.size()-1));
1245
            TaxonNode taxonNode = dao.load(nodeId, null);
1246
            commonParent = new TaxonNodeDto(taxonNode);
1247
        }
1248
        return commonParent;
1249
    }
1250

    
1251
    @Override
1252
    public List<TaxonDistributionDTO> getTaxonDistributionDTO(List<UUID> nodeUuids, List<String> propertyPaths,
1253
            Authentication authentication, boolean openChildren, TaxonNodeSortMode sortMode){
1254

    
1255
        nodeUuids = nodeUuids.stream().distinct().collect(Collectors.toList());
1256
        List<TaxonNode> nodes = new ArrayList<>();
1257

    
1258
        List<TaxonNode> parentNodes = load(nodeUuids, propertyPaths);
1259
        if (sortMode != null){
1260
            parentNodes.sort(sortMode.comparator());
1261
        }
1262
        if (openChildren){
1263
            //TODO we could remove nodes which are children of other nodes in parentNodes list here as they are duplicates
1264
            for (TaxonNode node: parentNodes){
1265
                if (node == null || nodes.contains(node)){
1266
                    continue;
1267
                }
1268
                nodes.add(node);
1269
                List<TaxonNode> children = new ArrayList<>();
1270
                children.addAll(loadChildNodesOfTaxonNode(node,
1271
                        propertyPaths, true,  true, sortMode));
1272
                for (TaxonNode child: children){
1273
                    if (!nodes.contains(child)){
1274
                        nodes.add(child);
1275
                    }
1276
                }
1277
            }
1278
        }else{
1279
            nodes.addAll(nodes);
1280
        }
1281

    
1282
        List<TaxonDistributionDTO> result = new ArrayList<>();
1283
        boolean hasPermission = false;
1284
        for(TaxonNode node: nodes){
1285
            if (authentication != null ) {
1286
                hasPermission = permissionEvaluator.hasPermission(authentication, node, Operation.UPDATE);
1287
            }else {
1288
                hasPermission = true;
1289
            }
1290
            if (node.getTaxon() != null && hasPermission){
1291
                try{
1292
                    TaxonDistributionDTO dto = new TaxonDistributionDTO(node);
1293
                    result.add(dto);
1294
                }catch(Exception e){
1295
                    logger.error(e.getMessage(), e);
1296
                }
1297
            }
1298
        }
1299
        return result;
1300
    }
1301

    
1302
    @Override
1303
    public <S extends TaxonNode> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
1304
            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
1305
        return page(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, INCLUDE_UNPUBLISHED);
1306
    }
1307

    
1308
    @Override
1309
    public <S extends TaxonNode> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
1310
            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths, boolean includeUnpublished) {
1311

    
1312
        List<S> records;
1313
        long resultSize = dao.count(clazz, restrictions);
1314
        if(AbstractPagerImpl.hasResultsInRange(resultSize, pageIndex, pageSize)){
1315
            records = dao.list(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, includeUnpublished);
1316
        } else {
1317
            records = new ArrayList<>();
1318
        }
1319
        Pager<S> pager = new DefaultPagerImpl<>(pageIndex, resultSize, pageSize, records);
1320
        return pager;
1321
    }
1322

    
1323
    @Override
1324
    public List<TaxonDistributionDTO> getTaxonDistributionDTO(List<UUID> nodeUuids,
1325
            List<String> propertyPaths, boolean openChildren) {
1326
        return getTaxonDistributionDTO(nodeUuids, propertyPaths, null, openChildren, null);
1327
    }
1328

    
1329
    @Override
1330
    @Transactional(readOnly = false)
1331
    public UpdateResult cloneSubtree(SubtreeCloneConfigurator config) {
1332
        UpdateResult result = new UpdateResult();
1333

    
1334
        if (config.getSubTreeUuids().isEmpty()){
1335
            return result;
1336
        }
1337

    
1338
        //TODO error handling
1339
        Reference taxonSecundum = config.isReuseTaxa() || config.isReuseTaxonSecundum() || config.getTaxonSecundumUuid() == null ?
1340
                null : referenceDao.findByUuid(config.getTaxonSecundumUuid());
1341
        config.setTaxonSecundum(taxonSecundum);
1342

    
1343
        Reference parentChildReference = config.isReuseParentChildReference() || config.getParentChildReferenceUuid() == null ?
1344
                null : referenceDao.findByUuid(config.getParentChildReferenceUuid());
1345
        config.setParentChildReference(parentChildReference);
1346

    
1347
        Reference taxonRelationshipReference = config.getRelationTypeToOldTaxon() == null ?
1348
                null : referenceDao.findByUuid(config.getRelationshipReferenceUuid());
1349
        config.setRelationshipReference(taxonRelationshipReference);
1350

    
1351
        Classification classificationClone = Classification.NewInstance(config.getNewClassificationName());
1352

    
1353
        if (config.isReuseClassificationReference()){
1354
            TaxonNode anyNode = dao.findByUuid(config.getSubTreeUuids().iterator().next());
1355
            if (anyNode != null){
1356
                Reference oldClassificationRef = anyNode.getClassification().getReference();
1357
                classificationClone.setReference(oldClassificationRef);
1358
            }
1359
        }else if (config.getClassificationReferenceUuid() != null) {
1360
            Reference classificationReference = referenceDao.findByUuid(config.getClassificationReferenceUuid());
1361
            classificationClone.setReference(classificationReference);
1362
        }
1363

    
1364
        //clone taxa and taxon nodes
1365
//      List<Integer> childNodeIds = taxonNodeService.idList(taxonNodeFilter);
1366
//      List<TaxonNode> childNodes = taxonNodeService.loadByIds(childNodeIds, null);
1367
        List<TaxonNode> rootNodes = this.find(config.getSubTreeUuids());
1368
        for (TaxonNode taxonNode : rootNodes) {
1369
            cloneTaxonRecursive(taxonNode, classificationClone.getRootNode(), config);
1370
        }
1371
        classificationDao.saveOrUpdate(classificationClone);
1372
        result.setCdmEntity(classificationClone);
1373
        return result;
1374
    }
1375

    
1376
    private void cloneTaxonRecursive(TaxonNode originalParentNode, TaxonNode parentNodeClone,
1377
            SubtreeCloneConfigurator config){
1378

    
1379
        Taxon originalTaxon = CdmBase.deproxy(originalParentNode.getTaxon());
1380
        TaxonNode childNodeClone;
1381
        if (originalTaxon != null){
1382
            String microReference = null;
1383
            if (config.isReuseTaxa()){
1384
                childNodeClone = parentNodeClone.addChildTaxon(originalTaxon, config.getParentChildReference(), microReference);
1385
            }else{
1386
                Taxon cloneTaxon = originalTaxon.clone(config.isIncludeSynonymsIncludingManAndProParte(),
1387
                        config.isIncludeTaxonRelationshipsExcludingManAndProParte(),
1388
                        config.isIncludeDescriptiveData(), config.isIncludeMedia());
1389

    
1390
                //name
1391
                if (!config.isReuseNames()){
1392
                    cloneTaxon.setName(cloneTaxon.getName().clone());
1393
                    //TODO needs further handling for name relationships etc., see #9349
1394
                    cloneTaxon.getSynonyms().forEach(syn ->
1395
                        syn.setName(syn.getName() == null ? null : syn.getName().clone()));
1396
                }
1397

    
1398
                if (!config.isReuseTaxonSecundum()){
1399
                    cloneTaxon.setSec(config.getTaxonSecundum());
1400
                }
1401

    
1402
                //add relation between taxa
1403
                if (config.getRelationTypeToOldTaxon() != null){
1404
                    TaxonRelationship rel = cloneTaxon.addTaxonRelation(originalParentNode.getTaxon(), config.getRelationTypeToOldTaxon(),
1405
                            config.getRelationshipReference(), microReference);
1406
                    rel.setDoubtful(config.isRelationDoubtful());
1407
                }
1408
                childNodeClone = parentNodeClone.addChildTaxon(cloneTaxon, config.getParentChildReference(), microReference);
1409
            }
1410

    
1411
            //probably necessary as taxon nodes do not cascade
1412
            dao.saveOrUpdate(childNodeClone);
1413

    
1414
        }else{
1415
            childNodeClone = parentNodeClone;
1416
        }
1417
        //add children
1418
        if (config.isDoRecursive()){
1419
            List<TaxonNode> originalChildNodes = originalParentNode.getChildNodes();
1420
            HHH_9751_Util.removeAllNull(originalChildNodes);
1421

    
1422
            for (TaxonNode originalChildNode : originalChildNodes) {
1423
                cloneTaxonRecursive(originalChildNode, childNodeClone, config);
1424
            }
1425
        }
1426
    }
1427

    
1428
    @Override
1429
    public HomotypicGroupDto getHomotypicGroupDto(UUID homotypicGroupUuid, UUID nodeUuid) {
1430

    
1431
        HomotypicalGroup group = homotypicalGroupDao.load(homotypicGroupUuid);
1432
        if (group == null){
1433
            return null;
1434
        }
1435
        return new HomotypicGroupDto(group, nodeUuid);
1436
    }
1437

    
1438
    @Override
1439
    public TaxonNodeDto getTaxonNodeDto(UUID nodeUuid) {
1440
        return dao.getTaxonNodeDto(nodeUuid);
1441
    }
1442

    
1443
    @Override
1444
    public List<TaxonNodeDto> getTaxonNodeDtos(List<UUID> nodeUuids) {
1445
        return dao.getTaxonNodeDtos(nodeUuids);
1446
    }
1447

    
1448

    
1449
}
(86-86/95)