Project

General

Profile

Download (54.4 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.Map;
21
import java.util.Map.Entry;
22
import java.util.Set;
23
import java.util.UUID;
24

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

    
32
import eu.etaxonomy.cdm.api.service.UpdateResult.Status;
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.filter.TaxonNodeFilter;
49
import eu.etaxonomy.cdm.hibernate.HHH_9751_Util;
50
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
51
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
52
import eu.etaxonomy.cdm.model.common.CdmBase;
53
import eu.etaxonomy.cdm.model.common.Language;
54
import eu.etaxonomy.cdm.model.common.LanguageString;
55
import eu.etaxonomy.cdm.model.common.TreeIndex;
56
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
57
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
58
import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
59
import eu.etaxonomy.cdm.model.description.TaxonDescription;
60
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
61
import eu.etaxonomy.cdm.model.name.HybridRelationship;
62
import eu.etaxonomy.cdm.model.name.Rank;
63
import eu.etaxonomy.cdm.model.name.TaxonName;
64
import eu.etaxonomy.cdm.model.permission.Operation;
65
import eu.etaxonomy.cdm.model.reference.Reference;
66
import eu.etaxonomy.cdm.model.taxon.Classification;
67
import eu.etaxonomy.cdm.model.taxon.HomotypicGroupTaxonComparator;
68
import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
69
import eu.etaxonomy.cdm.model.taxon.Synonym;
70
import eu.etaxonomy.cdm.model.taxon.SynonymType;
71
import eu.etaxonomy.cdm.model.taxon.Taxon;
72
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
73
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
74
import eu.etaxonomy.cdm.model.taxon.TaxonNodeAgentRelation;
75
import eu.etaxonomy.cdm.model.taxon.TaxonNodeStatus;
76
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
77
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
78
import eu.etaxonomy.cdm.model.term.DefinedTerm;
79
import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
80
import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
81
import eu.etaxonomy.cdm.persistence.dao.reference.IOriginalSourceDao;
82
import eu.etaxonomy.cdm.persistence.dao.reference.IReferenceDao;
83
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
84
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
85
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeFilterDao;
86
import eu.etaxonomy.cdm.persistence.dto.MergeResult;
87
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
88
import eu.etaxonomy.cdm.persistence.permission.ICdmPermissionEvaluator;
89
import eu.etaxonomy.cdm.persistence.query.OrderHint;
90

    
91
/**
92
 * @author n.hoffmann
93
 * @since Apr 9, 2010
94
 */
95
@Service
96
@Transactional(readOnly = true)
97
public class TaxonNodeServiceImpl
98
           extends AnnotatableServiceBase<TaxonNode, ITaxonNodeDao>
99
           implements ITaxonNodeService{
100

    
101
    private static final Logger logger = Logger.getLogger(TaxonNodeServiceImpl.class);
102

    
103
    @Autowired
104
    private IBeanInitializer defaultBeanInitializer;
105

    
106
    @Autowired
107
    private ITaxonService taxonService;
108

    
109
    @Autowired
110
    private IReferenceService referenceService;
111

    
112
    @Autowired
113
    private IDescriptiveDataSetService dataSetService;
114

    
115
    @Autowired
116
    private IAgentService agentService;
117

    
118
    @Autowired
119
    private INameService nameService;
120

    
121
    @Autowired
122
    private IOriginalSourceDao sourceDao;
123

    
124
    @Autowired
125
    private ITaxonNodeFilterDao nodeFilterDao;
126

    
127
    @Autowired
128
    private IReferenceDao referenceDao;
129

    
130
    @Autowired
131
    private IClassificationDao classificationDao;
132

    
133
    @Autowired
134
    IProgressMonitorService progressMonitorService;
135

    
136
    @Autowired
137
    private ICdmPermissionEvaluator permissionEvaluator;
138

    
139
    @Override
140
    public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
141
            List<String> propertyPaths, boolean recursive,  boolean includeUnpublished,
142
            TaxonNodeSortMode sortMode) {
143

    
144
        getSession().refresh(taxonNode);
145
        List<TaxonNode> childNodes;
146
        if (recursive == true){
147
        	childNodes  = dao.listChildrenOf(taxonNode, null, null, recursive, includeUnpublished, null);
148
        }else if (includeUnpublished){
149
            childNodes = new ArrayList<>(taxonNode.getChildNodes());
150
        }else{
151
            childNodes = new ArrayList<>();
152
            for (TaxonNode node:taxonNode.getChildNodes()){
153
                if (node.getTaxon().isPublish()){
154
                    childNodes.add(node);
155
                }
156
            }
157
        }
158

    
159
        HHH_9751_Util.removeAllNull(childNodes);
160

    
161
        if (sortMode != null){
162
            Comparator<TaxonNode> comparator = sortMode.newComparator();
163
        	Collections.sort(childNodes, comparator);
164
        }
165
        defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
166
        return childNodes;
167
    }
168

    
169
    @Override
170
    public List<TaxonNode> listChildrenOf(TaxonNode node, Integer pageSize, Integer pageIndex,
171
            boolean recursive, boolean includeUnpublished, List<String> propertyPaths){
172
        return dao.listChildrenOf(node, pageSize, pageIndex, recursive, includeUnpublished, propertyPaths);
173
    }
174

    
175
    @Override
176
    public TaxonNodeDto getParentUuidAndTitleCache(ITaxonTreeNode child) {
177
        UUID uuid = child.getUuid();
178
        int id = child.getId();
179
        TaxonNodeDto uuidAndTitleCache = new TaxonNodeDto(uuid, id, null);
180
        return getParentUuidAndTitleCache(uuidAndTitleCache);
181
    }
182

    
183
    @Override
184
    public TaxonNodeDto getParentUuidAndTitleCache(TaxonNodeDto child) {
185
        return dao.getParentUuidAndTitleCache(child);
186
    }
187

    
188
    @Override
189
    public List<TaxonNodeDto> listChildNodesAsTaxonNodeDto(TaxonNodeDto parent) {
190
        return dao.listChildNodesAsTaxonNodeDto(parent);
191
    }
192

    
193
    @Override
194
    public List<TaxonNodeDto> getUuidAndTitleCache(Integer limit, String pattern, UUID classificationUuid) {
195
        return dao.getUuidAndTitleCache(limit, pattern, classificationUuid, true);
196
    }
197

    
198
    @Override
199
    public List<TaxonNodeDto> listChildNodesAsTaxonNodeDto(ITaxonTreeNode parent) {
200
        List<String> propertyPaths = new ArrayList<>();
201
        propertyPaths.add("parent");
202
        parent = dao.load(parent.getId(), propertyPaths);
203
        TaxonNodeDto uuidAndTitleCache = new TaxonNodeDto(parent);
204
        return listChildNodesAsTaxonNodeDto(uuidAndTitleCache);
205
    }
206

    
207
    @Override
208
    public List<TaxonNodeDto> taxonNodeDtoParentRank(Classification classification, Rank rank, TaxonName name) {
209
    	return dao.getParentTaxonNodeDtoForRank(classification, rank, name);
210
    }
211

    
212
    @Override
213
    public List<TaxonNodeDto> taxonNodeDtoParentRank(Classification classification, Rank rank, TaxonBase<?> taxonBase) {
214
        return dao.getParentTaxonNodeDtoForRank(classification, rank, taxonBase);
215
    }
216

    
217
    @Override
218
    public Pager<TaxonNodeDto> pageChildNodesDTOs(UUID taxonNodeUuid, boolean recursive,  boolean includeUnpublished,
219
            boolean doSynonyms, TaxonNodeSortMode sortMode,
220
            Integer pageSize, Integer pageIndex) {
221

    
222
        TaxonNode parentNode = dao.load(taxonNodeUuid);
223

    
224
        List<CdmBase> allRecords = new ArrayList<>();
225

    
226
        //acceptedTaxa
227
        List<TaxonNode> childNodes = loadChildNodesOfTaxonNode(parentNode, null, recursive, includeUnpublished, sortMode);
228
        allRecords.addAll(childNodes);
229

    
230
        //add synonyms if pager is not yet full synonyms
231
        if (doSynonyms){
232
            List<Synonym> synList = new ArrayList<>(parentNode.getTaxon().getSynonyms());
233
            Collections.sort(synList, new HomotypicGroupTaxonComparator(null));
234
            //TODO: test sorting
235

    
236
            allRecords.addAll(synList);
237
        }
238

    
239
        List<TaxonNodeDto> dtos = new ArrayList<>(pageSize==null?25:pageSize);
240
        long totalCount = Long.valueOf(allRecords.size());
241

    
242
        TaxonName parentName = null;
243

    
244
        for(CdmBase item : PagerUtils.pageList(allRecords, pageIndex, pageSize)) {
245
            if (item.isInstanceOf(TaxonNode.class)){
246
                dtos.add(new TaxonNodeDto(CdmBase.deproxy(item, TaxonNode.class)));
247
            }else if (item.isInstanceOf(Synonym.class)){
248
                Synonym synonym = CdmBase.deproxy(item, Synonym.class);
249
                parentName = parentName == null? parentNode.getTaxon().getName(): parentName;
250
                boolean isHomotypic = synonym.getName().isHomotypic(parentName);
251
                dtos.add(new TaxonNodeDto(synonym, isHomotypic));
252
            }
253
        }
254
        return new DefaultPagerImpl<>(pageIndex, totalCount, pageSize , dtos);
255
    }
256

    
257
    @Override
258
    public TaxonNodeDto parentDto(UUID taxonNodeUuid) {
259
        if (taxonNodeUuid == null){
260
            return null;
261
        }
262
        TaxonNode taxonNode = dao.load(taxonNodeUuid);
263
        if(taxonNode.getParent() != null) {
264
            return new TaxonNodeDto(taxonNode.getParent());
265
        }
266
        return null;
267
    }
268

    
269
    @Override
270
    public TaxonNodeDto dto(UUID taxonNodeUuid) {
271
        if (taxonNodeUuid == null){
272
            return null;
273
        }
274
        TaxonNode taxonNode = dao.load(taxonNodeUuid);
275
        if (taxonNode != null){
276
            return new TaxonNodeDto(taxonNode);
277
        }
278
        return null;
279
    }
280

    
281
    @Override
282
    @Autowired
283
    protected void setDao(ITaxonNodeDao dao) {
284
        this.dao = dao;
285
    }
286

    
287
    @Override
288
    @Transactional(readOnly = false)
289
    public DeleteResult makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode, TaxonNode newAcceptedTaxonNode,
290
            SynonymType synonymType, Reference citation, String citationMicroReference, boolean setNameInSource)  {
291

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

    
298
        if(oldTaxonNode.equals(newAcceptedTaxonNode)){
299
            throw new IllegalArgumentException("Taxon can not be made synonym of its own.");
300
        }
301

    
302
        Classification classification = oldTaxonNode.getClassification();
303
        Taxon oldTaxon = HibernateProxyHelper.deproxy(oldTaxonNode.getTaxon());
304
        Taxon newAcceptedTaxon = (Taxon)this.taxonService.find(newAcceptedTaxonNode.getTaxon().getUuid());
305
        newAcceptedTaxon = HibernateProxyHelper.deproxy(newAcceptedTaxon);
306
        // Move oldTaxon to newTaxon
307
        //TaxonName synonymName = oldTaxon.getName();
308
        TaxonName newSynonymName = CdmBase.deproxy(oldTaxon.getName());
309
        HomotypicalGroup group = CdmBase.deproxy(newSynonymName.getHomotypicalGroup());
310
        if (synonymType == null){
311
            if (newSynonymName.isHomotypic(newAcceptedTaxon.getName())){
312
                synonymType = SynonymType.HOMOTYPIC_SYNONYM_OF();
313
            }else{
314
                synonymType = SynonymType.HETEROTYPIC_SYNONYM_OF();
315
            }
316
        }
317

    
318
        //set homotypic group
319
        TaxonName newAcceptedTaxonName = HibernateProxyHelper.deproxy(newAcceptedTaxon.getName(), TaxonName.class);
320
        newAcceptedTaxon.setName(newAcceptedTaxonName);
321
        // Move Synonym Relations to new Taxon
322
        newAcceptedTaxon.addSynonymName(newSynonymName, citation, citationMicroReference, synonymType);
323
         // Move Synonyms to new Taxon
324
        // From ticket 3163 we can move taxon with accepted name having homotypic synonyms
325
        List<Synonym> synonymsInHomotypicalGroup = null;
326

    
327
        //the synonyms of the homotypical group of the old taxon
328
        if (synonymType.equals(SynonymType.HOMOTYPIC_SYNONYM_OF())){
329
        	synonymsInHomotypicalGroup = oldTaxon.getSynonymsInGroup(group);
330
        }
331

    
332
        Set<Synonym> syns = new HashSet<>(oldTaxon.getSynonyms());
333
        for(Synonym synonym : syns){
334
            SynonymType srt;
335
            if(synonym.getHomotypicGroup()!= null
336
                    && synonym.getHomotypicGroup().equals(newAcceptedTaxonName.getHomotypicalGroup())) {
337
                srt = SynonymType.HOMOTYPIC_SYNONYM_OF();
338
            } else if(synonym.getType() != null && synonym.getType().equals(SynonymType.HOMOTYPIC_SYNONYM_OF())) {
339
            	if (synonymType.equals(SynonymType.HOMOTYPIC_SYNONYM_OF())){
340
            		srt = SynonymType.HOMOTYPIC_SYNONYM_OF();
341
            	} else{
342
            		srt = SynonymType.HETEROTYPIC_SYNONYM_OF();
343
            	}
344
            } else {
345
                if (synonymsInHomotypicalGroup != null && synonymsInHomotypicalGroup.contains(synonym)){
346
                    srt = SynonymType.HOMOTYPIC_SYNONYM_OF();
347
                }else{
348
                    srt = synonym.getType();
349
                }
350

    
351
            }
352

    
353
            newAcceptedTaxon.addSynonym(synonym, srt);
354

    
355

    
356
            /*if (synonymsInHomotypicalGroup.contains(synRelation.getSynonym()) && srt.equals(SynonymType.HETEROTYPIC_SYNONYM_OF())){
357
            	homotypicalGroupAcceptedTaxon.addTypifiedName(synRelation.getSynonym().getName());
358
            }*/
359

    
360
        }
361

    
362

    
363
        // CHILD NODES
364
        if(oldTaxonNode.getChildNodes() != null && oldTaxonNode.getChildNodes().size() != 0){
365
        	List<TaxonNode> childNodes = new ArrayList<>();
366
        	for (TaxonNode childNode : oldTaxonNode.getChildNodes()){
367
        		childNodes.add(childNode);
368
        	}
369
            for(TaxonNode childNode :childNodes){
370
                newAcceptedTaxonNode.addChildNode(childNode, childNode.getReference(), childNode.getMicroReference()); // childNode.getSynonymToBeUsed()
371
            }
372
        }
373

    
374
        //Move Taxon RelationShips to new Taxon
375
        for(TaxonRelationship taxonRelationship : oldTaxon.getTaxonRelations()){
376
            Taxon fromTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getFromTaxon());
377
            Taxon toTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getToTaxon());
378
            if (fromTaxon == oldTaxon){
379
                newAcceptedTaxon.addTaxonRelation(taxonRelationship.getToTaxon(), taxonRelationship.getType(),
380
                        taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
381

    
382
            }else if(toTaxon == oldTaxon){
383
               fromTaxon.addTaxonRelation(newAcceptedTaxon, taxonRelationship.getType(),
384
                        taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
385
               taxonService.saveOrUpdate(fromTaxon);
386

    
387
            }else{
388
                logger.warn("Taxon is not part of its own Taxonrelationship");
389
            }
390
            // Remove old relationships
391

    
392
            fromTaxon.removeTaxonRelation(taxonRelationship);
393
            toTaxon.removeTaxonRelation(taxonRelationship);
394
            taxonRelationship.setToTaxon(null);
395
            taxonRelationship.setFromTaxon(null);
396
        }
397

    
398
        //Move descriptions to new taxon
399
        List<TaxonDescription> descriptions = new ArrayList<TaxonDescription>( oldTaxon.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())
400
        for(TaxonDescription description : descriptions){
401
            String message = "Description copied from former accepted taxon: %s (Old title: %s)";
402
            message = String.format(message, oldTaxon.getTitleCache(), description.getTitleCache());
403
            description.setTitleCache(message, true);
404
            if (setNameInSource) {
405
                for (DescriptionElementBase element: description.getElements()){
406
                    for (DescriptionElementSource source: element.getSources()){
407
                        if (source.getNameUsedInSource() == null){
408
                            source.setNameUsedInSource(newSynonymName);
409
                        }
410
                    }
411
                }
412
            }
413
            //oldTaxon.removeDescription(description, false);
414
            newAcceptedTaxon.addDescription(description);
415
        }
416
        oldTaxon.clearDescriptions();
417

    
418
        taxonService.saveOrUpdate(newAcceptedTaxon);
419

    
420
        taxonService.saveOrUpdate(oldTaxon);
421
        taxonService.getSession().flush();
422

    
423
        TaxonDeletionConfigurator conf = new TaxonDeletionConfigurator();
424
        conf.setDeleteSynonymsIfPossible(false);
425
        conf.setDeleteNameIfPossible(false);
426
        DeleteResult result = taxonService.isDeletable(oldTaxon.getUuid(), conf);
427

    
428

    
429
        if (result.isOk()){
430
        	 result = taxonService.deleteTaxon(oldTaxon.getUuid(), conf, classification.getUuid());
431

    
432
        }else{
433
        	result.setStatus(Status.OK);
434
        	TaxonNodeDeletionConfigurator config = new TaxonNodeDeletionConfigurator();
435
        	config.setDeleteElement(false);
436
        	conf.setTaxonNodeConfig(config);
437
        	result.includeResult(deleteTaxonNode(oldTaxonNode, conf));
438
        }
439

    
440
        result.addUpdatedObject(newAcceptedTaxon);
441

    
442

    
443
        //oldTaxonNode.delete();
444
        return result;
445
    }
446
    @Override
447
    @Transactional(readOnly = false)
448
    public UpdateResult makeTaxonNodeSynonymsOfAnotherTaxonNode( Set<UUID> oldTaxonNodeUuids,
449
            UUID newAcceptedTaxonNodeUUIDs,
450
            SynonymType synonymType,
451
            Reference citation,
452
            String citationMicroReference,
453
            boolean setNameInSource) {
454
    	UpdateResult result = new UpdateResult();
455
    	for (UUID nodeUuid: oldTaxonNodeUuids) {
456
    		result.includeResult(makeTaxonNodeASynonymOfAnotherTaxonNode(nodeUuid, newAcceptedTaxonNodeUUIDs, synonymType, citation, citationMicroReference, setNameInSource));
457
    	}
458
    	return result;
459
    }
460

    
461
    @Override
462
    @Transactional(readOnly = false)
463
    public UpdateResult makeTaxonNodeASynonymOfAnotherTaxonNode(UUID oldTaxonNodeUuid,
464
            UUID newAcceptedTaxonNodeUUID,
465
            SynonymType synonymType,
466
            Reference citation,
467
            String citationMicroReference,
468
            boolean setNameInSource) {
469

    
470
        TaxonNode oldTaxonNode = dao.load(oldTaxonNodeUuid);
471
        TaxonNode oldTaxonParentNode = oldTaxonNode.getParent();
472
        TaxonNode newTaxonNode = dao.load(newAcceptedTaxonNodeUUID);
473

    
474
        UpdateResult result = makeTaxonNodeASynonymOfAnotherTaxonNode(oldTaxonNode,
475
                newTaxonNode,
476
                synonymType,
477
                citation,
478
                citationMicroReference, setNameInSource);
479
        result.addUpdatedCdmId(new CdmEntityIdentifier(oldTaxonParentNode.getId(), TaxonNode.class));
480
        result.addUpdatedCdmId(new CdmEntityIdentifier(newTaxonNode.getId(), TaxonNode.class));
481
        result.setCdmEntity(oldTaxonParentNode);
482
        return result;
483
    }
484

    
485
    @Override
486
    @Transactional(readOnly = false)
487
    public DeleteResult deleteTaxonNodes(List<TaxonNode> list, TaxonDeletionConfigurator config) {
488

    
489
        if (config == null){
490
        	config = new TaxonDeletionConfigurator();
491
        }
492
        DeleteResult result = new DeleteResult();
493
        Classification classification = null;
494
        List<TaxonNode> taxonNodes = new ArrayList<>(list);
495

    
496
        for (TaxonNode treeNode:taxonNodes){
497
        	if (treeNode != null){
498

    
499
        		TaxonNode taxonNode;
500
	            taxonNode = CdmBase.deproxy(treeNode);
501
	            TaxonNode parent = taxonNode.getParent();
502
	            	//check whether the node has children or the children are already deleted
503
	            if(taxonNode.hasChildNodes()) {
504
            		List<TaxonNode> children = new ArrayList<> ();
505
            		List<TaxonNode> childNodesList = taxonNode.getChildNodes();
506
        			children.addAll(childNodesList);
507
        			//To avoid NPE when child is also in list of taxonNodes, remove it from the list
508
        			Iterator<TaxonNode> it = taxonNodes.iterator();
509
        			for (TaxonNode child: children) {
510
        				while (it.hasNext()) {
511
        					if (it.next().equals(child)) {
512
        						it.remove();
513
        					}
514
        				}
515
        			}
516
        			int compare = config.getTaxonNodeConfig().getChildHandling().compareTo(ChildHandling.DELETE);
517
        			boolean childHandling = (compare == 0)? true: false;
518
            		if (childHandling){
519
            			boolean changeDeleteTaxon = false;
520
            			if (!config.getTaxonNodeConfig().isDeleteTaxon()){
521
            				config.getTaxonNodeConfig().setDeleteTaxon(true);
522
            				changeDeleteTaxon = true;
523
            			}
524
            			DeleteResult resultNodes = deleteTaxonNodes(children, config);
525
            			if (!resultNodes.isOk()){
526
                            result.addExceptions(resultNodes.getExceptions());
527
                            result.setStatus(resultNodes.getStatus());
528
                        }
529
            			if (changeDeleteTaxon){
530
            				config.getTaxonNodeConfig().setDeleteTaxon(false);
531
            			}
532

    
533
            		} else {
534
            			//move the children to the parent
535

    
536
            			for (TaxonNode child: childNodesList){
537
            				parent.addChildNode(child, child.getReference(), child.getMicroReference());
538
            			}
539

    
540
            		}
541
            	}
542

    
543
	            classification = taxonNode.getClassification();
544

    
545
	            if (classification.getRootNode().equals(taxonNode)){
546
	            	classification.removeRootNode();
547
	            	classification = null;
548
	            }else if (classification.getChildNodes().contains(taxonNode)){
549
            		Taxon taxon = taxonNode.getTaxon();
550
            		classification.deleteChildNode(taxonNode);
551

    
552
	            	//node is rootNode
553
	            	if (taxon != null){
554

    
555
	            		if (config.getTaxonNodeConfig().isDeleteTaxon()){
556
	            		    taxonService.saveOrUpdate(taxon);
557
	            		    saveOrUpdate(taxonNode);
558

    
559
			            	TaxonDeletionConfigurator configNew = new TaxonDeletionConfigurator();
560
			            	configNew.setClassificationUuid(classification.getUuid());
561
			            	DeleteResult resultTaxon = taxonService.deleteTaxon(taxon.getUuid(), configNew, classification.getUuid());
562
			            	if (!resultTaxon.isOk()){
563
                                result.addExceptions(resultTaxon.getExceptions());
564
                                result.setStatus(resultTaxon.getStatus());
565
                            }
566

    
567
		            	}
568
	            	}
569
            		classification = null;
570

    
571
	            } else {
572
	            	//classification = null;
573
	            	Taxon taxon = taxonNode.getTaxon();
574
	            	taxon = CdmBase.deproxy(taxon);
575
	            	if (taxon != null){
576
	            		taxon.removeTaxonNode(taxonNode);
577
	            		if (config.getTaxonNodeConfig().isDeleteTaxon()){
578
			            	TaxonDeletionConfigurator configNew = new TaxonDeletionConfigurator();
579
			            	saveOrUpdate(taxonNode);
580
			            	taxonService.saveOrUpdate(taxon);
581
			            	DeleteResult resultTaxon = taxonService.deleteTaxon(taxon.getUuid(), configNew, classification.getUuid());
582

    
583
                            if (!resultTaxon.isOk()){
584
                                result.addExceptions(resultTaxon.getExceptions());
585
                                result.setStatus(resultTaxon.getStatus());
586
                            }
587
		            	}
588
	            	}
589

    
590
	            }
591

    
592
	            result.addUpdatedObject(parent);
593
	            if(result.getCdmEntity() == null){
594
	                result.setCdmEntity(taxonNode);
595
                }
596
	            UUID uuid = dao.delete(taxonNode);
597
	            logger.debug("Deleted node " +uuid.toString());
598

    
599
	        }
600
        }
601
        /*if (classification != null){
602
            result.addUpdatedObject(classification);
603
        	DeleteResult resultClassification = classService.delete(classification);
604
        	 if (!resultClassification.isOk()){
605
                 result.addExceptions(resultClassification.getExceptions());
606
                 result.setStatus(resultClassification.getStatus());
607
             }
608
        }*/
609
        return result;
610
    }
611

    
612

    
613
    @Override
614
    @Transactional(readOnly = false)
615
    public DeleteResult deleteTaxonNodes(Collection<UUID> nodeUuids, TaxonDeletionConfigurator config) {
616
        List<TaxonNode> nodes = new ArrayList<>();
617
        for(UUID nodeUuid : nodeUuids) {
618
            nodes.add(dao.load(nodeUuid));
619
        }
620
        return deleteTaxonNodes(nodes, config);
621
    }
622

    
623

    
624
    @Override
625
    @Transactional(readOnly = false)
626
    public DeleteResult deleteTaxonNode(UUID nodeUUID, TaxonDeletionConfigurator config) {
627

    
628
    	TaxonNode node = CdmBase.deproxy(dao.load(nodeUUID));
629
    	return deleteTaxonNode(node, config);
630
    }
631

    
632
    @Override
633
    @Transactional(readOnly = false)
634
    public DeleteResult deleteTaxonNode(TaxonNode node, TaxonDeletionConfigurator config) {
635
        DeleteResult result = new DeleteResult();
636
        if (node == null){
637
            result.setAbort();
638
            result.addException(new Exception("The TaxonNode was already deleted."));
639
            return result;
640
        }
641
        Taxon taxon = null;
642
        try{
643
            taxon = HibernateProxyHelper.deproxy(node.getTaxon());
644
        }catch(NullPointerException e){
645
            result.setAbort();
646
            result.addException(new Exception("The Taxon was already deleted."));
647

    
648
        }
649

    
650

    
651
    	TaxonNode parent = HibernateProxyHelper.deproxy(node.getParent(), TaxonNode.class);
652
    	if (config == null){
653
    		config = new TaxonDeletionConfigurator();
654
    	}
655

    
656

    
657
    	if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.MOVE_TO_PARENT)){
658
    	   Object[] children = node.getChildNodes().toArray();
659
    	   TaxonNode childNode;
660
    	   for (Object child: children){
661
    	       childNode = (TaxonNode) child;
662
    	       parent.addChildNode(childNode, childNode.getReference(), childNode.getMicroReference());
663

    
664
    	   }
665
    	}else{
666
    	    result.includeResult(deleteTaxonNodes(node.getChildNodes(), config));
667
    	}
668

    
669
    	//remove node from DescriptiveDataSet
670
        commonService.getReferencingObjects(node).stream()
671
        .filter(obj->obj instanceof DescriptiveDataSet)
672
        .forEach(dataSet->{
673
            ((DescriptiveDataSet)dataSet).removeTaxonSubtree(node);
674
            dataSetService.saveOrUpdate((DescriptiveDataSet) dataSet);
675
        });
676

    
677
    	if (taxon != null){
678
        	if (config.getTaxonNodeConfig().isDeleteTaxon() && (config.isDeleteInAllClassifications() || taxon.getTaxonNodes().size() == 1)){
679
        		result = taxonService.deleteTaxon(taxon.getUuid(), config, node.getClassification().getUuid());
680
        		result.addUpdatedObject(parent);
681
        		if (result.isOk()){
682
        			return result;
683
        		}
684
        	} else {
685
        	    result.addUpdatedObject(taxon);
686
        	}
687
    	}
688
    	result.setCdmEntity(node);
689
    	boolean success = true;
690
    	if (taxon != null){
691
    	    success = taxon.removeTaxonNode(node);
692
    	    taxonService.saveOrUpdate(taxon);
693
    	}
694
    	dao.saveOrUpdate(parent);
695

    
696
    	result.addUpdatedObject(parent);
697

    
698
    	if (success){
699
			result.setStatus(Status.OK);
700
			if (parent != null){
701
    			parent = HibernateProxyHelper.deproxy(parent, TaxonNode.class);
702
    			int index = parent.getChildNodes().indexOf(node);
703
    			if (index > -1){
704
    			    parent.removeChild(index);
705
    			}
706
			}
707
    		if (!dao.delete(node, config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)).equals(null)){
708
    		    result.getUpdatedObjects().remove(node);
709
    			result.addDeletedObject(node);
710
    			return result;
711
    		} else {
712
    			result.setError();
713
    			return result;
714
    		}
715
    	}else{
716
    	    if (dao.findByUuid(node.getUuid()) != null){
717
        		result.setError();
718
        		result.addException(new Exception("The node can not be removed from the taxon."));
719
    		}
720
    		return result;
721
    	}
722
    }
723

    
724

    
725
    @Override
726
    public List<TaxonNode> listAllNodesForClassification(Classification classification, Integer start, Integer end) {
727
        return dao.getTaxonOfAcceptedTaxaByClassification(classification, start, end);
728
    }
729

    
730
    @Override
731
    public int countAllNodesForClassification(Classification classification) {
732
        return dao.countTaxonOfAcceptedTaxaByClassification(classification);
733
    }
734

    
735
    @Override
736
    @Transactional
737
    public UpdateResult moveTaxonNode(UUID taxonNodeUuid, UUID targetNodeUuid, int movingType){
738
        TaxonNode taxonNode = HibernateProxyHelper.deproxy(dao.load(taxonNodeUuid));
739
    	TaxonNode targetNode = HibernateProxyHelper.deproxy(dao.load(targetNodeUuid));
740
    	UpdateResult result = moveTaxonNode(taxonNode, targetNode, movingType);
741
    	return result;
742
    }
743

    
744
    @Override
745
    @Transactional
746
    public UpdateResult moveTaxonNode(TaxonNode taxonNode, TaxonNode newParent, int movingType){
747
        UpdateResult result = new UpdateResult();
748

    
749
        TaxonNode parentParent = HibernateProxyHelper.deproxy(newParent.getParent());
750
        Integer sortIndex = -1;
751
        if (movingType == 0){
752
            sortIndex = 0;
753
        }else if (movingType == 1){
754
            sortIndex = newParent.getSortIndex();
755
            newParent = parentParent;
756
        } else if (movingType == 2){
757
            sortIndex = newParent.getSortIndex() +1;
758
            newParent = parentParent;
759
        } else{
760
            result.setAbort();
761
            result.addException(new Exception("The moving type "+ movingType +" is not supported."));
762
        }
763

    
764
        taxonNode = newParent.addChildNode(taxonNode, sortIndex, taxonNode.getReference(),  taxonNode.getMicroReference());
765
        result.addUpdatedObject(taxonNode);
766

    
767
        return result;
768
    }
769

    
770
    @Override
771
    @Transactional
772
    public UpdateResult moveTaxonNodes(Set<UUID> taxonNodeUuids, UUID newParentNodeUuid, int movingType, IProgressMonitor monitor){
773

    
774
        if (monitor == null){
775
            monitor = DefaultProgressMonitor.NewInstance();
776
        }
777
        UpdateResult result = new UpdateResult();
778

    
779
        TaxonNode targetNode = dao.load(newParentNodeUuid);
780
        List<TaxonNode> nodes = dao.list(taxonNodeUuids, null, null, null, null);
781

    
782
        monitor.beginTask("Move Taxonnodes", nodes.size()*2);
783
        monitor.subTask("move taxon nodes");
784
        for (TaxonNode node: nodes){
785
            if (!monitor.isCanceled()){
786
                if (!nodes.contains(node.getParent())){
787
                    result.includeResult(moveTaxonNode(node,targetNode, movingType));
788
                }
789
                monitor.worked(1);
790
            }else{
791
                monitor.done();
792
                result.setAbort();
793
                break;
794
            }
795
        }
796
        if (!monitor.isCanceled()){
797
            monitor.subTask("saving and reindex");
798
            dao.saveOrUpdateAll(nodes);
799
        }else{
800
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
801
        }
802

    
803
        monitor.done();
804
        return result;
805
    }
806

    
807
    @Override
808
    public Pager<TaxonNodeAgentRelation> pageTaxonNodeAgentRelations(UUID taxonUuid, UUID classificationUuid,
809
            UUID agentUuid, UUID rankUuid, UUID relTypeUuid, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
810

    
811

    
812
        List<TaxonNodeAgentRelation> records = null;
813

    
814
        long count = dao.countTaxonNodeAgentRelations(taxonUuid, classificationUuid, agentUuid, rankUuid, relTypeUuid);
815
        if(PagerUtils.hasResultsInRange(count, pageIndex, pageSize)) {
816
            records = dao.listTaxonNodeAgentRelations(taxonUuid, classificationUuid,
817
                    agentUuid, rankUuid, relTypeUuid, PagerUtils.startFor(pageSize, pageIndex), PagerUtils.limitFor(pageSize), propertyPaths);
818
        }
819

    
820
        Pager<TaxonNodeAgentRelation> pager = new DefaultPagerImpl<>(pageIndex, count, pageSize, records);
821
        return pager;
822
    }
823

    
824
    @Override
825
    @Transactional
826
    public UpdateResult createNewTaxonNode(UUID parentNodeUuid, CreateTaxonDTO taxonDto,
827
            DescriptionElementSource source, String microref,
828
            TaxonNodeStatus status, Map<Language,LanguageString> statusNote){
829

    
830
        UpdateResult result = new UpdateResult();
831
        TaxonName name = null;
832
        if (taxonDto.getNameUuid() != null){
833
            name = nameService.load(taxonDto.getNameUuid());
834

    
835
        }else{
836
            UpdateResult tmpResult = nameService.parseName(taxonDto.getTaxonNameString(),
837
                    taxonDto.getCode(), taxonDto.getPreferredRank(),  true);
838
            result.addUpdatedObjects(tmpResult.getUpdatedObjects());
839
            name = (TaxonName)tmpResult.getCdmEntity();
840
        }
841
        Reference sec = null;
842
        if (taxonDto.getSecUuid() != null ){
843
            sec = referenceService.load(taxonDto.getSecUuid());
844
        }
845
        if (!name.isPersited()){
846
            for (HybridRelationship rel : name.getHybridChildRelations()){
847
                if (!rel.getHybridName().isPersited()) {
848
                    nameService.save(rel.getHybridName());
849
                }
850
                if (!rel.getParentName().isPersited()) {
851
                    nameService.save(rel.getParentName());
852
                }
853
            }
854
        }
855

    
856
       Taxon newTaxon = Taxon.NewInstance(name, sec);
857
       newTaxon.setPublish(taxonDto.isPublish());
858
//       UUID taxonUuid = taxonService.saveOrUpdate(newTaxon);
859
//       newTaxon = (Taxon) taxonService.load(taxonUuid);
860

    
861
       TaxonNode parent = dao.load(parentNodeUuid);
862
       TaxonNode child = null;
863
       Reference ref = null;
864
       if (source != null){
865
           if (source.isPersited()){
866
               source = (DescriptionElementSource) sourceDao.load(source.getUuid());
867
           }
868
           if (source.getCitation() != null){
869
               source.setCitation(referenceService.load(source.getCitation().getUuid()));
870
           }
871
           if (source.getNameUsedInSource() !=null){
872
               source.setNameUsedInSource(nameService.load(source.getNameUsedInSource().getUuid()));
873
           }
874
       }
875

    
876
       try{
877
           child = parent.addChildTaxon(newTaxon, source);
878
           child.setStatus(status);
879

    
880
           if (statusNote != null){
881
               child.getStatusNote().putAll(statusNote);
882
           }
883

    
884
        }catch(Exception e){
885
            result.addException(e);
886
            result.setError();
887
            return result;
888
        }
889
        child = dao.save(child);
890

    
891
        result.addUpdatedObject(parent);
892
        if (child != null){
893
            result.setCdmEntity(child);
894
        }
895
        return result;
896
    }
897

    
898
    @Override
899
    @Transactional
900
    public UpdateResult saveNewTaxonNode(TaxonNode newTaxonNode){
901
        UpdateResult result = new UpdateResult();
902
        UUID parentUuid = newTaxonNode.getParent().getUuid();
903
        Taxon taxon = null;
904

    
905
        if (newTaxonNode.getTaxon().isPersited()){
906
            taxon = (Taxon)taxonService.load(newTaxonNode.getTaxon().getUuid());
907
        }else if (newTaxonNode.getTaxon().getName().isPersited()){
908
            TaxonName name = nameService.load(newTaxonNode.getTaxon().getName().getUuid());
909
            taxon = newTaxonNode.getTaxon();
910
            taxon.setName(name);
911
        }else{
912
            //taxon and name is not persisted, persist possible related names
913
            for (HybridRelationship rel : newTaxonNode.getTaxon().getName().getHybridChildRelations()){
914
                if (!rel.getHybridName().isPersited()) {
915
                    nameService.save(rel.getHybridName());
916
                }
917
                if (!rel.getParentName().isPersited()) {
918
                    nameService.save(rel.getParentName());
919
                }
920
            }
921
        }
922
        if (taxon == null){
923
            taxon = newTaxonNode.getTaxon();
924
        }
925
        taxon.removeTaxonNode(newTaxonNode);
926

    
927
        if (taxon.getSec() != null && taxon.getSec().isPersited()){
928
            Reference sec = referenceService.load(taxon.getSec().getUuid());
929
            taxon.setSec(sec);
930
        }
931
        if (!taxon.isPersited()){
932
            MergeResult<TaxonBase> mergeResult = taxonService.merge(taxon, true);
933
            taxon = (Taxon) mergeResult.getMergedEntity();
934

    
935
        }
936

    
937
        TaxonNode parent = dao.load(parentUuid);
938
        TaxonNode child = null;
939
        try{
940
            child = parent.addChildTaxon(taxon, newTaxonNode.getReference(), newTaxonNode.getMicroReference());
941
        }catch(Exception e){
942
            result.addException(e);
943
            result.setError();
944
            return result;
945
        }
946

    
947
        //TODO can't we work with clone method here?
948
        child.setStatus(newTaxonNode.getStatus());
949
        for (TaxonNodeAgentRelation agentRel :newTaxonNode.getAgentRelations()){
950
            child.addAgentRelation(agentRel.getType(), agentRel.getAgent());
951
        }
952
        for (Entry<Language, LanguageString> entry: newTaxonNode.getStatusNote().entrySet()){
953
            child.putStatusNote(entry.getKey(), entry.getValue().getText());
954
        }
955

    
956
        newTaxonNode = null;
957
        MergeResult<TaxonNode> mergeNode = dao.merge(child,true);
958
        child = mergeNode.getMergedEntity();
959
        result.addUpdatedObject(child.getParent());
960
        result.setCdmEntity(child);
961
        return result;
962
    }
963

    
964
    @Override
965
    @Transactional
966
    public UpdateResult createNewTaxonNode(UUID parentNodeUuid, UUID taxonUuid, UUID refUuid, String microref){
967
        UpdateResult result = new UpdateResult();
968
        TaxonNode parent = dao.load(parentNodeUuid);
969
        Taxon taxon = (Taxon) taxonService.load(taxonUuid);
970
        TaxonNode child = null;
971
        try{
972
            child = parent.addChildTaxon(taxon, parent.getReference(), parent.getMicroReference());
973
        }catch(Exception e){
974
            result.addException(e);
975
            result.setError();
976
            return result;
977
        }
978

    
979
        result.addUpdatedObject(parent);
980
        if (child != null){
981
            result.setCdmEntity(child);
982
        }
983
        return result;
984
    }
985

    
986
    @Override
987
    @Transactional
988
    public UpdateResult addTaxonNodeAgentRelation(UUID taxonNodeUUID, UUID agentUUID, DefinedTerm relationshipType){
989
        UpdateResult result = new UpdateResult();
990
        TaxonNode node = dao.load(taxonNodeUUID);
991
        TeamOrPersonBase<?> agent = (TeamOrPersonBase<?>) agentService.load(agentUUID);
992
        node.addAgentRelation(relationshipType, agent);
993
        try{
994
            dao.merge(node, true);
995
        }catch (Exception e){
996
            result.setError();
997
            result.addException(e);
998
        }
999
        result.setCdmEntity(node);
1000
        return result;
1001
    }
1002

    
1003
    @Override
1004
    @Transactional(readOnly=false)
1005
    public UpdateResult setSecundumForSubtree(SecundumForSubtreeConfigurator config) {
1006
        UpdateResult result = new UpdateResult();
1007
        IProgressMonitor monitor = config.getMonitor();
1008

    
1009
        if (monitor == null){
1010
            monitor = DefaultProgressMonitor.NewInstance();
1011
        }
1012
        TaxonNode subTree = load(config.getSubtreeUuid());
1013
        TreeIndex subTreeIndex = null;
1014
        Reference newSec = null;
1015
        if (config.getNewSecundum() != null){
1016
            newSec = referenceService.load(config.getNewSecundum().getUuid());
1017
        }
1018

    
1019
        if (config.getSubtreeUuid() == null){
1020
            result.setError();
1021
            result.addException(new NullPointerException("No subtree given"));
1022
            monitor.done();
1023
            return result;
1024
        }
1025

    
1026
        if (subTree == null){
1027
            result.setError();
1028
            result.addException(new NullPointerException("Subtree does not exist"));
1029
            monitor.done();
1030
            return result;
1031
        }else{
1032
            subTreeIndex = TreeIndex.NewInstance(subTree.treeIndex());
1033
            int count = config.isIncludeAcceptedTaxa() ? dao.countSecundumForSubtreeAcceptedTaxa(subTreeIndex, newSec, config.isOverwriteExistingAccepted(), config.isIncludeSharedTaxa(), config.isEmptySecundumDetail()):0;
1034
            count += config.isIncludeSynonyms() ? dao.countSecundumForSubtreeSynonyms(subTreeIndex, newSec, config.isOverwriteExistingSynonyms(), config.isIncludeSharedTaxa() , config.isEmptySecundumDetail()) :0;
1035
            monitor.beginTask("Update Secundum Reference", count);
1036
        }
1037

    
1038
        //Reference ref = config.getNewSecundum();
1039
        if (config.isIncludeAcceptedTaxa()){
1040
            monitor.subTask("Update Accepted Taxa");
1041

    
1042
            Set<TaxonBase> updatedTaxa = dao.setSecundumForSubtreeAcceptedTaxa(subTreeIndex, newSec, config.isOverwriteExistingAccepted(), config.isIncludeSharedTaxa(), config.isEmptySecundumDetail(), monitor);
1043
            result.addUpdatedObjects(updatedTaxa);
1044
        }
1045
        if (config.isIncludeSynonyms()){
1046
           monitor.subTask("Update Synonyms");
1047
           Set<TaxonBase> updatedSynonyms = dao.setSecundumForSubtreeSynonyms(subTreeIndex, newSec, config.isOverwriteExistingSynonyms(), config.isIncludeSharedTaxa() , config.isEmptySecundumDetail(), monitor);
1048
           result.addUpdatedObjects(updatedSynonyms);
1049
        }
1050

    
1051
        monitor.done();
1052
        return result;
1053
    }
1054

    
1055
    @Override
1056
    @Transactional(readOnly=false)
1057
    public UpdateResult setPublishForSubtree(PublishForSubtreeConfigurator config){
1058
        UpdateResult result = new UpdateResult();
1059
        IProgressMonitor monitor = config.getMonitor();
1060
        if (monitor == null){
1061
            monitor = DefaultProgressMonitor.NewInstance();
1062
        }
1063
        TreeIndex subTreeIndex = null;
1064

    
1065
        if (config.getSubtreeUuid() == null){
1066
            result.setError();
1067
            result.addException(new NullPointerException("No subtree given"));
1068
            monitor.done();
1069
            return result;
1070
        }
1071
        TaxonNode subTree = find(config.getSubtreeUuid());
1072
        boolean includeAcceptedTaxa = config.isIncludeAcceptedTaxa();
1073
        boolean publish = config.isPublish();
1074
        boolean includeSynonyms = config.isIncludeSynonyms();
1075
        boolean includeSharedTaxa = config.isIncludeSharedTaxa();
1076
        boolean includeHybrids = config.isIncludeHybrids();
1077
        boolean includeRelatedTaxa = config.isIncludeProParteSynonyms() || config.isIncludeMisapplications();
1078
        if (subTree == null){
1079
            result.setError();
1080
            result.addException(new NullPointerException("Subtree does not exist"));
1081
            monitor.done();
1082
            return result;
1083
        }else{
1084
            subTreeIndex = TreeIndex.NewInstance(subTree.treeIndex());
1085
            int count = includeAcceptedTaxa ? dao.countPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa, includeHybrids):0;
1086
            count += includeSynonyms ? dao.countPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa, includeHybrids):0;
1087
            count += includeRelatedTaxa ? dao.countPublishForSubtreeRelatedTaxa(subTreeIndex, publish, includeSharedTaxa, includeHybrids):0;
1088
            monitor.beginTask("Update publish flag", count);
1089
        }
1090

    
1091

    
1092
        if (includeAcceptedTaxa){
1093
            monitor.subTask("Update Accepted Taxa");
1094
            @SuppressWarnings("rawtypes")
1095
            Set<TaxonBase> updatedTaxa = dao.setPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa, includeHybrids, monitor);
1096
            result.addUpdatedObjects(updatedTaxa);
1097
        }
1098
        if (includeSynonyms){
1099
            monitor.subTask("Update Synonyms");
1100
            @SuppressWarnings("rawtypes")
1101
            Set<TaxonBase> updatedSynonyms = dao.setPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa, includeHybrids, monitor);
1102
            result.addUpdatedObjects(updatedSynonyms);
1103
        }
1104
        if (includeRelatedTaxa){
1105
            monitor.subTask("Update Related Taxa");
1106
            Set<UUID> relationTypes = new HashSet<>();
1107
            if (config.isIncludeMisapplications()){
1108
                relationTypes.addAll(TaxonRelationshipType.misappliedNameUuids());
1109
            }
1110
            if (config.isIncludeProParteSynonyms()){
1111
                relationTypes.addAll(TaxonRelationshipType.proParteOrPartialSynonymUuids());
1112
            }
1113
            @SuppressWarnings("rawtypes")
1114
            Set<TaxonBase> updatedTaxa = dao.setPublishForSubtreeRelatedTaxa(subTreeIndex, publish,
1115
                    relationTypes, includeSharedTaxa, includeHybrids, monitor);
1116
            result.addUpdatedObjects(updatedTaxa);
1117
        }
1118

    
1119
        monitor.done();
1120
        return result;
1121
    }
1122

    
1123
    @Override
1124
    public long count(TaxonNodeFilter filter){
1125
        return nodeFilterDao.count(filter);
1126
    }
1127

    
1128
    @Override
1129
    public List<UUID> uuidList(TaxonNodeFilter filter){
1130
        return nodeFilterDao.listUuids(filter);
1131
    }
1132

    
1133
    @Override
1134
    public List<Integer> idList(TaxonNodeFilter filter){
1135
        return nodeFilterDao.idList(filter);
1136
    }
1137

    
1138
    @Override
1139
    public TaxonNodeDto findCommonParentDto(Collection<TaxonNodeDto> nodes) {
1140
        TaxonNodeDto commonParent = null;
1141
        List<String> treePath = null;
1142
        for (TaxonNodeDto nodeDto : nodes) {
1143
            String nodeTreeIndex = nodeDto.getTreeIndex();
1144
            nodeTreeIndex = nodeTreeIndex.replaceFirst("#", "");
1145
            String[] split = nodeTreeIndex.split("#");
1146
            if(treePath == null){
1147
                treePath = Arrays.asList(split);
1148
            }
1149
            else{
1150
                List<String> match = new ArrayList<>();
1151
                for(int i=0;i<treePath.size();i++){
1152
                    if(i>=split.length){
1153
                        //current tree index is shorter so break
1154
                        break;
1155
                    }
1156
                    else if(split[i].equals(treePath.get(i))){
1157
                        //match found
1158
                        match.add(treePath.get(i));
1159
                    }
1160
                    else{
1161
                        //first mismatch found
1162
                        break;
1163
                    }
1164
                }
1165
                treePath = match;
1166
                if(treePath.isEmpty()){
1167
                    //no common parent found for at least two nodes
1168
                    //-> they belong to a different classification
1169
                    break;
1170
                }
1171
            }
1172
        }
1173
        if(treePath!=null && !treePath.isEmpty()) {
1174
            //get last index
1175
            int nodeId = Integer.parseInt(treePath.get(treePath.size()-1));
1176
            TaxonNode taxonNode = dao.load(nodeId, null);
1177
            commonParent = new TaxonNodeDto(taxonNode);
1178
        }
1179
        return commonParent;
1180
    }
1181

    
1182
    @Override
1183
    public List<TaxonDistributionDTO> getTaxonDistributionDTO(List<UUID> nodeUuids, List<String> propertyPaths, Authentication authentication, boolean openChildren){
1184
        Set<TaxonNode> nodes = new HashSet<>();
1185
        if (openChildren){
1186
            List<TaxonNode> parentNodes = load(nodeUuids, propertyPaths);
1187
            for (TaxonNode node: parentNodes){
1188
                nodes.addAll(listChildrenOf(node, null, null, true, true, propertyPaths));
1189
            }
1190
            nodes.addAll(parentNodes);
1191

    
1192
        }
1193
        List<TaxonDistributionDTO> result = new ArrayList<>();
1194
        boolean hasPermission = false;
1195
        //TaxonDescription instance = TaxonDescription.NewInstance();
1196
        //hasPermission = permissionEvaluator.hasPermission(authentication, instance, Operation.UPDATE);
1197
        for(TaxonNode node:nodes){
1198
            if (authentication != null ) {
1199
                hasPermission = permissionEvaluator.hasPermission(authentication, node, Operation.UPDATE);
1200
            }else {
1201
                hasPermission = true;
1202
            }
1203
            if (node.getTaxon() != null && hasPermission){
1204
                try{
1205
                    TaxonDistributionDTO dto = new TaxonDistributionDTO(node);
1206
                    result.add(dto);
1207
                }catch(Exception e){
1208
                    logger.error(e.getMessage(), e);
1209
                }
1210
            }
1211
        }
1212
//        result.sort(new TaxonDistributionDTOComparator());
1213
        return result;
1214
    }
1215

    
1216
    @Override
1217
    public <S extends TaxonNode> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
1218
            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
1219
        return page(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, INCLUDE_UNPUBLISHED);
1220
    }
1221

    
1222
    @Override
1223
    public <S extends TaxonNode> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
1224
            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths, boolean includeUnpublished) {
1225

    
1226
        List<S> records;
1227
        long resultSize = dao.count(clazz, restrictions);
1228
        if(AbstractPagerImpl.hasResultsInRange(resultSize, pageIndex, pageSize)){
1229
            records = dao.list(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, includeUnpublished);
1230
        } else {
1231
            records = new ArrayList<>();
1232
        }
1233
        Pager<S> pager = new DefaultPagerImpl<>(pageIndex, resultSize, pageSize, records);
1234
        return pager;
1235
    }
1236

    
1237
//    @Override
1238
//    public List<TaxonDistributionDTO> getTaxonDistributionDTOForSubtree(UUID parentNodeUuid,
1239
//            List<String> propertyPaths, boolean openChildren) {
1240
//        return getTaxonDistributionDTOForSubtree(parentNodeUuid, propertyPaths, null, openChildren);
1241
//    }
1242

    
1243
    @Override
1244
    public List<TaxonDistributionDTO> getTaxonDistributionDTO(List<UUID> nodeUuids,
1245
            List<String> propertyPaths, boolean openChildren) {
1246
        return getTaxonDistributionDTO(nodeUuids, propertyPaths, null, openChildren);
1247
    }
1248

    
1249

    
1250
    @Override
1251
    @Transactional(readOnly = false)
1252
    public UpdateResult cloneSubtree(SubtreeCloneConfigurator config) {
1253
        UpdateResult result = new UpdateResult();
1254

    
1255
        if (config.getSubTreeUuids().isEmpty()){
1256
            return result;
1257
        }
1258

    
1259
        //TODO error handling
1260
        Reference taxonSecundum = config.isReuseTaxa() || config.isReuseTaxonSecundum() || config.getTaxonSecundumUuid() == null ?
1261
                null : referenceDao.findByUuid(config.getTaxonSecundumUuid());
1262
        config.setTaxonSecundum(taxonSecundum);
1263

    
1264
        Reference parentChildReference = config.isReuseParentChildReference() || config.getParentChildReferenceUuid() == null ?
1265
                null : referenceDao.findByUuid(config.getParentChildReferenceUuid());
1266
        config.setParentChildReference(parentChildReference);
1267

    
1268
        Reference taxonRelationshipReference = config.getRelationTypeToOldTaxon() == null ?
1269
                null : referenceDao.findByUuid(config.getRelationshipReferenceUuid());
1270
        config.setRelationshipReference(taxonRelationshipReference);
1271

    
1272
        Classification classificationClone = Classification.NewInstance(config.getNewClassificationName());
1273

    
1274
        if (config.isReuseClassificationReference()){
1275
            TaxonNode anyNode = dao.findByUuid(config.getSubTreeUuids().iterator().next());
1276
            if (anyNode != null){
1277
                Reference oldClassificationRef = anyNode.getClassification().getReference();
1278
                classificationClone.setReference(oldClassificationRef);
1279
            }
1280
        }else if (config.getClassificationReferenceUuid() != null) {
1281
            Reference classificationReference = referenceDao.findByUuid(config.getClassificationReferenceUuid());
1282
            classificationClone.setReference(classificationReference);
1283
        }
1284

    
1285
        //clone taxa and taxon nodes
1286
//      List<Integer> childNodeIds = taxonNodeService.idList(taxonNodeFilter);
1287
//      List<TaxonNode> childNodes = taxonNodeService.loadByIds(childNodeIds, null);
1288
        List<TaxonNode> rootNodes = this.find(config.getSubTreeUuids());
1289
        for (TaxonNode taxonNode : rootNodes) {
1290
            cloneTaxonRecursive(taxonNode, classificationClone.getRootNode(), config);
1291
        }
1292
        classificationDao.saveOrUpdate(classificationClone);
1293
        result.setCdmEntity(classificationClone);
1294
        return result;
1295
    }
1296

    
1297
    private void cloneTaxonRecursive(TaxonNode originalParentNode, TaxonNode parentNodeClone,
1298
            SubtreeCloneConfigurator config){
1299

    
1300
        Taxon originalTaxon = CdmBase.deproxy(originalParentNode.getTaxon());
1301
        TaxonNode childNodeClone;
1302
        if (originalTaxon != null){
1303
            String microReference = null;
1304
            if (config.isReuseTaxa()){
1305
                childNodeClone = parentNodeClone.addChildTaxon(originalTaxon, config.getParentChildReference(), microReference);
1306
            }else{
1307
                Taxon cloneTaxon = originalTaxon.clone(config.isIncludeSynonymsIncludingManAndProParte(),
1308
                        config.isIncludeTaxonRelationshipsExcludingManAndProParte(),
1309
                        config.isIncludeDescriptiveData(), config.isIncludeMedia());
1310

    
1311
                //name
1312
                if (!config.isReuseNames()){
1313
                    cloneTaxon.setName(cloneTaxon.getName().clone());
1314
                    //TODO needs further handling for name relationships etc., see #9349
1315
                    cloneTaxon.getSynonyms().forEach(syn ->
1316
                        syn.setName(syn.getName() == null ? null : syn.getName().clone()));
1317
                }
1318

    
1319
                if (!config.isReuseTaxonSecundum()){
1320
                    cloneTaxon.setSec(config.getTaxonSecundum());
1321
                }
1322

    
1323
                //add relation between taxa
1324
                if (config.getRelationTypeToOldTaxon() != null){
1325
                    TaxonRelationship rel = cloneTaxon.addTaxonRelation(originalParentNode.getTaxon(), config.getRelationTypeToOldTaxon(),
1326
                            config.getRelationshipReference(), microReference);
1327
                    rel.setDoubtful(config.isRelationDoubtful());
1328
                }
1329
                childNodeClone = parentNodeClone.addChildTaxon(cloneTaxon, config.getParentChildReference(), microReference);
1330
            }
1331

    
1332
            //probably necessary as taxon nodes do not cascade
1333
            dao.saveOrUpdate(childNodeClone);
1334

    
1335
        }else{
1336
            childNodeClone = parentNodeClone;
1337
        }
1338
        //add children
1339
        if (config.isDoRecursive()){
1340
            List<TaxonNode> originalChildNodes = originalParentNode.getChildNodes();
1341
            HHH_9751_Util.removeAllNull(originalChildNodes);
1342

    
1343
            for (TaxonNode originalChildNode : originalChildNodes) {
1344
                cloneTaxonRecursive(originalChildNode, childNodeClone, config);
1345
            }
1346
        }
1347
    }
1348

    
1349
}
(90-90/100)