Project

General

Profile

Download (46.8 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.TaxonDeletionConfigurator;
38
import eu.etaxonomy.cdm.api.service.dto.EntityDTO;
39
import eu.etaxonomy.cdm.api.service.dto.GroupedTaxonDTO;
40
import eu.etaxonomy.cdm.api.service.dto.MarkedEntityDTO;
41
import eu.etaxonomy.cdm.api.service.dto.TaxonInContextDTO;
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.IProgressMonitor;
47
import eu.etaxonomy.cdm.exception.FilterException;
48
import eu.etaxonomy.cdm.exception.UnpublishedException;
49
import eu.etaxonomy.cdm.hibernate.HHH_9751_Util;
50
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
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.TaxonRelationshipType;
72
import eu.etaxonomy.cdm.model.term.DefinedTermBase;
73
import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
74
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
75
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
76
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
77
import eu.etaxonomy.cdm.persistence.dao.term.IDefinedTermDao;
78
import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
79
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
80
import eu.etaxonomy.cdm.persistence.dto.TaxonStatus;
81
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
82
import eu.etaxonomy.cdm.persistence.query.OrderHint;
83
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
84
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
85

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

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

    
98
    @Autowired
99
    private ITaxonNodeDao taxonNodeDao;
100

    
101
    @Autowired
102
    private ITaxonDao taxonDao;
103

    
104
    @Autowired
105
    private ITaxonNodeService taxonNodeService;
106

    
107
    @Autowired
108
    private IDefinedTermDao termDao;
109

    
110
    @Autowired
111
    private IBeanInitializer defaultBeanInitializer;
112

    
113
    @Override
114
    @Autowired
115
    protected void setDao(IClassificationDao dao) {
116
        this.dao = dao;
117
    }
118

    
119
    private Comparator<? super TaxonNode> taxonNodeComparator;
120

    
121
    @Autowired
122
    public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){
123
        this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;
124
    }
125

    
126
    @Override
127
    public TaxonNode loadTaxonNodeByTaxon(Taxon taxon, UUID classificationUuid, List<String> propertyPaths){
128
        Classification tree = dao.load(classificationUuid);
129
        TaxonNode node = tree.getNode(taxon);
130

    
131
        return loadTaxonNode(node.getUuid(), propertyPaths);
132
    }
133

    
134
    public TaxonNode loadTaxonNode(UUID taxonNodeUuid, List<String> propertyPaths){
135
        return taxonNodeDao.load(taxonNodeUuid, propertyPaths);
136
    }
137

    
138
    @Override
139
    @Transactional(readOnly = false)
140
    public UpdateResult cloneClassification(UUID classificationUuid,
141
    		String name, Reference sec, TaxonRelationshipType relationshipType) {
142
        UpdateResult result = new UpdateResult();
143
    	Classification classification = load(classificationUuid);
144
    	Classification clone = Classification.NewInstance(name);
145
    	clone.setReference(sec);
146

    
147
    	//clone taxa and taxon nodes
148
    	List<TaxonNode> childNodes = classification.getRootNode().getChildNodes();
149
    	for (TaxonNode taxonNode : childNodes) {
150
    		addChildTaxa(taxonNode, null, clone, relationshipType);
151
    	}
152
    	dao.saveOrUpdate(clone);
153
    	result.setCdmEntity(clone);
154
    	return result;
155
    }
156

    
157
    private void addChildTaxa(TaxonNode originalParentNode, TaxonNode cloneParentNode, Classification classification, TaxonRelationshipType relationshipType){
158
        Reference reference = classification.getReference();
159
    	Taxon cloneTaxon = (Taxon) HibernateProxyHelper.deproxy(originalParentNode.getTaxon(), Taxon.class).clone();
160
    	cloneTaxon.setSec(reference);
161
		String microReference = null;
162
		List<TaxonNode> originalChildNodes = originalParentNode.getChildNodes();
163
		HHH_9751_Util.removeAllNull(originalChildNodes);
164

    
165
		//add relation between taxa
166
		if (relationshipType != null){
167
		    cloneTaxon.addTaxonRelation(originalParentNode.getTaxon(), relationshipType, reference, microReference);
168
		}
169

    
170
		TaxonNode cloneChildNode = null;
171
    	//add taxon node to either parent node or classification (no parent node)
172
    	if(cloneParentNode==null){
173
    		cloneChildNode = classification.addChildTaxon(cloneTaxon, reference, microReference);
174
    	}
175
    	else{
176
    		cloneChildNode = cloneParentNode.addChildTaxon(cloneTaxon, reference, microReference);
177
    	}
178
    	taxonNodeDao.saveOrUpdate(cloneChildNode);
179
    	//add children
180
		for (TaxonNode originalChildNode : originalChildNodes) {
181
    		addChildTaxa(originalChildNode, cloneChildNode, classification, relationshipType);
182
    	}
183
    }
184

    
185
    @Override
186
    public List<TaxonNode> listRankSpecificRootNodes(Classification classification,
187
            TaxonNode subtree, Rank rank,
188
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
189
        return pageRankSpecificRootNodes(classification, subtree, rank, includeUnpublished, pageSize, pageIndex, propertyPaths).getRecords();
190
    }
191

    
192
    @Override
193
    public List<TaxonNodeDto> listRankSpecificRootNodeDtos(Classification classification, TaxonNode subtree,
194
            Rank rank, boolean includeUnpublished, Integer pageSize, Integer pageIndex, TaxonNodeDtoSortMode sortMode,
195
            List<String> propertyPaths) {
196
        List<TaxonNode> list = listRankSpecificRootNodes(classification, subtree, rank, includeUnpublished, pageSize, pageIndex, propertyPaths);
197
        return list.stream().filter(e ->  e != null).map(e -> new TaxonNodeDto(e)).sorted(sortMode.newComparator()).collect(Collectors.toList());
198
    }
199

    
200
    @Override
201
    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, Rank rank,
202
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
203
        return pageRankSpecificRootNodes(classification, null, rank, includeUnpublished, pageSize, pageIndex, propertyPaths);
204
    }
205

    
206
    @Override
207
    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, TaxonNode subtree, Rank rank,
208
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
209
        long[] numberOfResults = dao.countRankSpecificRootNodes(classification, subtree, includeUnpublished, rank);
210
        long totalNumberOfResults = numberOfResults[0] + (numberOfResults.length > 1 ? numberOfResults[1] : 0);
211

    
212
        List<TaxonNode> results = new ArrayList<>();
213

    
214
        if (AbstractPagerImpl.hasResultsInRange(totalNumberOfResults, pageIndex, pageSize)) { // no point checking again
215
            Integer limit = PagerUtils.limitFor(pageSize);
216
            Integer start = PagerUtils.startFor(pageSize, pageIndex);
217

    
218
            Integer remainingLimit = limit;
219
            int[] queryIndexes = rank == null ? new int[]{0} : new int[]{0,1};
220

    
221
            for(int queryIndex: queryIndexes) {
222
                if(start != null && start > numberOfResults[queryIndex]) {
223
                    // start in next query with new start value
224
                    start = start - (int)numberOfResults[queryIndex];
225
                    continue;
226
                }
227

    
228
                List<TaxonNode> perQueryResults = dao.listRankSpecificRootNodes(classification,
229
                        subtree, rank, includeUnpublished, remainingLimit,
230
                        start, propertyPaths, queryIndex);
231
                results.addAll(perQueryResults);
232
                if(remainingLimit != null ){
233
                    remainingLimit = remainingLimit - results.size();
234
                    if(remainingLimit <= 0) {
235
                        // no need to run further queries if first query returned enough items!
236
                        break;
237
                    }
238
                    // start at with fist item of next query to fetch the remaining items
239
                    start = 0;
240
                }
241
            }
242
        }
243
//        long start_t = System.currentTimeMillis();
244
        Collections.sort(results, taxonNodeComparator); // TODO is ordering during the hibernate query in the dao possible?
245
//        System.err.println("service.pageRankSpecificRootNodes() - Collections.sort(results,  taxonNodeComparator) " + (System.currentTimeMillis() - start_t));
246
        return new DefaultPagerImpl<>(pageIndex, totalNumberOfResults, pageSize, results);
247

    
248
    }
249

    
250
    @Override
251
    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, Rank baseRank,
252
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
253
        return loadTreeBranch(taxonNode, null, baseRank, includeUnpublished, propertyPaths);
254
    }
255

    
256
    /**
257
     * {@inheritDoc}
258
     */
259
    @Override
260
    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, TaxonNode subtree, Rank baseRank,
261
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
262

    
263
        TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
264
        if(baseRank != null){
265
            baseRank = (Rank) termDao.load(baseRank.getUuid());
266
        }
267
        if (!includeUnpublished && thisNode.getTaxon() != null && !thisNode.getTaxon().isPublish()){
268
            throw new UnpublishedException("Final taxon in tree branch is unpublished.");
269
        }
270

    
271
        List<TaxonNode> pathToRoot = new ArrayList<>();
272
        pathToRoot.add(thisNode);
273

    
274
        while(!thisNode.isTopmostNode()){
275
            //TODO why do we need to deproxy here?
276
            //     without this thisNode.getParent() will return NULL in
277
            //     some cases (environment dependend?) even if the parent exits
278
            TaxonNode parentNode = CdmBase.deproxy(thisNode).getParent();
279

    
280
            if(parentNode == null){
281
                throw new NullPointerException("Taxon node " + thisNode + " must have a parent since it is not top most");
282
            }
283
            if(parentNode.getTaxon() == null){
284
                throw new NullPointerException("The taxon associated with taxon node " + parentNode + " is NULL");
285
            }
286
            if(!includeUnpublished && !parentNode.getTaxon().isPublish()){
287
                throw new UnpublishedException("Some taxon in tree branch is unpublished.");
288
            }
289
            if(parentNode.getTaxon().getName() == null){
290
                throw new NullPointerException("The name of the taxon associated with taxonNode " + parentNode + " is NULL");
291
            }
292

    
293
            Rank parentNodeRank = (parentNode.getTaxon().getName() == null) ? null : parentNode.getTaxon().getName().getRank();
294
            // stop if the next parent is higher than the baseRank
295
            if(baseRank != null && parentNodeRank != null && baseRank.isLower(parentNodeRank)){
296
                break;
297
            }
298
            if((subtree!= null && !subtree.isAncestor(parentNode) )){
299
                break;
300
            }
301

    
302
            pathToRoot.add(parentNode);
303
            thisNode = parentNode;
304
        }
305

    
306
        // initialize and invert order of nodes in list
307
        defaultBeanInitializer.initializeAll(pathToRoot, propertyPaths);
308
        Collections.reverse(pathToRoot);
309

    
310
        return pathToRoot;
311
    }
312

    
313
    @Override
314
    public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification, Rank baseRank,
315
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
316
        return loadTreeBranchToTaxon(taxon, classification, null, baseRank, includeUnpublished, propertyPaths);
317
    }
318

    
319
    @Override
320
    public List<TaxonNodeDto> loadTreeBranchDTOsToTaxon(Taxon taxon, Classification classification,
321
            TaxonNode subtree, Rank baseRank,
322
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException {
323
        List<TaxonNode> list = loadTreeBranchToTaxon(taxon, classification, subtree, baseRank, includeUnpublished, propertyPaths);
324
        return list.stream().map(e -> new TaxonNodeDto(e)).collect(Collectors.toList());
325
    }
326

    
327
    @Override
328
    public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification,
329
            TaxonNode subtree, Rank baseRank,
330
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
331

    
332
        UUID nodeUuid = getTaxonNodeUuidByTaxonUuid(classification.getUuid(), taxon.getUuid());
333
        TaxonNode node = taxonNodeService.find(nodeUuid);
334
        if(node == null){
335
            logger.warn("The specified taxon is not found in the given tree.");
336
            return null;
337
        }else if (subtree != null && !node.isDescendant(subtree)){
338
            //TODO handle as exception? E.g. FilterException, AccessDeniedException?
339
            logger.warn("The specified taxon is not found for the given subtree.");
340
            return null;
341
        }
342

    
343
        return loadTreeBranch(node, subtree, baseRank, includeUnpublished, propertyPaths);
344
    }
345

    
346

    
347
    @Override
348
    public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
349
            List<String> propertyPaths) {
350
        taxonNode = taxonNodeDao.load(taxonNode.getUuid());
351
        List<TaxonNode> childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
352
        defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
353
        Collections.sort(childNodes, taxonNodeComparator);
354
        return childNodes;
355
    }
356

    
357
    @Override
358
    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid,
359
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths){
360
        try {
361
            return listChildNodesOfTaxon(taxonUuid, classificationUuid, null, includeUnpublished, pageSize, pageIndex, propertyPaths);
362
        } catch (FilterException e) {
363
            throw new RuntimeException(e);  //this should not happen as filter is null
364
        }
365
    }
366

    
367
    @Override
368
    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid, UUID subtreeUuid,
369
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) throws FilterException{
370

    
371
        Classification classification = dao.load(classificationUuid);
372
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
373
        TaxonNode subtree = taxonNodeDao.load(subtreeUuid);
374
        if (subtreeUuid != null && subtree == null){
375
            throw new FilterException("Taxon node for subtree filter can not be found in database", true);
376
        }
377

    
378
        List<TaxonNode> results = dao.listChildrenOf(
379
                taxon, classification, subtree, includeUnpublished, pageSize, pageIndex, propertyPaths);
380
        Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
381
        return results;
382
    }
383

    
384
    @Override
385
    public List<TaxonNodeDto> listChildNodeDtosOfTaxon(UUID taxonUuid, UUID classificationUuid, UUID subtreeUuid, boolean includeUnpublished,
386
            Integer pageSize, Integer pageIndex, TaxonNodeDtoSortMode sortMode, List<String> propertyPaths) throws FilterException{
387
        Classification classification = dao.load(classificationUuid);
388
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
389
        TaxonNode subtree = taxonNodeDao.load(subtreeUuid);
390
        if (subtreeUuid != null && subtree == null){
391
            throw new FilterException("Taxon node for subtree filter can not be found in database", true);
392
        }
393

    
394
        List<TaxonNode> results = dao.listChildrenOf(
395
                taxon, classification, subtree, includeUnpublished, pageSize, pageIndex, propertyPaths);
396
        Comparator<TaxonNodeDto> comparator = sortMode.newComparator();
397
        // TODO order during the hibernate query in the dao?
398
        List<TaxonNodeDto> dtos = results.stream().map(e -> new TaxonNodeDto(e)).sorted(comparator).collect(Collectors.toList());
399
        return dtos;
400
    }
401

    
402
    @Override
403
    public Pager<TaxonNode> pageSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, boolean includeUnpublished,
404
            Integer pageSize, Integer pageIndex, List<String> propertyPaths){
405

    
406
        Classification classification = dao.load(classificationUuid);
407
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
408

    
409
        long numberOfResults = dao.countSiblingsOf(taxon, classification, includeUnpublished);
410

    
411
        List<TaxonNode> results;
412
        if(PagerUtils.hasResultsInRange(numberOfResults, pageIndex, pageSize)) {
413
            results = dao.listSiblingsOf(taxon, classification, includeUnpublished, pageSize, pageIndex, propertyPaths);
414
            Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
415
        } else {
416
            results = new ArrayList<>();
417
        }
418

    
419
        return new DefaultPagerImpl<>(pageIndex, numberOfResults, pageSize, results);
420
    }
421

    
422
    @Override
423
    public List<TaxonNode> listSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, boolean includeUnpublished,
424
            Integer pageSize, Integer pageIndex, List<String> propertyPaths){
425

    
426
        Pager<TaxonNode> pager = pageSiblingsOfTaxon(taxonUuid, classificationUuid, includeUnpublished, pageSize, pageIndex, propertyPaths);
427
        return pager.getRecords();
428
    }
429

    
430
    @Override
431
    public ITaxonTreeNode getTreeNodeByUuid(UUID uuid){
432
        ITaxonTreeNode treeNode = taxonNodeDao.findByUuid(uuid);
433
        if(treeNode == null){
434
            treeNode = dao.findByUuid(uuid);
435
        }
436

    
437
        return treeNode;
438
    }
439

    
440
    @Override
441
    public TaxonNode getRootNode(UUID classificationUuid){
442
        return dao.getRootNode(classificationUuid);
443
    }
444

    
445
    @Override
446
    public List<Classification> listClassifications(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
447
        return dao.list(limit, start, orderHints, propertyPaths);
448
    }
449

    
450
    @Override
451
    public UUID removeTaxonNode(TaxonNode taxonNode) {
452
        return taxonNodeDao.delete(taxonNode);
453
    }
454
    @Override
455
    public UUID removeTreeNode(ITaxonTreeNode treeNode) {
456
        if(treeNode instanceof Classification){
457
            return dao.delete((Classification) treeNode);
458
        }else if(treeNode instanceof TaxonNode){
459
            return taxonNodeDao.delete((TaxonNode)treeNode);
460
        }
461
        return null;
462
    }
463
    @Override
464
    public UUID saveTaxonNode(TaxonNode taxonNode) {
465
        return taxonNodeDao.save(taxonNode).getUuid();
466
    }
467

    
468
    @Override
469
    public Map<UUID, TaxonNode> saveTaxonNodeAll(
470
            Collection<TaxonNode> taxonNodeCollection) {
471
        return taxonNodeDao.saveAll(taxonNodeCollection);
472
    }
473

    
474
    @Override
475
    public UUID saveClassification(Classification classification) {
476

    
477
       taxonNodeDao.saveOrUpdateAll(classification.getAllNodes());
478
       UUID result =dao.saveOrUpdate(classification);
479
       return result;
480
    }
481

    
482
    @Override
483
    public UUID saveTreeNode(ITaxonTreeNode treeNode) {
484
        if(treeNode instanceof Classification){
485
            return dao.save((Classification) treeNode).getUuid();
486
        }else if(treeNode instanceof TaxonNode){
487
            return taxonNodeDao.save((TaxonNode)treeNode).getUuid();
488
        }
489
        return null;
490
    }
491

    
492
    @Override
493
    public List<TaxonNode> getAllNodes(){
494
        return taxonNodeDao.list(null,null);
495
    }
496

    
497
    @Override
498
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, Integer limit, String pattern, boolean searchForClassifications) {
499
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid),  limit, pattern, searchForClassifications);
500
    }
501

    
502
    @Override
503
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification,  Integer limit, String pattern, boolean searchForClassifications) {
504
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification,  limit, pattern, searchForClassifications);
505
    }
506

    
507
    @Override
508
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, boolean searchForClassifications ) {
509
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), null, null, searchForClassifications);
510
    }
511

    
512
    @Override
513
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, boolean searchForClassifications ) {
514
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, null, null, searchForClassifications);
515
    }
516

    
517
    @Override
518
    public List<UuidAndTitleCache<Classification>> getUuidAndTitleCache(Integer limit, String pattern) {
519
        return dao.getUuidAndTitleCache(limit, pattern);
520
    }
521

    
522
    @Override
523
    public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(
524
            TaxonNode taxonNode, List<String> propertyPaths, int size,
525
            int height, int widthOrDuration, String[] mimeTypes) {
526

    
527
        TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<>();
528
        List<MediaRepresentation> mediaRepresentations = new ArrayList<>();
529

    
530
        //add all media of the children to the result map
531
        if (taxonNode != null){
532

    
533
            List<TaxonNode> nodes = new ArrayList<>();
534

    
535
            nodes.add(loadTaxonNode(taxonNode.getUuid(), propertyPaths));
536
            nodes.addAll(loadChildNodesOfTaxonNode(taxonNode, propertyPaths));
537

    
538
            for(TaxonNode node : nodes){
539
                Taxon taxon = node.getTaxon();
540
                for (TaxonDescription taxonDescription: taxon.getDescriptions()){
541
                    for (DescriptionElementBase descriptionElement: taxonDescription.getElements()){
542
                        for(Media media : descriptionElement.getMedia()){
543
                            //find the best matching representation
544
                            mediaRepresentations.add(MediaUtils.findBestMatchingRepresentation(media,null, size, height, widthOrDuration, mimeTypes, MediaUtils.MissingValueStrategy.MAX));
545
                        }
546
                    }
547
                }
548
                result.put(taxon.getUuid(), mediaRepresentations);
549
            }
550
        }
551

    
552
        return result;
553
    }
554

    
555
    @Override
556
    @Transactional(readOnly = false)
557
    public UpdateResult updateCaches(Class<? extends Classification> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Classification> cacheStrategy, IProgressMonitor monitor) {
558
        if (clazz == null){
559
            clazz = Classification.class;
560
        }
561
        return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
562
    }
563

    
564
    /**
565
     *
566
     * @param allNodesOfClassification
567
     * @return null - if  allNodesOfClassification is empty <br>
568
     */
569

    
570
    private Map<String, List<TaxonNode>> getSortedGenusList(Collection<TaxonNode> allNodesOfClassification){
571

    
572
    	if(allNodesOfClassification == null || allNodesOfClassification.isEmpty()){
573
    		return null;
574
    	}
575
    	Map<String, List<TaxonNode>> sortedGenusMap = new HashMap<>();
576
    	for(TaxonNode node:allNodesOfClassification){
577
    		Taxon taxon = node.getTaxon();
578
    		INonViralName name = taxon.getName();
579
    		String genusOrUninomial = name.getGenusOrUninomial();
580
    		//if rank unknown split string and take first word
581
    		if(genusOrUninomial == null){
582
    			String titleCache = taxon.getTitleCache();
583
    			String[] split = titleCache.split("\\s+");
584
    			for(String s:split){
585
    				genusOrUninomial = s;
586
    				break;
587
    			}
588
    		}
589
    		//if node has children
590

    
591
    		//retrieve list from map if not create List
592
    		if(sortedGenusMap.containsKey(genusOrUninomial)){
593
    			List<TaxonNode> list = sortedGenusMap.get(genusOrUninomial);
594
    			list.add(node);
595
    			sortedGenusMap.put(genusOrUninomial, list);
596
    		}else{
597
    			//create List for genus
598
    			List<TaxonNode> list = new ArrayList<>();
599
    			list.add(node);
600
    			sortedGenusMap.put(genusOrUninomial, list);
601
    		}
602
    	}
603
    	return sortedGenusMap;
604
    }
605

    
606
    /**
607
     *
608
     * creates new Classification and parent TaxonNodes at genus level
609
     *
610
     *
611
     * @param map GenusMap which holds a name (Genus) and all the same Taxa as a list
612
     * @param classification you want to improve the hierarchy (will not be modified)
613
     * @param configurator to change certain settings, if null then standard settings will be taken
614
     * @return new classification with parentNodes for each entry in the map
615
     */
616
    @SuppressWarnings({ "unchecked" })
617
	@Transactional(readOnly = false)
618
	@Override
619
    public UpdateResult createHierarchyInClassification(Classification classification, CreateHierarchyForClassificationConfigurator configurator){
620
        UpdateResult result = new UpdateResult();
621
    	classification = dao.findByUuid(classification.getUuid());
622
    	Map<String, List<TaxonNode>> map = getSortedGenusList(classification.getAllNodes());
623

    
624
    	final String APPENDIX = "repaired";
625
    	String titleCache = StringUtils.isBlank(classification.getTitleCache()) ? " " : classification.getTitleCache() ;
626
    	//TODO classification clone???
627
    	Classification newClassification = Classification.NewInstance(titleCache +" "+ APPENDIX);
628
    	newClassification.setReference(classification.getReference());
629

    
630
    	for(Map.Entry<String, List<TaxonNode>> entry:map.entrySet()){
631
    		String genus = entry.getKey();
632
    		List<TaxonNode> listOfTaxonNodes = entry.getValue();
633
    		TaxonNode parentNode = null;
634
    		//Search for genus in list
635
    		for(TaxonNode tNode:listOfTaxonNodes){
636
    			//take that taxonNode as parent and remove from list with all it possible children
637
    			//FIXME NPE for name
638
    			TaxonName name = tNode.getTaxon().getName();
639
    			if(name.getNameCache().equalsIgnoreCase(genus)){
640
    				TaxonNode clone = (TaxonNode) tNode.clone();
641
    				if(!tNode.hasChildNodes()){
642
    					//FIXME remove classification
643
//    					parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
644
    					parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
645
    					//remove taxonNode from list because just added to classification
646
    					result.addUpdatedObject(tNode);
647
    					listOfTaxonNodes.remove(tNode);
648
    				}else{
649
    					//get all childNodes
650
    					//save prior Hierarchy and remove them from the list
651
    					List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tNode, clone, result);
652
//    					parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
653
      					//FIXME remove classification
654
    					parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
655
    					//remove taxonNode from list because just added to classification
656
    					result.addUpdatedObject(tNode);
657
    					listOfTaxonNodes.remove(tNode);
658
    					if(copyAllChildrenToTaxonNode != null){
659
    						listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
660
    					}
661
    				}
662
    				break;
663
    			}
664
    		}
665
    		if(parentNode == null){
666
    			//if no match found in list, create parentNode
667
    			NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
668
    			TaxonName TaxonName = (TaxonName)parser.parseFullName(genus);
669
    			//TODO Sec via configurator
670
    			Taxon taxon = Taxon.NewInstance(TaxonName, null);
671
    			parentNode = newClassification.addChildTaxon(taxon, 0, null, null);
672
    			result.addUpdatedObject(parentNode);
673
    		}
674
    		//iterate over the rest of the list
675
    		for(TaxonNode tn : listOfTaxonNodes){
676
    			//if TaxonNode has a parent and this is not the classification then skip it
677
    			//and add to new classification via the parentNode as children of it
678
    			//this should assures to keep the already existing hierarchy
679
    			//FIXME: Assert is not rootnode --> entrypoint is not classification in future but rather rootNode
680

    
681
    			if(!tn.isTopmostNode()){
682
    				continue; //skip to next taxonNode
683
    			}
684

    
685
    			TaxonNode clone = (TaxonNode) tn.clone();
686
    			//FIXME: citation from node
687
    			//TODO: addchildNode without citation and references
688
//    			TaxonNode taxonNode = parentNode.addChildNode(clone, classification.getCitation(), classification.getMicroReference());
689
    			TaxonNode taxonNode = parentNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
690
    			result.addUnChangedObject(clone);
691
    			if(tn.hasChildNodes()){
692
    				//save hierarchy in new classification
693
    				List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tn, taxonNode, result);
694
    				if(copyAllChildrenToTaxonNode != null){
695
    					listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
696
    				}
697
    			}
698
    		}
699
    	}
700
    	dao.saveOrUpdate(newClassification);
701
    	result.setCdmEntity(newClassification);
702
    	return result;
703
    }
704

    
705
    /**
706
     *
707
     * recursive method to get all childnodes of taxonNode in classification.
708
     *
709
     * @param classification just for References and Citation, can be null
710
     * @param copyFromNode TaxonNode with Children
711
     * @param copyToNode TaxonNode which will receive the children
712
     * @return List of ChildNode which has been added. If node has no children returns null
713
     */
714
   private List<TaxonNode> copyAllChildrenToTaxonNode(TaxonNode copyFromNode, TaxonNode copyToNode, UpdateResult result) {
715
		List<TaxonNode> childNodes;
716
		if(!copyFromNode.hasChildNodes()){
717
			return null;
718
		}else{
719
			childNodes = copyFromNode.getChildNodes();
720
		}
721
		for(TaxonNode childNode:childNodes){
722
			TaxonNode clone = (TaxonNode) childNode.clone();
723
			result.addUnChangedObject(clone);
724
			if(childNode.hasChildNodes()){
725
				copyAllChildrenToTaxonNode(childNode, clone, result);
726
			}
727
			//FIXME: citation from node instead of classification
728
//			copyToNode.addChildNode(clone,classification.getCitation(), classification.getMicroReference());
729
			copyToNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
730
		}
731
		return childNodes;
732
	}
733

    
734
    @Override
735
    public ClassificationLookupDTO classificationLookup(Classification classification) {
736
        return dao.classificationLookup(classification);
737
    }
738

    
739
    @Override
740
    @Transactional
741
    public DeleteResult delete(UUID classificationUuid, TaxonDeletionConfigurator config){
742
        DeleteResult result = new DeleteResult();
743
        Classification classification = dao.findByUuid(classificationUuid);
744
        if (classification == null){
745
            result.addException(new IllegalArgumentException("The classification does not exist in database."));
746
            result.setAbort();
747
            return result;
748
        }
749
        if (!classification.hasChildNodes()){
750
            dao.delete(classification);
751
            result.addDeletedObject(classification);
752
            return result;
753
        }
754
        if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){
755
//            TaxonNode root = classification.getRootNode();
756
//            result.includeResult(taxonNodeService.deleteTaxonNode(HibernateProxyHelper.deproxy(root), config));
757
//            result.addDeletedObject(classification);
758
            dao.delete(classification);
759
            result.addDeletedObject(classification);
760
            return result;
761
        }
762

    
763

    
764
        return result;
765
    }
766

    
767
    @Override
768
    public List<GroupedTaxonDTO> groupTaxaByHigherTaxon(List<UUID> originalTaxonUuids, UUID classificationUuid, Rank minRank, Rank maxRank){
769
        List<GroupedTaxonDTO> result = new ArrayList<>();
770

    
771
        //get treeindex for each taxonUUID
772
        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
773

    
774
        //build treeindex list (or tree)
775
        //TODO make it work with TreeIndex or move there
776
        List<String> treeIndexClosureStr = new ArrayList<>();
777
        for (TreeIndex treeIndex : taxonIdTreeIndexMap.values()){
778
            String[] splits = treeIndex.toString().substring(1).split(ITreeNode.separator);
779
            String currentIndex = ITreeNode.separator;
780
            for (String split : splits){
781
                if (split.equals("")){
782
                    continue;
783
                }
784
                currentIndex += split + ITreeNode.separator;
785
                if (!treeIndexClosureStr.contains(currentIndex) && !split.startsWith(ITreeNode.treePrefix)){
786
                    treeIndexClosureStr.add(currentIndex);
787
                }
788
            }
789
        }
790

    
791
        //get rank sortindex for all parent taxa with sortindex <= minRank and sortIndex >= maxRank (if available)
792
        Integer minRankOrderIndex = minRank == null ? null : minRank.getOrderIndex();
793
        Integer maxRankOrderIndex = maxRank == null ? null : maxRank.getOrderIndex();
794
        List<TreeIndex> treeIndexClosure = TreeIndex.NewListInstance(treeIndexClosureStr);
795

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

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

    
801
        Map<TreeIndex, Integer> treeIndexSortIndexMap = new HashMap<>();
802
        TreeIndex lastTreeIndex = null;
803
        for (TreeIndex treeIndex : treeIndexList){
804
            if (lastTreeIndex != null && lastTreeIndex.hasChild(treeIndex)){
805
                treeIndexSortIndexMap.remove(lastTreeIndex);
806
            }
807
            treeIndexSortIndexMap.put(treeIndex, treeIndexSortIndexMapTmp.get(treeIndex));
808
            lastTreeIndex = treeIndex;
809
        }
810

    
811
        //get taxonID for treeIndexes
812
        Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(treeIndexSortIndexMap.keySet());
813

    
814
        //fill result list
815
        for (UUID originalTaxonUuid : originalTaxonUuids){
816
            GroupedTaxonDTO item = new GroupedTaxonDTO();
817
            result.add(item);
818
            item.setTaxonUuid(originalTaxonUuid);
819
            TreeIndex groupTreeIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
820
            String groupIndexX = TreeIndex.toString(groupTreeIndex);
821
            while (groupTreeIndex != null){
822
                if (treeIndexTaxonIdMap.get(groupTreeIndex) != null){
823
                    UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupTreeIndex);
824
                    item.setGroupTaxonUuid(uuidAndLabel.getUuid());
825
                    item.setGroupTaxonName(uuidAndLabel.getTitleCache());
826
                    break;
827
                }else{
828
                    groupTreeIndex = groupTreeIndex.parent();
829
//                    int index = groupIndex.substring(0, groupIndex.length()-1).lastIndexOf(ITreeNode.separator);
830
//                    groupIndex = index < 0 ? null : groupIndex.substring(0, index+1);
831
                }
832
            }
833
        }
834

    
835
        return result;
836
    }
837

    
838
    @Override
839
    public List<GroupedTaxonDTO> groupTaxaByMarkedParents(List<UUID> originalTaxonUuids, UUID classificationUuid,
840
            MarkerType markerType, Boolean flag) {
841

    
842
        List<GroupedTaxonDTO> result = new ArrayList<>();
843

    
844
        //get treeindex for each taxonUUID
845
        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
846

    
847
        //get all marked tree indexes
848
        Set<TreeIndex> markedTreeIndexes = dao.getMarkedTreeIndexes(markerType, flag);
849

    
850
        Map<TreeIndex, TreeIndex> groupedMap = TreeIndex.group(markedTreeIndexes, taxonIdTreeIndexMap.values());
851
        Set<TreeIndex> notNullGroups = new HashSet<>(groupedMap.values());
852
        notNullGroups.remove(null);
853

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

    
857
        //fill result list
858
        for (UUID originalTaxonUuid : originalTaxonUuids){
859
            GroupedTaxonDTO item = new GroupedTaxonDTO();
860
            result.add(item);
861
            item.setTaxonUuid(originalTaxonUuid);
862

    
863
            TreeIndex toBeGroupedTreeIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
864
            TreeIndex groupTreeIndex = groupedMap.get(toBeGroupedTreeIndex);
865
            UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupTreeIndex);
866
            if (uuidAndLabel != null){
867
                item.setGroupTaxonUuid(uuidAndLabel.getUuid());
868
                item.setGroupTaxonName(uuidAndLabel.getTitleCache());
869
            }
870
        }
871

    
872
        return result;
873
    }
874

    
875
    @Override
876
    public UUID getTaxonNodeUuidByTaxonUuid(UUID classificationUuid, UUID taxonUuid) {
877
        Map<UUID, UUID> map = dao.getTaxonNodeUuidByTaxonUuid(classificationUuid, Arrays.asList(taxonUuid));
878
        UUID taxonNodeUuid = map.get(taxonUuid);
879
        return taxonNodeUuid;
880
    }
881

    
882
    @Override
883
    public TaxonInContextDTO getTaxonInContext(UUID classificationUuid, UUID taxonBaseUuid,
884
            Boolean doChildren, Boolean doSynonyms, boolean includeUnpublished, List<UUID> ancestorMarkers,
885
            TaxonNodeSortMode sortMode) {
886

    
887
        TaxonInContextDTO result = new TaxonInContextDTO();
888

    
889
        TaxonBase<?> taxonBase = taxonDao.load(taxonBaseUuid);
890
        if (taxonBase == null){
891
            throw new EntityNotFoundException("Taxon with uuid " + taxonBaseUuid + " not found in datasource");
892
        }
893
        boolean isSynonym = false;
894
        Taxon acceptedTaxon;
895
        if (taxonBase.isInstanceOf(Synonym.class)){
896
            isSynonym = true;
897
            Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
898
            acceptedTaxon = synonym.getAcceptedTaxon();
899
            if (acceptedTaxon == null) {
900
                throw new EntityNotFoundException("Accepted taxon not found for synonym"  );
901
            }
902
            TaxonStatus taxonStatus = TaxonStatus.Synonym;
903
            if (synonym.getName()!= null && acceptedTaxon.getName() != null
904
                    && synonym.getName().getHomotypicalGroup().equals(acceptedTaxon.getName().getHomotypicalGroup())){
905
                taxonStatus = TaxonStatus.SynonymObjective;
906
            }
907
            result.setTaxonStatus(taxonStatus);
908

    
909
        }else{
910
            acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
911
            result.setTaxonStatus(TaxonStatus.Accepted);
912
        }
913
        UUID acceptedTaxonUuid = acceptedTaxon.getUuid();
914

    
915
        UUID taxonNodeUuid = getTaxonNodeUuidByTaxonUuid(classificationUuid, acceptedTaxonUuid);
916
        if (taxonNodeUuid == null) {
917
            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 );
918
        }
919
        result.setTaxonNodeUuid(taxonNodeUuid);
920

    
921
        //TODO make it a dao call
922
        Taxon parentTaxon = getParentTaxon(classificationUuid, acceptedTaxon);
923
        if (parentTaxon != null){
924
            result.setParentTaxonUuid(parentTaxon.getUuid());
925
            result.setParentTaxonLabel(parentTaxon.getTitleCache());
926
            if (parentTaxon.getName() != null){
927
                result.setParentNameLabel(parentTaxon.getName().getTitleCache());
928
            }
929
        }
930

    
931
        result.setTaxonUuid(taxonBaseUuid);
932
        result.setClassificationUuid(classificationUuid);
933
        if (taxonBase.getSec() != null){
934
            result.setSecundumUuid(taxonBase.getSec().getUuid());
935
            result.setSecundumLabel(taxonBase.getSec().getTitleCache());
936
        }
937
        result.setTaxonLabel(taxonBase.getTitleCache());
938

    
939
        TaxonName name = taxonBase.getName();
940
        result.setNameUuid(name.getUuid());
941
        result.setNameLabel(name.getTitleCache());
942
        result.setNameWithoutAuthor(name.getNameCache());
943
        result.setGenusOrUninomial(name.getGenusOrUninomial());
944
        result.setInfraGenericEpithet(name.getInfraGenericEpithet());
945
        result.setSpeciesEpithet(name.getSpecificEpithet());
946
        result.setInfraSpecificEpithet(name.getInfraSpecificEpithet());
947

    
948
        result.setAuthorship(name.getAuthorshipCache());
949

    
950
        Rank rank = name.getRank();
951
        if (rank != null){
952
            result.setRankUuid(rank.getUuid());
953
            String rankLabel = rank.getAbbreviation();
954
            if (StringUtils.isBlank(rankLabel)){
955
                rankLabel = rank.getLabel();
956
            }
957
            result.setRankLabel(rankLabel);
958
        }
959

    
960
        boolean recursive = false;
961
        Integer pageSize = null;
962
        Integer pageIndex = null;
963
        Pager<TaxonNodeDto> children = taxonNodeService.pageChildNodesDTOs(taxonNodeUuid, recursive, includeUnpublished, doSynonyms,
964
                sortMode, pageSize, pageIndex);
965

    
966
        //children
967
        if(! isSynonym) {
968
            for (TaxonNodeDto childDto : children.getRecords()){
969
                if (doChildren && childDto.getTaxonStatus().equals(TaxonStatus.Accepted)){
970
                    EntityDTO<Taxon> child = new EntityDTO<Taxon>(childDto.getTaxonUuid(), childDto.getTitleCache());
971
                    result.addChild(child);
972
                }else if (doSynonyms && childDto.getTaxonStatus().isSynonym()){
973
                    EntityDTO<Synonym> child = new EntityDTO<>(childDto.getTaxonUuid(), childDto.getTitleCache());
974
                    result.addSynonym(child);
975
                }
976
            }
977
        }else{
978
            result.setAcceptedTaxonUuid(acceptedTaxonUuid);
979
            String nameTitel = acceptedTaxon.getName() == null ? null : acceptedTaxon.getName().getTitleCache();
980
            result.setAcceptedTaxonLabel(acceptedTaxon.getTitleCache());
981
            result.setAcceptedNameLabel(nameTitel);
982
        }
983

    
984
        //marked ancestors
985
        if (ancestorMarkers != null && !ancestorMarkers.isEmpty()){
986
            @SuppressWarnings("rawtypes")
987
            List<DefinedTermBase> markerTypesTerms = termDao.list(ancestorMarkers, pageSize, null, null, null);
988
            List<MarkerType> markerTypes = new ArrayList<>();
989
            for (DefinedTermBase<?> term : markerTypesTerms){
990
                if (term.isInstanceOf(MarkerType.class)){
991
                    markerTypes.add(CdmBase.deproxy(term, MarkerType.class));
992
                }
993
            }
994
            if (! markerTypes.isEmpty()){
995
                TaxonNode node = taxonNodeDao.findByUuid(taxonNodeUuid);
996
                handleAncestorsForMarkersRecursive(result, markerTypes, node);
997
            }
998
        }
999

    
1000
        return result;
1001
    }
1002

    
1003
    private Taxon getParentTaxon(UUID classificationUuid, Taxon acceptedTaxon) {
1004
        if (classificationUuid == null){
1005
            return null;
1006
        }
1007
        TaxonNode parent = null;
1008
        for (TaxonNode node : acceptedTaxon.getTaxonNodes()){
1009
            if (classificationUuid.equals(node.getClassification().getUuid())){
1010
                parent = node.getParent();
1011
            }
1012
        }
1013
        if (parent != null){
1014
            return parent.getTaxon();
1015
        }
1016
        return null;
1017
    }
1018

    
1019
    private void handleAncestorsForMarkersRecursive(TaxonInContextDTO result, List<MarkerType> markerTypes, TaxonNode node) {
1020
       for (MarkerType type : markerTypes){
1021
            Taxon taxon = node.getTaxon();
1022
            if (taxon != null && taxon.hasMarker(type, true)){
1023
                String label =  taxon.getName() == null? taxon.getTitleCache() : taxon.getName().getTitleCache();
1024
                MarkedEntityDTO<Taxon> dto = new MarkedEntityDTO<>(type, true, taxon.getUuid(), label);
1025
                result.addMarkedAncestor(dto);
1026
            }
1027
        }
1028
        TaxonNode parentNode = node.getParent();
1029
        if (parentNode != null){
1030
            handleAncestorsForMarkersRecursive(result, markerTypes, parentNode);
1031
        }
1032
    }
1033

    
1034
    @Override
1035
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1036
            Classification classification) {
1037
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, false);
1038
    }
1039

    
1040
    @Override
1041
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1042
            UUID classificationUuid) {
1043
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classificationUuid, false);
1044
    }
1045

    
1046
    @Override
1047
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1048
            UUID classificationUuid, Integer limit, String pattern) {
1049
        return  getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classificationUuid,  limit, pattern, false);
1050
    }
1051

    
1052
    @Override
1053
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1054
            Classification classification, Integer limit, String pattern) {
1055
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, limit, pattern, false);
1056
    }
1057
}
(5-5/100)