Project

General

Profile

Download (49.7 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9

    
10
package eu.etaxonomy.cdm.api.service;
11

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

    
26
import javax.persistence.EntityNotFoundException;
27

    
28
import org.apache.commons.collections.CollectionUtils;
29
import org.apache.commons.lang.StringUtils;
30
import org.apache.log4j.Logger;
31
import org.springframework.beans.factory.annotation.Autowired;
32
import org.springframework.stereotype.Service;
33
import org.springframework.transaction.annotation.Transactional;
34

    
35
import eu.etaxonomy.cdm.api.service.config.CreateHierarchyForClassificationConfigurator;
36
import eu.etaxonomy.cdm.api.service.config.NodeDeletionConfigurator.ChildHandling;
37
import eu.etaxonomy.cdm.api.service.config.SubtreeCloneConfigurator;
38
import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
39
import eu.etaxonomy.cdm.api.service.dto.EntityDTO;
40
import eu.etaxonomy.cdm.api.service.dto.GroupedTaxonDTO;
41
import eu.etaxonomy.cdm.api.service.dto.MarkedEntityDTO;
42
import eu.etaxonomy.cdm.api.service.dto.TaxonInContextDTO;
43
import eu.etaxonomy.cdm.api.service.pager.Pager;
44
import eu.etaxonomy.cdm.api.service.pager.PagerUtils;
45
import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
46
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
47
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
48
import eu.etaxonomy.cdm.exception.FilterException;
49
import eu.etaxonomy.cdm.exception.UnpublishedException;
50
import eu.etaxonomy.cdm.hibernate.HHH_9751_Util;
51
import eu.etaxonomy.cdm.model.common.CdmBase;
52
import eu.etaxonomy.cdm.model.common.ITreeNode;
53
import eu.etaxonomy.cdm.model.common.MarkerType;
54
import eu.etaxonomy.cdm.model.common.TreeIndex;
55
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
56
import eu.etaxonomy.cdm.model.description.TaxonDescription;
57
import eu.etaxonomy.cdm.model.media.Media;
58
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
59
import eu.etaxonomy.cdm.model.media.MediaUtils;
60
import eu.etaxonomy.cdm.model.name.INonViralName;
61
import eu.etaxonomy.cdm.model.name.Rank;
62
import eu.etaxonomy.cdm.model.name.TaxonName;
63
import eu.etaxonomy.cdm.model.reference.Reference;
64
import eu.etaxonomy.cdm.model.taxon.Classification;
65
import eu.etaxonomy.cdm.model.taxon.ITaxonNodeComparator;
66
import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
67
import eu.etaxonomy.cdm.model.taxon.Synonym;
68
import eu.etaxonomy.cdm.model.taxon.Taxon;
69
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
70
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
71
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
72
import eu.etaxonomy.cdm.model.term.DefinedTermBase;
73
import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
74
import eu.etaxonomy.cdm.persistence.dao.reference.IReferenceDao;
75
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
76
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
77
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
78
import eu.etaxonomy.cdm.persistence.dao.term.IDefinedTermDao;
79
import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
80
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
81
import eu.etaxonomy.cdm.persistence.dto.TaxonStatus;
82
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
83
import eu.etaxonomy.cdm.persistence.query.OrderHint;
84
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
85
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
86

    
87
/**
88
 * @author n.hoffmann
89
 * @since Sep 21, 2009
90
 */
91
@Service
92
@Transactional(readOnly = true)
93
public class ClassificationServiceImpl
94
             extends IdentifiableServiceBase<Classification, IClassificationDao>
95
             implements IClassificationService {
96

    
97
    private static final Logger logger = Logger.getLogger(ClassificationServiceImpl.class);
98

    
99
    @Autowired
100
    private ITaxonNodeDao taxonNodeDao;
101

    
102
    @Autowired
103
    private ITaxonDao taxonDao;
104

    
105
    @Autowired
106
    private ITaxonNodeService taxonNodeService;
107

    
108
    @Autowired
109
    private IReferenceDao referenceDao;
110

    
111
    @Autowired
112
    private IDefinedTermDao termDao;
113

    
114
    @Autowired
115
    private IBeanInitializer defaultBeanInitializer;
116

    
117
    @Override
118
    @Autowired
119
    protected void setDao(IClassificationDao dao) {
120
        this.dao = dao;
121
    }
122

    
123
    private Comparator<? super TaxonNode> taxonNodeComparator;
124

    
125
    @Autowired
126
    public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){
127
        this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;
128
    }
129

    
130
    @Override
131
    public TaxonNode loadTaxonNodeByTaxon(Taxon taxon, UUID classificationUuid, List<String> propertyPaths){
132
        Classification tree = dao.load(classificationUuid);
133
        TaxonNode node = tree.getNode(taxon);
134

    
135
        return loadTaxonNode(node.getUuid(), propertyPaths);
136
    }
137

    
138
    public TaxonNode loadTaxonNode(UUID taxonNodeUuid, List<String> propertyPaths){
139
        return taxonNodeDao.load(taxonNodeUuid, propertyPaths);
140
    }
141

    
142
    @Override
143
    @Transactional(readOnly = false)
144
    public UpdateResult cloneClassification(SubtreeCloneConfigurator config) {
145
        UpdateResult result = new UpdateResult();
146

    
147
        if (config.getSubTreeUuids().isEmpty()){
148
            return result;
149
        }
150

    
151
        //TODO error handling
152
        Reference taxonSecundum = config.isReuseTaxa() || config.isReuseTaxonSecundum() || config.getTaxonSecundumUuid() == null ?
153
                null : referenceDao.findByUuid(config.getTaxonSecundumUuid());
154
        config.setTaxonSecundum(taxonSecundum);
155

    
156
        Reference parentChildReference = config.isReuseParentChildReference() || config.getParentChildReferenceUuid() == null ?
157
                null : referenceDao.findByUuid(config.getParentChildReferenceUuid());
158
        config.setParentChildReference(parentChildReference);
159

    
160
        Reference taxonRelationshipReference = config.getRelationTypeToOldTaxon() == null ?
161
                null : referenceDao.findByUuid(config.getRelationshipReferenceUuid());
162
        config.setRelationshipReference(taxonRelationshipReference);
163

    
164
        Classification classificationClone = Classification.NewInstance(config.getClassificationName());
165

    
166
        if (config.isReuseClassificationReference()){
167
            TaxonNode anyNode = taxonNodeDao.findByUuid(config.getSubTreeUuids().iterator().next());
168
            if (anyNode != null){
169
                Reference oldClassificationRef = anyNode.getClassification().getReference();
170
                classificationClone.setReference(oldClassificationRef);
171
            }
172
        }else if (config.getClassificationReferenceUuid() != null) {
173
            Reference classificationReference = referenceDao.findByUuid(config.getClassificationReferenceUuid());
174
            classificationClone.setReference(classificationReference);
175
        }
176

    
177
    	//clone taxa and taxon nodes
178
//    	List<Integer> childNodeIds = taxonNodeService.idList(taxonNodeFilter);
179
//    	List<TaxonNode> childNodes = taxonNodeService.loadByIds(childNodeIds, null);
180
    	List<TaxonNode> rootNodes = taxonNodeService.find(config.getSubTreeUuids());
181
    	for (TaxonNode taxonNode : rootNodes) {
182
    	    addChildTaxaToClone(taxonNode, classificationClone.getRootNode(), config);
183
    	}
184
    	dao.saveOrUpdate(classificationClone);
185
    	result.setCdmEntity(classificationClone);
186
    	return result;
187
    }
188

    
189
    private void addChildTaxaToClone(TaxonNode originalParentNode, TaxonNode parentNodeClone,
190
            SubtreeCloneConfigurator config){
191

    
192
        Taxon originalTaxon = CdmBase.deproxy(originalParentNode.getTaxon());
193
        if (originalTaxon == null){
194
            for (TaxonNode originalChildChildNode : originalParentNode.getChildNodes()) {
195
                addChildTaxaToClone(originalChildChildNode, parentNodeClone, config);
196
            }
197
        }else{
198
            TaxonNode childNodeClone;
199
            String microReference = null;
200
            if (config.isReuseTaxa()){
201
                childNodeClone = parentNodeClone.addChildTaxon(originalTaxon, config.getParentChildReference(), microReference);
202
            }else{
203
                Taxon cloneTaxon = originalTaxon.clone(config.isCloneSynonyms(), config.isCloneTaxonRelationships(),
204
                        config.isCloneDescriptiveData(), config.isCloneMedia());
205
//                xxx KonzeptClone MAN, ppSyns;
206
                if (!config.isReuseTaxonSecundum()){
207
                    cloneTaxon.setSec(config.getTaxonSecundum());
208
                }
209

    
210
                //add relation between taxa
211
                if (config.getRelationTypeToOldTaxon() != null){
212
                    TaxonRelationship rel = cloneTaxon.addTaxonRelation(originalParentNode.getTaxon(), config.getRelationTypeToOldTaxon(),
213
                            config.getRelationshipReference(), microReference);
214
                    rel.setDoubtful(config.isRelationDoubtful());
215
                }
216
                childNodeClone = parentNodeClone.addChildTaxon(cloneTaxon, config.getParentChildReference(), microReference);
217
            }
218

    
219
            //TODO necessary?
220
            taxonNodeDao.saveOrUpdate(childNodeClone);
221
            //add children
222
            List<TaxonNode> originalChildNodes = originalParentNode.getChildNodes();
223
            HHH_9751_Util.removeAllNull(originalChildNodes);
224

    
225
            for (TaxonNode originalChildNode : originalChildNodes) {
226
                addChildTaxaToClone(originalChildNode, childNodeClone, config);
227
            }
228
        }
229
    }
230

    
231
    @Override
232
    public List<TaxonNode> listRankSpecificRootNodes(Classification classification,
233
            TaxonNode subtree, Rank rank,
234
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
235
        return pageRankSpecificRootNodes(classification, subtree, rank, includeUnpublished, pageSize, pageIndex, propertyPaths).getRecords();
236
    }
237

    
238
    @Override
239
    public List<TaxonNodeDto> listRankSpecificRootNodeDtos(Classification classification, TaxonNode subtree,
240
            Rank rank, boolean includeUnpublished, Integer pageSize, Integer pageIndex, TaxonNodeDtoSortMode sortMode,
241
            List<String> propertyPaths) {
242
        List<TaxonNode> list = listRankSpecificRootNodes(classification, subtree, rank, includeUnpublished, pageSize, pageIndex, propertyPaths);
243
        return list.stream().filter(e ->  e != null).map(e -> new TaxonNodeDto(e)).sorted(sortMode.newComparator()).collect(Collectors.toList());
244
    }
245

    
246
    @Override
247
    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, Rank rank,
248
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
249
        return pageRankSpecificRootNodes(classification, null, rank, includeUnpublished, pageSize, pageIndex, propertyPaths);
250
    }
251

    
252
    @Override
253
    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, TaxonNode subtree, Rank rank,
254
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
255
        long[] numberOfResults = dao.countRankSpecificRootNodes(classification, subtree, includeUnpublished, rank);
256
        long totalNumberOfResults = numberOfResults[0] + (numberOfResults.length > 1 ? numberOfResults[1] : 0);
257

    
258
        List<TaxonNode> results = new ArrayList<>();
259

    
260
        if (AbstractPagerImpl.hasResultsInRange(totalNumberOfResults, pageIndex, pageSize)) { // no point checking again
261
            Integer limit = PagerUtils.limitFor(pageSize);
262
            Integer start = PagerUtils.startFor(pageSize, pageIndex);
263

    
264
            Integer remainingLimit = limit;
265
            int[] queryIndexes = rank == null ? new int[]{0} : new int[]{0,1};
266

    
267
            for(int queryIndex: queryIndexes) {
268
                if(start != null && start > numberOfResults[queryIndex]) {
269
                    // start in next query with new start value
270
                    start = start - (int)numberOfResults[queryIndex];
271
                    continue;
272
                }
273

    
274
                List<TaxonNode> perQueryResults = dao.listRankSpecificRootNodes(classification,
275
                        subtree, rank, includeUnpublished, remainingLimit,
276
                        start, propertyPaths, queryIndex);
277
                results.addAll(perQueryResults);
278
                if(remainingLimit != null ){
279
                    remainingLimit = remainingLimit - results.size();
280
                    if(remainingLimit <= 0) {
281
                        // no need to run further queries if first query returned enough items!
282
                        break;
283
                    }
284
                    // start at with fist item of next query to fetch the remaining items
285
                    start = 0;
286
                }
287
            }
288
        }
289
//        long start_t = System.currentTimeMillis();
290
        Collections.sort(results, taxonNodeComparator); // TODO is ordering during the hibernate query in the dao possible?
291
//        System.err.println("service.pageRankSpecificRootNodes() - Collections.sort(results,  taxonNodeComparator) " + (System.currentTimeMillis() - start_t));
292
        return new DefaultPagerImpl<>(pageIndex, totalNumberOfResults, pageSize, results);
293

    
294
    }
295

    
296
    @Override
297
    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, Rank baseRank,
298
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
299
        return loadTreeBranch(taxonNode, null, baseRank, includeUnpublished, propertyPaths);
300
    }
301

    
302
    @Override
303
    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, TaxonNode subtree, Rank baseRank,
304
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
305

    
306
        TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
307
        if(baseRank != null){
308
            baseRank = (Rank) termDao.load(baseRank.getUuid());
309
        }
310
        if (!includeUnpublished && thisNode.getTaxon() != null && !thisNode.getTaxon().isPublish()){
311
            throw new UnpublishedException("Final taxon in tree branch is unpublished.");
312
        }
313

    
314
        List<TaxonNode> pathToRoot = new ArrayList<>();
315
        pathToRoot.add(thisNode);
316

    
317
        while(!thisNode.isTopmostNode()){
318
            //TODO why do we need to deproxy here?
319
            //     without this thisNode.getParent() will return NULL in
320
            //     some cases (environment dependend?) even if the parent exits
321
            TaxonNode parentNode = CdmBase.deproxy(thisNode).getParent();
322

    
323
            if(parentNode == null){
324
                throw new NullPointerException("Taxon node " + thisNode + " must have a parent since it is not top most");
325
            }
326
            if(parentNode.getTaxon() == null){
327
                throw new NullPointerException("The taxon associated with taxon node " + parentNode + " is NULL");
328
            }
329
            if(!includeUnpublished && !parentNode.getTaxon().isPublish()){
330
                throw new UnpublishedException("Some taxon in tree branch is unpublished.");
331
            }
332
            if(parentNode.getTaxon().getName() == null){
333
                throw new NullPointerException("The name of the taxon associated with taxonNode " + parentNode + " is NULL");
334
            }
335

    
336
            Rank parentNodeRank = (parentNode.getTaxon().getName() == null) ? null : parentNode.getTaxon().getName().getRank();
337
            // stop if the next parent is higher than the baseRank
338
            if(baseRank != null && parentNodeRank != null && baseRank.isLower(parentNodeRank)){
339
                break;
340
            }
341
            if((subtree!= null && !subtree.isAncestor(parentNode) )){
342
                break;
343
            }
344

    
345
            pathToRoot.add(parentNode);
346
            thisNode = parentNode;
347
        }
348

    
349
        // initialize and invert order of nodes in list
350
        defaultBeanInitializer.initializeAll(pathToRoot, propertyPaths);
351
        Collections.reverse(pathToRoot);
352

    
353
        return pathToRoot;
354
    }
355

    
356
    @Override
357
    public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification, Rank baseRank,
358
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
359
        return loadTreeBranchToTaxon(taxon, classification, null, baseRank, includeUnpublished, propertyPaths);
360
    }
361

    
362
    @Override
363
    public List<TaxonNodeDto> loadTreeBranchDTOsToTaxon(Taxon taxon, Classification classification,
364
            TaxonNode subtree, Rank baseRank,
365
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException {
366
        List<TaxonNode> list = loadTreeBranchToTaxon(taxon, classification, subtree, baseRank, includeUnpublished, propertyPaths);
367
        return list.stream().map(e -> new TaxonNodeDto(e)).collect(Collectors.toList());
368
    }
369

    
370
    @Override
371
    public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification,
372
            TaxonNode subtree, Rank baseRank,
373
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
374

    
375
        UUID nodeUuid = getTaxonNodeUuidByTaxonUuid(classification.getUuid(), taxon.getUuid());
376
        TaxonNode node = taxonNodeService.find(nodeUuid);
377
        if(node == null){
378
            logger.warn("The specified taxon is not found in the given tree.");
379
            return null;
380
        }else if (subtree != null && !node.isDescendant(subtree)){
381
            //TODO handle as exception? E.g. FilterException, AccessDeniedException?
382
            logger.warn("The specified taxon is not found for the given subtree.");
383
            return null;
384
        }
385

    
386
        return loadTreeBranch(node, subtree, baseRank, includeUnpublished, propertyPaths);
387
    }
388

    
389

    
390
    @Override
391
    public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
392
            List<String> propertyPaths) {
393
        taxonNode = taxonNodeDao.load(taxonNode.getUuid());
394
        List<TaxonNode> childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
395
        defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
396
        Collections.sort(childNodes, taxonNodeComparator);
397
        return childNodes;
398
    }
399

    
400
    @Override
401
    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid,
402
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths){
403
        try {
404
            return listChildNodesOfTaxon(taxonUuid, classificationUuid, null, includeUnpublished, pageSize, pageIndex, propertyPaths);
405
        } catch (FilterException e) {
406
            throw new RuntimeException(e);  //this should not happen as filter is null
407
        }
408
    }
409

    
410
    @Override
411
    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid, UUID subtreeUuid,
412
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) throws FilterException{
413

    
414
        Classification classification = dao.load(classificationUuid);
415
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
416
        TaxonNode subtree = taxonNodeDao.load(subtreeUuid);
417
        if (subtreeUuid != null && subtree == null){
418
            throw new FilterException("Taxon node for subtree filter can not be found in database", true);
419
        }
420

    
421
        List<TaxonNode> results = dao.listChildrenOf(
422
                taxon, classification, subtree, includeUnpublished, pageSize, pageIndex, propertyPaths);
423
        Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
424
        return results;
425
    }
426

    
427
    @Override
428
    public List<TaxonNodeDto> listChildNodeDtosOfTaxon(UUID taxonUuid, UUID classificationUuid, UUID subtreeUuid, boolean includeUnpublished,
429
            Integer pageSize, Integer pageIndex, TaxonNodeDtoSortMode sortMode, List<String> propertyPaths) throws FilterException{
430
        Classification classification = dao.load(classificationUuid);
431
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
432
        TaxonNode subtree = taxonNodeDao.load(subtreeUuid);
433
        if (subtreeUuid != null && subtree == null){
434
            throw new FilterException("Taxon node for subtree filter can not be found in database", true);
435
        }
436

    
437
        List<TaxonNode> results = dao.listChildrenOf(
438
                taxon, classification, subtree, includeUnpublished, pageSize, pageIndex, propertyPaths);
439
        Comparator<TaxonNodeDto> comparator = sortMode.newComparator();
440
        // TODO order during the hibernate query in the dao?
441
        List<TaxonNodeDto> dtos = results.stream().map(e -> new TaxonNodeDto(e)).sorted(comparator).collect(Collectors.toList());
442
        return dtos;
443
    }
444

    
445
    @Override
446
    public Pager<TaxonNode> pageSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, boolean includeUnpublished,
447
            Integer pageSize, Integer pageIndex, List<String> propertyPaths){
448

    
449
        Classification classification = dao.load(classificationUuid);
450
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
451

    
452
        long numberOfResults = dao.countSiblingsOf(taxon, classification, includeUnpublished);
453

    
454
        List<TaxonNode> results;
455
        if(PagerUtils.hasResultsInRange(numberOfResults, pageIndex, pageSize)) {
456
            results = dao.listSiblingsOf(taxon, classification, includeUnpublished, pageSize, pageIndex, propertyPaths);
457
            Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
458
        } else {
459
            results = new ArrayList<>();
460
        }
461

    
462
        return new DefaultPagerImpl<>(pageIndex, numberOfResults, pageSize, results);
463
    }
464

    
465
    @Override
466
    public List<TaxonNode> listSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, boolean includeUnpublished,
467
            Integer pageSize, Integer pageIndex, List<String> propertyPaths){
468

    
469
        Pager<TaxonNode> pager = pageSiblingsOfTaxon(taxonUuid, classificationUuid, includeUnpublished, pageSize, pageIndex, propertyPaths);
470
        return pager.getRecords();
471
    }
472

    
473
    @Override
474
    public ITaxonTreeNode getTreeNodeByUuid(UUID uuid){
475
        ITaxonTreeNode treeNode = taxonNodeDao.findByUuid(uuid);
476
        if(treeNode == null){
477
            treeNode = dao.findByUuid(uuid);
478
        }
479

    
480
        return treeNode;
481
    }
482

    
483
    @Override
484
    public TaxonNode getRootNode(UUID classificationUuid){
485
        return dao.getRootNode(classificationUuid);
486
    }
487

    
488
    @Override
489
    public List<Classification> listClassifications(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
490
        return dao.list(limit, start, orderHints, propertyPaths);
491
    }
492

    
493
    @Override
494
    public UUID removeTaxonNode(TaxonNode taxonNode) {
495
        return taxonNodeDao.delete(taxonNode);
496
    }
497
    @Override
498
    public UUID removeTreeNode(ITaxonTreeNode treeNode) {
499
        if(treeNode instanceof Classification){
500
            return dao.delete((Classification) treeNode);
501
        }else if(treeNode instanceof TaxonNode){
502
            return taxonNodeDao.delete((TaxonNode)treeNode);
503
        }
504
        return null;
505
    }
506
    @Override
507
    public UUID saveTaxonNode(TaxonNode taxonNode) {
508
        return taxonNodeDao.save(taxonNode).getUuid();
509
    }
510

    
511
    @Override
512
    public Map<UUID, TaxonNode> saveTaxonNodeAll(
513
            Collection<TaxonNode> taxonNodeCollection) {
514
        return taxonNodeDao.saveAll(taxonNodeCollection);
515
    }
516

    
517
    @Override
518
    public UUID saveClassification(Classification classification) {
519

    
520
       taxonNodeDao.saveOrUpdateAll(classification.getAllNodes());
521
       UUID result =dao.saveOrUpdate(classification);
522
       return result;
523
    }
524

    
525
    @Override
526
    public UUID saveTreeNode(ITaxonTreeNode treeNode) {
527
        if(treeNode instanceof Classification){
528
            return dao.save((Classification) treeNode).getUuid();
529
        }else if(treeNode instanceof TaxonNode){
530
            return taxonNodeDao.save((TaxonNode)treeNode).getUuid();
531
        }
532
        return null;
533
    }
534

    
535
    @Override
536
    public List<TaxonNode> getAllNodes(){
537
        return taxonNodeDao.list(null,null);
538
    }
539

    
540
    @Override
541
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, Integer limit, String pattern, boolean searchForClassifications, boolean includeDoubtful) {
542
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid),  limit, pattern, searchForClassifications, includeDoubtful);
543
    }
544

    
545
    @Override
546
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification,  Integer limit, String pattern, boolean searchForClassifications) {
547
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification,  limit, pattern, searchForClassifications);
548
    }
549

    
550
    @Override
551
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, boolean searchForClassifications ) {
552
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), null, null, searchForClassifications);
553
    }
554

    
555
    @Override
556
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, boolean searchForClassifications ) {
557
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, null, null, searchForClassifications);
558
    }
559

    
560
    @Override
561
    public List<UuidAndTitleCache<Classification>> getUuidAndTitleCache(Integer limit, String pattern) {
562
        return dao.getUuidAndTitleCache(limit, pattern);
563
    }
564

    
565
    @Override
566
    public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(
567
            TaxonNode taxonNode, List<String> propertyPaths, int size,
568
            int height, int widthOrDuration, String[] mimeTypes) {
569

    
570
        TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<>();
571
        List<MediaRepresentation> mediaRepresentations = new ArrayList<>();
572

    
573
        //add all media of the children to the result map
574
        if (taxonNode != null){
575

    
576
            List<TaxonNode> nodes = new ArrayList<>();
577

    
578
            nodes.add(loadTaxonNode(taxonNode.getUuid(), propertyPaths));
579
            nodes.addAll(loadChildNodesOfTaxonNode(taxonNode, propertyPaths));
580

    
581
            for(TaxonNode node : nodes){
582
                Taxon taxon = node.getTaxon();
583
                for (TaxonDescription taxonDescription: taxon.getDescriptions()){
584
                    for (DescriptionElementBase descriptionElement: taxonDescription.getElements()){
585
                        for(Media media : descriptionElement.getMedia()){
586
                            //find the best matching representation
587
                            mediaRepresentations.add(MediaUtils.findBestMatchingRepresentation(media,null, size, height, widthOrDuration, mimeTypes, MediaUtils.MissingValueStrategy.MAX));
588
                        }
589
                    }
590
                }
591
                result.put(taxon.getUuid(), mediaRepresentations);
592
            }
593
        }
594

    
595
        return result;
596
    }
597

    
598
    @Override
599
    @Transactional(readOnly = false)
600
    public UpdateResult updateCaches(Class<? extends Classification> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Classification> cacheStrategy, IProgressMonitor monitor) {
601
        if (clazz == null){
602
            clazz = Classification.class;
603
        }
604
        return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
605
    }
606

    
607
    /**
608
     *
609
     * @param allNodesOfClassification
610
     * @return null - if  allNodesOfClassification is empty <br>
611
     */
612

    
613
    private Map<String, List<TaxonNode>> getSortedGenusList(Collection<TaxonNode> allNodesOfClassification){
614

    
615
    	if(allNodesOfClassification == null || allNodesOfClassification.isEmpty()){
616
    		return null;
617
    	}
618
    	Map<String, List<TaxonNode>> sortedGenusMap = new HashMap<>();
619
    	for(TaxonNode node:allNodesOfClassification){
620
    		Taxon taxon = node.getTaxon();
621
    		INonViralName name = taxon.getName();
622
    		String genusOrUninomial = name.getGenusOrUninomial();
623
    		//if rank unknown split string and take first word
624
    		if(genusOrUninomial == null){
625
    			String titleCache = taxon.getTitleCache();
626
    			String[] split = titleCache.split("\\s+");
627
    			for(String s:split){
628
    				genusOrUninomial = s;
629
    				break;
630
    			}
631
    		}
632
    		//if node has children
633

    
634
    		//retrieve list from map if not create List
635
    		if(sortedGenusMap.containsKey(genusOrUninomial)){
636
    			List<TaxonNode> list = sortedGenusMap.get(genusOrUninomial);
637
    			list.add(node);
638
    			sortedGenusMap.put(genusOrUninomial, list);
639
    		}else{
640
    			//create List for genus
641
    			List<TaxonNode> list = new ArrayList<>();
642
    			list.add(node);
643
    			sortedGenusMap.put(genusOrUninomial, list);
644
    		}
645
    	}
646
    	return sortedGenusMap;
647
    }
648

    
649
    /**
650
     *
651
     * creates new Classification and parent TaxonNodes at genus level
652
     *
653
     *
654
     * @param map GenusMap which holds a name (Genus) and all the same Taxa as a list
655
     * @param classification you want to improve the hierarchy (will not be modified)
656
     * @param configurator to change certain settings, if null then standard settings will be taken
657
     * @return new classification with parentNodes for each entry in the map
658
     */
659
    @SuppressWarnings({ "unchecked" })
660
	@Transactional(readOnly = false)
661
	@Override
662
    public UpdateResult createHierarchyInClassification(Classification classification, CreateHierarchyForClassificationConfigurator configurator){
663
        UpdateResult result = new UpdateResult();
664
    	classification = dao.findByUuid(classification.getUuid());
665
    	Map<String, List<TaxonNode>> map = getSortedGenusList(classification.getAllNodes());
666

    
667
    	final String APPENDIX = "repaired";
668
    	String titleCache = StringUtils.isBlank(classification.getTitleCache()) ? " " : classification.getTitleCache() ;
669
    	//TODO classification clone???
670
    	Classification newClassification = Classification.NewInstance(titleCache +" "+ APPENDIX);
671
    	newClassification.setReference(classification.getReference());
672

    
673
    	for(Map.Entry<String, List<TaxonNode>> entry:map.entrySet()){
674
    		String genus = entry.getKey();
675
    		List<TaxonNode> listOfTaxonNodes = entry.getValue();
676
    		TaxonNode parentNode = null;
677
    		//Search for genus in list
678
    		for(TaxonNode tNode:listOfTaxonNodes){
679
    			//take that taxonNode as parent and remove from list with all it possible children
680
    			//FIXME NPE for name
681
    			TaxonName name = tNode.getTaxon().getName();
682
    			if(name.getNameCache().equalsIgnoreCase(genus)){
683
    				TaxonNode clone = tNode.clone();
684
    				if(!tNode.hasChildNodes()){
685
    					//FIXME remove classification
686
//    					parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
687
    					parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
688
    					//remove taxonNode from list because just added to classification
689
    					result.addUpdatedObject(tNode);
690
    					listOfTaxonNodes.remove(tNode);
691
    				}else{
692
    					//get all childNodes
693
    					//save prior Hierarchy and remove them from the list
694
    					List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tNode, clone, result);
695
//    					parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
696
      					//FIXME remove classification
697
    					parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
698
    					//remove taxonNode from list because just added to classification
699
    					result.addUpdatedObject(tNode);
700
    					listOfTaxonNodes.remove(tNode);
701
    					if(copyAllChildrenToTaxonNode != null){
702
    						listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
703
    					}
704
    				}
705
    				break;
706
    			}
707
    		}
708
    		if(parentNode == null){
709
    			//if no match found in list, create parentNode
710
    			NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
711
    			TaxonName TaxonName = (TaxonName)parser.parseFullName(genus);
712
    			//TODO Sec via configurator
713
    			Taxon taxon = Taxon.NewInstance(TaxonName, null);
714
    			parentNode = newClassification.addChildTaxon(taxon, 0, null, null);
715
    			result.addUpdatedObject(parentNode);
716
    		}
717
    		//iterate over the rest of the list
718
    		for(TaxonNode tn : listOfTaxonNodes){
719
    			//if TaxonNode has a parent and this is not the classification then skip it
720
    			//and add to new classification via the parentNode as children of it
721
    			//this should assures to keep the already existing hierarchy
722
    			//FIXME: Assert is not rootnode --> entrypoint is not classification in future but rather rootNode
723

    
724
    			if(!tn.isTopmostNode()){
725
    				continue; //skip to next taxonNode
726
    			}
727

    
728
    			TaxonNode clone = tn.clone();
729
    			//FIXME: citation from node
730
    			//TODO: addchildNode without citation and references
731
//    			TaxonNode taxonNode = parentNode.addChildNode(clone, classification.getCitation(), classification.getMicroReference());
732
    			TaxonNode taxonNode = parentNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
733
    			result.addUnChangedObject(clone);
734
    			if(tn.hasChildNodes()){
735
    				//save hierarchy in new classification
736
    				List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tn, taxonNode, result);
737
    				if(copyAllChildrenToTaxonNode != null){
738
    					listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
739
    				}
740
    			}
741
    		}
742
    	}
743
    	dao.saveOrUpdate(newClassification);
744
    	result.setCdmEntity(newClassification);
745
    	return result;
746
    }
747

    
748
    /**
749
     *
750
     * recursive method to get all childnodes of taxonNode in classification.
751
     *
752
     * @param classification just for References and Citation, can be null
753
     * @param copyFromNode TaxonNode with Children
754
     * @param copyToNode TaxonNode which will receive the children
755
     * @return List of ChildNode which has been added. If node has no children returns null
756
     */
757
   private List<TaxonNode> copyAllChildrenToTaxonNode(TaxonNode copyFromNode, TaxonNode copyToNode, UpdateResult result) {
758
		List<TaxonNode> childNodes;
759
		if(!copyFromNode.hasChildNodes()){
760
			return null;
761
		}else{
762
			childNodes = copyFromNode.getChildNodes();
763
		}
764
		for(TaxonNode childNode:childNodes){
765
			TaxonNode clone = childNode.clone();
766
			result.addUnChangedObject(clone);
767
			if(childNode.hasChildNodes()){
768
				copyAllChildrenToTaxonNode(childNode, clone, result);
769
			}
770
			//FIXME: citation from node instead of classification
771
//			copyToNode.addChildNode(clone,classification.getCitation(), classification.getMicroReference());
772
			copyToNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
773
		}
774
		return childNodes;
775
	}
776

    
777
    @Override
778
    public ClassificationLookupDTO classificationLookup(Classification classification) {
779
        return dao.classificationLookup(classification);
780
    }
781

    
782
    @Override
783
    @Transactional
784
    public DeleteResult delete(UUID classificationUuid, TaxonDeletionConfigurator config){
785
        DeleteResult result = new DeleteResult();
786
        Classification classification = dao.findByUuid(classificationUuid);
787
        if (classification == null){
788
            result.addException(new IllegalArgumentException("The classification does not exist in database."));
789
            result.setAbort();
790
            return result;
791
        }
792
        if (!classification.hasChildNodes()){
793
            dao.delete(classification);
794
            result.addDeletedObject(classification);
795
            return result;
796
        }
797
        if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){
798
//            TaxonNode root = classification.getRootNode();
799
//            result.includeResult(taxonNodeService.deleteTaxonNode(HibernateProxyHelper.deproxy(root), config));
800
//            result.addDeletedObject(classification);
801
            dao.delete(classification);
802
            result.addDeletedObject(classification);
803
            return result;
804
        }
805

    
806

    
807
        return result;
808
    }
809

    
810
    @Override
811
    public List<GroupedTaxonDTO> groupTaxaByHigherTaxon(List<UUID> originalTaxonUuids, UUID classificationUuid, Rank minRank, Rank maxRank){
812
        List<GroupedTaxonDTO> result = new ArrayList<>();
813

    
814
        //get treeindex for each taxonUUID
815
        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
816

    
817
        //build treeindex list (or tree)
818
        //TODO make it work with TreeIndex or move there
819
        List<String> treeIndexClosureStr = new ArrayList<>();
820
        for (TreeIndex treeIndex : taxonIdTreeIndexMap.values()){
821
            String[] splits = treeIndex.toString().substring(1).split(ITreeNode.separator);
822
            String currentIndex = ITreeNode.separator;
823
            for (String split : splits){
824
                if (split.equals("")){
825
                    continue;
826
                }
827
                currentIndex += split + ITreeNode.separator;
828
                if (!treeIndexClosureStr.contains(currentIndex) && !split.startsWith(ITreeNode.treePrefix)){
829
                    treeIndexClosureStr.add(currentIndex);
830
                }
831
            }
832
        }
833

    
834
        //get rank sortindex for all parent taxa with sortindex <= minRank and sortIndex >= maxRank (if available)
835
        Integer minRankOrderIndex = minRank == null ? null : minRank.getOrderIndex();
836
        Integer maxRankOrderIndex = maxRank == null ? null : maxRank.getOrderIndex();
837
        List<TreeIndex> treeIndexClosure = TreeIndex.NewListInstance(treeIndexClosureStr);
838

    
839
        Map<TreeIndex, Integer> treeIndexSortIndexMapTmp = taxonNodeDao.rankOrderIndexForTreeIndex(treeIndexClosure, minRankOrderIndex, maxRankOrderIndex);
840

    
841
        //remove all treeindex with "exists child in above map(and child.sortindex > xxx)
842
        List<TreeIndex> treeIndexList = TreeIndex.sort(treeIndexSortIndexMapTmp.keySet());
843

    
844
        Map<TreeIndex, Integer> treeIndexSortIndexMap = new HashMap<>();
845
        TreeIndex lastTreeIndex = null;
846
        for (TreeIndex treeIndex : treeIndexList){
847
            if (lastTreeIndex != null && lastTreeIndex.hasChild(treeIndex)){
848
                treeIndexSortIndexMap.remove(lastTreeIndex);
849
            }
850
            treeIndexSortIndexMap.put(treeIndex, treeIndexSortIndexMapTmp.get(treeIndex));
851
            lastTreeIndex = treeIndex;
852
        }
853

    
854
        //get taxonID for treeIndexes
855
        Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(treeIndexSortIndexMap.keySet());
856

    
857
        //fill result list
858
        for (UUID originalTaxonUuid : originalTaxonUuids){
859
            GroupedTaxonDTO item = new GroupedTaxonDTO();
860
            result.add(item);
861
            item.setTaxonUuid(originalTaxonUuid);
862
            TreeIndex groupTreeIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
863
            String groupIndexX = TreeIndex.toString(groupTreeIndex);
864
            while (groupTreeIndex != null){
865
                if (treeIndexTaxonIdMap.get(groupTreeIndex) != null){
866
                    UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupTreeIndex);
867
                    item.setGroupTaxonUuid(uuidAndLabel.getUuid());
868
                    item.setGroupTaxonName(uuidAndLabel.getTitleCache());
869
                    break;
870
                }else{
871
                    groupTreeIndex = groupTreeIndex.parent();
872
//                    int index = groupIndex.substring(0, groupIndex.length()-1).lastIndexOf(ITreeNode.separator);
873
//                    groupIndex = index < 0 ? null : groupIndex.substring(0, index+1);
874
                }
875
            }
876
        }
877

    
878
        return result;
879
    }
880

    
881
    @Override
882
    public List<GroupedTaxonDTO> groupTaxaByMarkedParents(List<UUID> originalTaxonUuids, UUID classificationUuid,
883
            MarkerType markerType, Boolean flag) {
884

    
885
        List<GroupedTaxonDTO> result = new ArrayList<>();
886

    
887
        //get treeindex for each taxonUUID
888
        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
889

    
890
        //get all marked tree indexes
891
        Set<TreeIndex> markedTreeIndexes = dao.getMarkedTreeIndexes(markerType, flag);
892

    
893
        Map<TreeIndex, TreeIndex> groupedMap = TreeIndex.group(markedTreeIndexes, taxonIdTreeIndexMap.values());
894
        Set<TreeIndex> notNullGroups = new HashSet<>(groupedMap.values());
895
        notNullGroups.remove(null);
896

    
897
        //get taxonInfo for treeIndexes
898
        Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(notNullGroups);
899

    
900
        //fill result list
901
        for (UUID originalTaxonUuid : originalTaxonUuids){
902
            GroupedTaxonDTO item = new GroupedTaxonDTO();
903
            result.add(item);
904
            item.setTaxonUuid(originalTaxonUuid);
905

    
906
            TreeIndex toBeGroupedTreeIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
907
            TreeIndex groupTreeIndex = groupedMap.get(toBeGroupedTreeIndex);
908
            UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupTreeIndex);
909
            if (uuidAndLabel != null){
910
                item.setGroupTaxonUuid(uuidAndLabel.getUuid());
911
                item.setGroupTaxonName(uuidAndLabel.getTitleCache());
912
            }
913
        }
914

    
915
        return result;
916
    }
917

    
918
    @Override
919
    public UUID getTaxonNodeUuidByTaxonUuid(UUID classificationUuid, UUID taxonUuid) {
920
        Map<UUID, UUID> map = dao.getTaxonNodeUuidByTaxonUuid(classificationUuid, Arrays.asList(taxonUuid));
921
        UUID taxonNodeUuid = map.get(taxonUuid);
922
        return taxonNodeUuid;
923
    }
924

    
925
    @Override
926
    public TaxonInContextDTO getTaxonInContext(UUID classificationUuid, UUID taxonBaseUuid,
927
            Boolean doChildren, Boolean doSynonyms, boolean includeUnpublished, List<UUID> ancestorMarkers,
928
            TaxonNodeSortMode sortMode) {
929

    
930
        TaxonInContextDTO result = new TaxonInContextDTO();
931

    
932
        TaxonBase<?> taxonBase = taxonDao.load(taxonBaseUuid);
933
        if (taxonBase == null){
934
            throw new EntityNotFoundException("Taxon with uuid " + taxonBaseUuid + " not found in datasource");
935
        }
936
        boolean isSynonym = false;
937
        Taxon acceptedTaxon;
938
        if (taxonBase.isInstanceOf(Synonym.class)){
939
            isSynonym = true;
940
            Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
941
            acceptedTaxon = synonym.getAcceptedTaxon();
942
            if (acceptedTaxon == null) {
943
                throw new EntityNotFoundException("Accepted taxon not found for synonym"  );
944
            }
945
            TaxonStatus taxonStatus = TaxonStatus.Synonym;
946
            if (synonym.getName()!= null && acceptedTaxon.getName() != null
947
                    && synonym.getName().getHomotypicalGroup().equals(acceptedTaxon.getName().getHomotypicalGroup())){
948
                taxonStatus = TaxonStatus.SynonymObjective;
949
            }
950
            result.setTaxonStatus(taxonStatus);
951

    
952
        }else{
953
            acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
954
            result.setTaxonStatus(TaxonStatus.Accepted);
955
        }
956
        UUID acceptedTaxonUuid = acceptedTaxon.getUuid();
957

    
958
        UUID taxonNodeUuid = getTaxonNodeUuidByTaxonUuid(classificationUuid, acceptedTaxonUuid);
959
        if (taxonNodeUuid == null) {
960
            throw new EntityNotFoundException("Taxon not found in classficiation with uuid " + classificationUuid + ". Either classification does not exist or does not contain taxon/synonym with uuid " + taxonBaseUuid );
961
        }
962
        result.setTaxonNodeUuid(taxonNodeUuid);
963

    
964
        //TODO make it a dao call
965
        Taxon parentTaxon = getParentTaxon(classificationUuid, acceptedTaxon);
966
        if (parentTaxon != null){
967
            result.setParentTaxonUuid(parentTaxon.getUuid());
968
            result.setParentTaxonLabel(parentTaxon.getTitleCache());
969
            if (parentTaxon.getName() != null){
970
                result.setParentNameLabel(parentTaxon.getName().getTitleCache());
971
            }
972
        }
973

    
974
        result.setTaxonUuid(taxonBaseUuid);
975
        result.setClassificationUuid(classificationUuid);
976
        if (taxonBase.getSec() != null){
977
            result.setSecundumUuid(taxonBase.getSec().getUuid());
978
            result.setSecundumLabel(taxonBase.getSec().getTitleCache());
979
        }
980
        result.setTaxonLabel(taxonBase.getTitleCache());
981

    
982
        TaxonName name = taxonBase.getName();
983
        result.setNameUuid(name.getUuid());
984
        result.setNameLabel(name.getTitleCache());
985
        result.setNameWithoutAuthor(name.getNameCache());
986
        result.setGenusOrUninomial(name.getGenusOrUninomial());
987
        result.setInfraGenericEpithet(name.getInfraGenericEpithet());
988
        result.setSpeciesEpithet(name.getSpecificEpithet());
989
        result.setInfraSpecificEpithet(name.getInfraSpecificEpithet());
990

    
991
        result.setAuthorship(name.getAuthorshipCache());
992

    
993
        Rank rank = name.getRank();
994
        if (rank != null){
995
            result.setRankUuid(rank.getUuid());
996
            String rankLabel = rank.getAbbreviation();
997
            if (StringUtils.isBlank(rankLabel)){
998
                rankLabel = rank.getLabel();
999
            }
1000
            result.setRankLabel(rankLabel);
1001
        }
1002

    
1003
        boolean recursive = false;
1004
        Integer pageSize = null;
1005
        Integer pageIndex = null;
1006
        Pager<TaxonNodeDto> children = taxonNodeService.pageChildNodesDTOs(taxonNodeUuid, recursive, includeUnpublished, doSynonyms,
1007
                sortMode, pageSize, pageIndex);
1008

    
1009
        //children
1010
        if(! isSynonym) {
1011
            for (TaxonNodeDto childDto : children.getRecords()){
1012
                if (doChildren && childDto.getTaxonStatus().equals(TaxonStatus.Accepted)){
1013
                    EntityDTO<Taxon> child = new EntityDTO<Taxon>(childDto.getTaxonUuid(), childDto.getTitleCache());
1014
                    result.addChild(child);
1015
                }else if (doSynonyms && childDto.getTaxonStatus().isSynonym()){
1016
                    EntityDTO<Synonym> child = new EntityDTO<>(childDto.getTaxonUuid(), childDto.getTitleCache());
1017
                    result.addSynonym(child);
1018
                }
1019
            }
1020
        }else{
1021
            result.setAcceptedTaxonUuid(acceptedTaxonUuid);
1022
            String nameTitel = acceptedTaxon.getName() == null ? null : acceptedTaxon.getName().getTitleCache();
1023
            result.setAcceptedTaxonLabel(acceptedTaxon.getTitleCache());
1024
            result.setAcceptedNameLabel(nameTitel);
1025
        }
1026

    
1027
        //marked ancestors
1028
        if (ancestorMarkers != null && !ancestorMarkers.isEmpty()){
1029
            @SuppressWarnings("rawtypes")
1030
            List<DefinedTermBase> markerTypesTerms = termDao.list(ancestorMarkers, pageSize, null, null, null);
1031
            List<MarkerType> markerTypes = new ArrayList<>();
1032
            for (DefinedTermBase<?> term : markerTypesTerms){
1033
                if (term.isInstanceOf(MarkerType.class)){
1034
                    markerTypes.add(CdmBase.deproxy(term, MarkerType.class));
1035
                }
1036
            }
1037
            if (! markerTypes.isEmpty()){
1038
                TaxonNode node = taxonNodeDao.findByUuid(taxonNodeUuid);
1039
                handleAncestorsForMarkersRecursive(result, markerTypes, node);
1040
            }
1041
        }
1042

    
1043
        return result;
1044
    }
1045

    
1046
    private Taxon getParentTaxon(UUID classificationUuid, Taxon acceptedTaxon) {
1047
        if (classificationUuid == null){
1048
            return null;
1049
        }
1050
        TaxonNode parent = null;
1051
        for (TaxonNode node : acceptedTaxon.getTaxonNodes()){
1052
            if (classificationUuid.equals(node.getClassification().getUuid())){
1053
                parent = node.getParent();
1054
            }
1055
        }
1056
        if (parent != null){
1057
            return parent.getTaxon();
1058
        }
1059
        return null;
1060
    }
1061

    
1062
    private void handleAncestorsForMarkersRecursive(TaxonInContextDTO result, List<MarkerType> markerTypes, TaxonNode node) {
1063
       for (MarkerType type : markerTypes){
1064
            Taxon taxon = node.getTaxon();
1065
            if (taxon != null && taxon.hasMarker(type, true)){
1066
                String label =  taxon.getName() == null? taxon.getTitleCache() : taxon.getName().getTitleCache();
1067
                MarkedEntityDTO<Taxon> dto = new MarkedEntityDTO<>(type, true, taxon.getUuid(), label);
1068
                result.addMarkedAncestor(dto);
1069
            }
1070
        }
1071
        TaxonNode parentNode = node.getParent();
1072
        if (parentNode != null){
1073
            handleAncestorsForMarkersRecursive(result, markerTypes, parentNode);
1074
        }
1075
    }
1076

    
1077
    @Override
1078
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1079
            Classification classification) {
1080
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, false);
1081
    }
1082

    
1083
    @Override
1084
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1085
            UUID classificationUuid) {
1086
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classificationUuid, false);
1087
    }
1088

    
1089
    @Override
1090
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1091
            UUID classificationUuid, Integer limit, String pattern) {
1092
        return  getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classificationUuid,  limit, pattern, false);
1093
    }
1094

    
1095
    @Override
1096
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1097
            Classification classification, Integer limit, String pattern) {
1098
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, limit, pattern, false);
1099
    }
1100

    
1101
    @Override
1102
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1103
            UUID classificationUuid, Integer limit, String pattern, boolean searchForClassifications) {
1104
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1105
                classificationUuid, limit, pattern, searchForClassifications, false);
1106
    }
1107
}
(5-5/100)