Project

General

Profile

Download (44.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.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.model.common.CdmBase;
50
import eu.etaxonomy.cdm.model.common.ITreeNode;
51
import eu.etaxonomy.cdm.model.common.MarkerType;
52
import eu.etaxonomy.cdm.model.common.TreeIndex;
53
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
54
import eu.etaxonomy.cdm.model.description.TaxonDescription;
55
import eu.etaxonomy.cdm.model.media.Media;
56
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
57
import eu.etaxonomy.cdm.model.media.MediaUtils;
58
import eu.etaxonomy.cdm.model.name.INonViralName;
59
import eu.etaxonomy.cdm.model.name.Rank;
60
import eu.etaxonomy.cdm.model.name.TaxonName;
61
import eu.etaxonomy.cdm.model.taxon.Classification;
62
import eu.etaxonomy.cdm.model.taxon.ITaxonNodeComparator;
63
import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
64
import eu.etaxonomy.cdm.model.taxon.Synonym;
65
import eu.etaxonomy.cdm.model.taxon.Taxon;
66
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
67
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
68
import eu.etaxonomy.cdm.model.term.DefinedTermBase;
69
import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
70
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
71
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
72
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
73
import eu.etaxonomy.cdm.persistence.dao.term.IDefinedTermDao;
74
import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
75
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
76
import eu.etaxonomy.cdm.persistence.dto.TaxonStatus;
77
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
78
import eu.etaxonomy.cdm.persistence.query.OrderHint;
79
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
80
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
81

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

    
92
    private static final Logger logger = Logger.getLogger(ClassificationServiceImpl.class);
93

    
94
    @Autowired
95
    private ITaxonNodeDao taxonNodeDao;
96

    
97
    @Autowired
98
    private ITaxonDao taxonDao;
99

    
100
    @Autowired
101
    private ITaxonNodeService taxonNodeService;
102

    
103
    @Autowired
104
    private IDefinedTermDao termDao;
105

    
106
    @Autowired
107
    private IBeanInitializer defaultBeanInitializer;
108

    
109
    @Override
110
    @Autowired
111
    protected void setDao(IClassificationDao dao) {
112
        this.dao = dao;
113
    }
114

    
115
    private Comparator<? super TaxonNode> taxonNodeComparator;
116

    
117
    @Autowired
118
    public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){
119
        this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;
120
    }
121

    
122
    @Override
123
    public TaxonNode loadTaxonNodeByTaxon(Taxon taxon, UUID classificationUuid, List<String> propertyPaths){
124
        Classification tree = dao.load(classificationUuid);
125
        TaxonNode node = tree.getNode(taxon);
126

    
127
        return loadTaxonNode(node.getUuid(), propertyPaths);
128
    }
129

    
130
    public TaxonNode loadTaxonNode(UUID taxonNodeUuid, List<String> propertyPaths){
131
        return taxonNodeDao.load(taxonNodeUuid, propertyPaths);
132
    }
133

    
134
    @Override
135
    public List<TaxonNode> listRankSpecificRootNodes(Classification classification,
136
            TaxonNode subtree, Rank rank,
137
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
138
        return pageRankSpecificRootNodes(classification, subtree, rank, includeUnpublished, pageSize, pageIndex, propertyPaths).getRecords();
139
    }
140

    
141
    @Override
142
    public List<TaxonNodeDto> listRankSpecificRootNodeDtos(Classification classification, TaxonNode subtree,
143
            Rank rank, boolean includeUnpublished, Integer pageSize, Integer pageIndex, TaxonNodeDtoSortMode sortMode,
144
            List<String> propertyPaths) {
145
        List<TaxonNode> list = listRankSpecificRootNodes(classification, subtree, rank, includeUnpublished, pageSize, pageIndex, propertyPaths);
146
        return list.stream().filter(e ->  e != null).map(e -> new TaxonNodeDto(e)).sorted(sortMode.newComparator()).collect(Collectors.toList());
147
    }
148

    
149
    @Override
150
    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, Rank rank,
151
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
152
        return pageRankSpecificRootNodes(classification, null, rank, includeUnpublished, pageSize, pageIndex, propertyPaths);
153
    }
154

    
155
    @Override
156
    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, TaxonNode subtree, Rank rank,
157
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
158
        long[] numberOfResults = dao.countRankSpecificRootNodes(classification, subtree, includeUnpublished, rank);
159
        long totalNumberOfResults = numberOfResults[0] + (numberOfResults.length > 1 ? numberOfResults[1] : 0);
160

    
161
        List<TaxonNode> results = new ArrayList<>();
162

    
163
        if (AbstractPagerImpl.hasResultsInRange(totalNumberOfResults, pageIndex, pageSize)) { // no point checking again
164
            Integer limit = PagerUtils.limitFor(pageSize);
165
            Integer start = PagerUtils.startFor(pageSize, pageIndex);
166

    
167
            Integer remainingLimit = limit;
168
            int[] queryIndexes = rank == null ? new int[]{0} : new int[]{0,1};
169

    
170
            for(int queryIndex: queryIndexes) {
171
                if(start != null && start > numberOfResults[queryIndex]) {
172
                    // start in next query with new start value
173
                    start = start - (int)numberOfResults[queryIndex];
174
                    continue;
175
                }
176

    
177
                List<TaxonNode> perQueryResults = dao.listRankSpecificRootNodes(classification,
178
                        subtree, rank, includeUnpublished, remainingLimit,
179
                        start, propertyPaths, queryIndex);
180
                results.addAll(perQueryResults);
181
                if(remainingLimit != null ){
182
                    remainingLimit = remainingLimit - results.size();
183
                    if(remainingLimit <= 0) {
184
                        // no need to run further queries if first query returned enough items!
185
                        break;
186
                    }
187
                    // start at with fist item of next query to fetch the remaining items
188
                    start = 0;
189
                }
190
            }
191
        }
192
//        long start_t = System.currentTimeMillis();
193
        Collections.sort(results, taxonNodeComparator); // TODO is ordering during the hibernate query in the dao possible?
194
//        System.err.println("service.pageRankSpecificRootNodes() - Collections.sort(results,  taxonNodeComparator) " + (System.currentTimeMillis() - start_t));
195
        return new DefaultPagerImpl<>(pageIndex, totalNumberOfResults, pageSize, results);
196

    
197
    }
198

    
199
    @Override
200
    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, Rank baseRank,
201
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
202
        return loadTreeBranch(taxonNode, null, baseRank, includeUnpublished, propertyPaths);
203
    }
204

    
205
    @Override
206
    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, TaxonNode subtree, Rank baseRank,
207
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
208

    
209
        TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
210
        if(baseRank != null){
211
            baseRank = (Rank) termDao.load(baseRank.getUuid());
212
        }
213
        if (!includeUnpublished && thisNode.getTaxon() != null && !thisNode.getTaxon().isPublish()){
214
            throw new UnpublishedException("Final taxon in tree branch is unpublished.");
215
        }
216

    
217
        List<TaxonNode> pathToRoot = new ArrayList<>();
218
        pathToRoot.add(thisNode);
219

    
220
        while(!thisNode.isTopmostNode()){
221
            //TODO why do we need to deproxy here?
222
            //     without this thisNode.getParent() will return NULL in
223
            //     some cases (environment dependend?) even if the parent exits
224
            TaxonNode parentNode = CdmBase.deproxy(thisNode).getParent();
225

    
226
            if(parentNode == null){
227
                throw new NullPointerException("Taxon node " + thisNode + " must have a parent since it is not top most");
228
            }
229
            if(parentNode.getTaxon() == null){
230
                throw new NullPointerException("The taxon associated with taxon node " + parentNode + " is NULL");
231
            }
232
            if(!includeUnpublished && !parentNode.getTaxon().isPublish()){
233
                throw new UnpublishedException("Some taxon in tree branch is unpublished.");
234
            }
235
            if(parentNode.getTaxon().getName() == null){
236
                throw new NullPointerException("The name of the taxon associated with taxonNode " + parentNode + " is NULL");
237
            }
238

    
239
            Rank parentNodeRank = (parentNode.getTaxon().getName() == null) ? null : parentNode.getTaxon().getName().getRank();
240
            // stop if the next parent is higher than the baseRank
241
            if(baseRank != null && parentNodeRank != null && baseRank.isLower(parentNodeRank)){
242
                break;
243
            }
244
            if((subtree!= null && !subtree.isAncestor(parentNode) )){
245
                break;
246
            }
247

    
248
            pathToRoot.add(parentNode);
249
            thisNode = parentNode;
250
        }
251

    
252
        // initialize and invert order of nodes in list
253
        defaultBeanInitializer.initializeAll(pathToRoot, propertyPaths);
254
        Collections.reverse(pathToRoot);
255

    
256
        return pathToRoot;
257
    }
258

    
259
    @Override
260
    public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification, Rank baseRank,
261
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
262
        return loadTreeBranchToTaxon(taxon, classification, null, baseRank, includeUnpublished, propertyPaths);
263
    }
264

    
265
    @Override
266
    public List<TaxonNodeDto> loadTreeBranchDTOsToTaxon(Taxon taxon, Classification classification,
267
            TaxonNode subtree, Rank baseRank,
268
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException {
269
        List<TaxonNode> list = loadTreeBranchToTaxon(taxon, classification, subtree, baseRank, includeUnpublished, propertyPaths);
270
        return list.stream().map(e -> new TaxonNodeDto(e)).collect(Collectors.toList());
271
    }
272

    
273
    @Override
274
    public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification,
275
            TaxonNode subtree, Rank baseRank,
276
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
277

    
278
        UUID nodeUuid = getTaxonNodeUuidByTaxonUuid(classification.getUuid(), taxon.getUuid());
279
        TaxonNode node = taxonNodeService.find(nodeUuid);
280
        if(node == null){
281
            logger.warn("The specified taxon is not found in the given tree.");
282
            return null;
283
        }else if (subtree != null && !node.isDescendant(subtree)){
284
            //TODO handle as exception? E.g. FilterException, AccessDeniedException?
285
            logger.warn("The specified taxon is not found for the given subtree.");
286
            return null;
287
        }
288

    
289
        return loadTreeBranch(node, subtree, baseRank, includeUnpublished, propertyPaths);
290
    }
291

    
292

    
293
    @Override
294
    public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
295
            List<String> propertyPaths) {
296
        taxonNode = taxonNodeDao.load(taxonNode.getUuid());
297
        List<TaxonNode> childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
298
        defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
299
        Collections.sort(childNodes, taxonNodeComparator);
300
        return childNodes;
301
    }
302

    
303
    @Override
304
    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid,
305
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths){
306
        try {
307
            return listChildNodesOfTaxon(taxonUuid, classificationUuid, null, includeUnpublished, pageSize, pageIndex, propertyPaths);
308
        } catch (FilterException e) {
309
            throw new RuntimeException(e);  //this should not happen as filter is null
310
        }
311
    }
312

    
313
    @Override
314
    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid, UUID subtreeUuid,
315
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) throws FilterException{
316

    
317
        Classification classification = dao.load(classificationUuid);
318
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
319
        TaxonNode subtree = taxonNodeDao.load(subtreeUuid);
320
        if (subtreeUuid != null && subtree == null){
321
            throw new FilterException("Taxon node for subtree filter can not be found in database", true);
322
        }
323

    
324
        List<TaxonNode> results = dao.listChildrenOf(
325
                taxon, classification, subtree, includeUnpublished, pageSize, pageIndex, propertyPaths);
326
        Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
327
        return results;
328
    }
329

    
330
    @Override
331
    public List<TaxonNodeDto> listChildNodeDtosOfTaxon(UUID taxonUuid, UUID classificationUuid, UUID subtreeUuid, boolean includeUnpublished,
332
            Integer pageSize, Integer pageIndex, TaxonNodeDtoSortMode sortMode, List<String> propertyPaths) throws FilterException{
333
        Classification classification = dao.load(classificationUuid);
334
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
335
        TaxonNode subtree = taxonNodeDao.load(subtreeUuid);
336
        if (subtreeUuid != null && subtree == null){
337
            throw new FilterException("Taxon node for subtree filter can not be found in database", true);
338
        }
339

    
340
        List<TaxonNode> results = dao.listChildrenOf(
341
                taxon, classification, subtree, includeUnpublished, pageSize, pageIndex, propertyPaths);
342
        Comparator<TaxonNodeDto> comparator = sortMode.newComparator();
343
        // TODO order during the hibernate query in the dao?
344
        List<TaxonNodeDto> dtos = results.stream().map(e -> new TaxonNodeDto(e)).sorted(comparator).collect(Collectors.toList());
345
        return dtos;
346
    }
347

    
348
    @Override
349
    public Pager<TaxonNode> pageSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, boolean includeUnpublished,
350
            Integer pageSize, Integer pageIndex, List<String> propertyPaths){
351

    
352
        Classification classification = dao.load(classificationUuid);
353
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
354

    
355
        long numberOfResults = dao.countSiblingsOf(taxon, classification, includeUnpublished);
356

    
357
        List<TaxonNode> results;
358
        if(PagerUtils.hasResultsInRange(numberOfResults, pageIndex, pageSize)) {
359
            results = dao.listSiblingsOf(taxon, classification, includeUnpublished, pageSize, pageIndex, propertyPaths);
360
            Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
361
        } else {
362
            results = new ArrayList<>();
363
        }
364

    
365
        return new DefaultPagerImpl<>(pageIndex, numberOfResults, pageSize, results);
366
    }
367

    
368
    @Override
369
    public List<TaxonNode> listSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, boolean includeUnpublished,
370
            Integer pageSize, Integer pageIndex, List<String> propertyPaths){
371

    
372
        Pager<TaxonNode> pager = pageSiblingsOfTaxon(taxonUuid, classificationUuid, includeUnpublished, pageSize, pageIndex, propertyPaths);
373
        return pager.getRecords();
374
    }
375

    
376
    @Override
377
    public ITaxonTreeNode getTreeNodeByUuid(UUID uuid){
378
        ITaxonTreeNode treeNode = taxonNodeDao.findByUuid(uuid);
379
        if(treeNode == null){
380
            treeNode = dao.findByUuid(uuid);
381
        }
382

    
383
        return treeNode;
384
    }
385

    
386
    @Override
387
    public TaxonNode getRootNode(UUID classificationUuid){
388
        return dao.getRootNode(classificationUuid);
389
    }
390

    
391
    @Override
392
    public List<Classification> listClassifications(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
393
        return dao.list(limit, start, orderHints, propertyPaths);
394
    }
395

    
396
    @Override
397
    public UUID removeTreeNode(ITaxonTreeNode treeNode) {
398
        if(treeNode instanceof Classification){
399
            return dao.delete((Classification) treeNode);
400
        }else if(treeNode instanceof TaxonNode){
401
            return taxonNodeDao.delete((TaxonNode)treeNode);
402
        }
403
        return null;
404
    }
405

    
406
    @Override
407
    public Map<UUID, TaxonNode> saveTaxonNodeAll(
408
            Collection<TaxonNode> taxonNodeCollection) {
409
        return taxonNodeDao.saveAll(taxonNodeCollection);
410
    }
411

    
412
    @Override
413
    public UUID saveClassification(Classification classification) {
414

    
415
       taxonNodeDao.saveOrUpdateAll(classification.getAllNodes());
416
       UUID result =dao.saveOrUpdate(classification);
417
       return result;
418
    }
419

    
420
    @Override
421
    public UUID saveTreeNode(ITaxonTreeNode treeNode) {
422
        if(treeNode instanceof Classification){
423
            return dao.save((Classification) treeNode).getUuid();
424
        }else if(treeNode instanceof TaxonNode){
425
            return taxonNodeDao.save((TaxonNode)treeNode).getUuid();
426
        }
427
        return null;
428
    }
429

    
430
    @Override
431
    public List<TaxonNode> getAllNodes(){
432
        return taxonNodeDao.list(null,null);
433
    }
434

    
435
    @Override
436
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, Integer limit, String pattern, boolean searchForClassifications, boolean includeDoubtful) {
437
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid),  limit, pattern, searchForClassifications, includeDoubtful);
438
    }
439

    
440
    @Override
441
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification,  Integer limit, String pattern, boolean searchForClassifications) {
442
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification,  limit, pattern, searchForClassifications);
443
    }
444

    
445
    @Override
446
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, boolean searchForClassifications ) {
447
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), null, null, searchForClassifications);
448
    }
449

    
450
    @Override
451
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, boolean searchForClassifications ) {
452
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, null, null, searchForClassifications);
453
    }
454

    
455
    @Override
456
    public List<UuidAndTitleCache<Classification>> getUuidAndTitleCache(Integer limit, String pattern) {
457
        return dao.getUuidAndTitleCache(limit, pattern);
458
    }
459

    
460
    @Override
461
    public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(
462
            TaxonNode taxonNode, List<String> propertyPaths, int size,
463
            int height, int widthOrDuration, String[] mimeTypes) {
464

    
465
        TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<>();
466
        List<MediaRepresentation> mediaRepresentations = new ArrayList<>();
467

    
468
        //add all media of the children to the result map
469
        if (taxonNode != null){
470

    
471
            List<TaxonNode> nodes = new ArrayList<>();
472

    
473
            nodes.add(loadTaxonNode(taxonNode.getUuid(), propertyPaths));
474
            nodes.addAll(loadChildNodesOfTaxonNode(taxonNode, propertyPaths));
475

    
476
            for(TaxonNode node : nodes){
477
                Taxon taxon = node.getTaxon();
478
                for (TaxonDescription taxonDescription: taxon.getDescriptions()){
479
                    for (DescriptionElementBase descriptionElement: taxonDescription.getElements()){
480
                        for(Media media : descriptionElement.getMedia()){
481
                            //find the best matching representation
482
                            mediaRepresentations.add(MediaUtils.findBestMatchingRepresentation(media,null, size, height, widthOrDuration, mimeTypes, MediaUtils.MissingValueStrategy.MAX));
483
                        }
484
                    }
485
                }
486
                result.put(taxon.getUuid(), mediaRepresentations);
487
            }
488
        }
489

    
490
        return result;
491
    }
492

    
493
    @Override
494
    @Transactional(readOnly = false)
495
    public UpdateResult updateCaches(Class<? extends Classification> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Classification> cacheStrategy, IProgressMonitor monitor) {
496
        if (clazz == null){
497
            clazz = Classification.class;
498
        }
499
        return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
500
    }
501

    
502
    /**
503
     *
504
     * @param allNodesOfClassification
505
     * @return null - if  allNodesOfClassification is empty <br>
506
     */
507

    
508
    private Map<String, List<TaxonNode>> getSortedGenusList(Collection<TaxonNode> allNodesOfClassification){
509

    
510
    	if(allNodesOfClassification == null || allNodesOfClassification.isEmpty()){
511
    		return null;
512
    	}
513
    	Map<String, List<TaxonNode>> sortedGenusMap = new HashMap<>();
514
    	for(TaxonNode node:allNodesOfClassification){
515
    		Taxon taxon = node.getTaxon();
516
    		INonViralName name = taxon.getName();
517
    		String genusOrUninomial = name.getGenusOrUninomial();
518
    		//if rank unknown split string and take first word
519
    		if(genusOrUninomial == null){
520
    			String titleCache = taxon.getTitleCache();
521
    			String[] split = titleCache.split("\\s+");
522
    			for(String s:split){
523
    				genusOrUninomial = s;
524
    				break;
525
    			}
526
    		}
527
    		//if node has children
528

    
529
    		//retrieve list from map if not create List
530
    		if(sortedGenusMap.containsKey(genusOrUninomial)){
531
    			List<TaxonNode> list = sortedGenusMap.get(genusOrUninomial);
532
    			list.add(node);
533
    			sortedGenusMap.put(genusOrUninomial, list);
534
    		}else{
535
    			//create List for genus
536
    			List<TaxonNode> list = new ArrayList<>();
537
    			list.add(node);
538
    			sortedGenusMap.put(genusOrUninomial, list);
539
    		}
540
    	}
541
    	return sortedGenusMap;
542
    }
543

    
544
    /**
545
     *
546
     * creates new Classification and parent TaxonNodes at genus level
547
     *
548
     *
549
     * @param map GenusMap which holds a name (Genus) and all the same Taxa as a list
550
     * @param classification you want to improve the hierarchy (will not be modified)
551
     * @param configurator to change certain settings, if null then standard settings will be taken
552
     * @return new classification with parentNodes for each entry in the map
553
     */
554
    @SuppressWarnings({ "unchecked" })
555
	@Transactional(readOnly = false)
556
	@Override
557
    public UpdateResult createHierarchyInClassification(Classification classification, CreateHierarchyForClassificationConfigurator configurator){
558
        UpdateResult result = new UpdateResult();
559
    	classification = dao.findByUuid(classification.getUuid());
560
    	Map<String, List<TaxonNode>> map = getSortedGenusList(classification.getAllNodes());
561

    
562
    	final String APPENDIX = "repaired";
563
    	String titleCache = StringUtils.isBlank(classification.getTitleCache()) ? " " : classification.getTitleCache() ;
564
    	//TODO classification clone???
565
    	Classification newClassification = Classification.NewInstance(titleCache +" "+ APPENDIX);
566
    	newClassification.setReference(classification.getReference());
567

    
568
    	for(Map.Entry<String, List<TaxonNode>> entry:map.entrySet()){
569
    		String genus = entry.getKey();
570
    		List<TaxonNode> listOfTaxonNodes = entry.getValue();
571
    		TaxonNode parentNode = null;
572
    		//Search for genus in list
573
    		for(TaxonNode tNode:listOfTaxonNodes){
574
    			//take that taxonNode as parent and remove from list with all it possible children
575
    			//FIXME NPE for name
576
    			TaxonName name = tNode.getTaxon().getName();
577
    			if(name.getNameCache().equalsIgnoreCase(genus)){
578
    				TaxonNode clone = tNode.clone();
579
    				if(!tNode.hasChildNodes()){
580
    					//FIXME remove classification
581
//    					parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
582
    					parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
583
    					//remove taxonNode from list because just added to classification
584
    					result.addUpdatedObject(tNode);
585
    					listOfTaxonNodes.remove(tNode);
586
    				}else{
587
    					//get all childNodes
588
    					//save prior Hierarchy and remove them from the list
589
    					List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tNode, clone, result);
590
//    					parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
591
      					//FIXME remove classification
592
    					parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
593
    					//remove taxonNode from list because just added to classification
594
    					result.addUpdatedObject(tNode);
595
    					listOfTaxonNodes.remove(tNode);
596
    					if(copyAllChildrenToTaxonNode != null){
597
    						listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
598
    					}
599
    				}
600
    				break;
601
    			}
602
    		}
603
    		if(parentNode == null){
604
    			//if no match found in list, create parentNode
605
    			NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
606
    			TaxonName TaxonName = (TaxonName)parser.parseFullName(genus);
607
    			//TODO Sec via configurator
608
    			Taxon taxon = Taxon.NewInstance(TaxonName, null);
609
    			parentNode = newClassification.addChildTaxon(taxon, 0, null, null);
610
    			result.addUpdatedObject(parentNode);
611
    		}
612
    		//iterate over the rest of the list
613
    		for(TaxonNode tn : listOfTaxonNodes){
614
    			//if TaxonNode has a parent and this is not the classification then skip it
615
    			//and add to new classification via the parentNode as children of it
616
    			//this should assures to keep the already existing hierarchy
617
    			//FIXME: Assert is not rootnode --> entrypoint is not classification in future but rather rootNode
618

    
619
    			if(!tn.isTopmostNode()){
620
    				continue; //skip to next taxonNode
621
    			}
622

    
623
    			TaxonNode clone = tn.clone();
624
    			//FIXME: citation from node
625
    			//TODO: addchildNode without citation and references
626
//    			TaxonNode taxonNode = parentNode.addChildNode(clone, classification.getCitation(), classification.getMicroReference());
627
    			TaxonNode taxonNode = parentNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
628
    			result.addUnChangedObject(clone);
629
    			if(tn.hasChildNodes()){
630
    				//save hierarchy in new classification
631
    				List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tn, taxonNode, result);
632
    				if(copyAllChildrenToTaxonNode != null){
633
    					listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
634
    				}
635
    			}
636
    		}
637
    	}
638
    	dao.saveOrUpdate(newClassification);
639
    	result.setCdmEntity(newClassification);
640
    	return result;
641
    }
642

    
643
    /**
644
     *
645
     * recursive method to get all childnodes of taxonNode in classification.
646
     *
647
     * @param classification just for References and Citation, can be null
648
     * @param copyFromNode TaxonNode with Children
649
     * @param copyToNode TaxonNode which will receive the children
650
     * @return List of ChildNode which has been added. If node has no children returns null
651
     */
652
   private List<TaxonNode> copyAllChildrenToTaxonNode(TaxonNode copyFromNode, TaxonNode copyToNode, UpdateResult result) {
653
		List<TaxonNode> childNodes;
654
		if(!copyFromNode.hasChildNodes()){
655
			return null;
656
		}else{
657
			childNodes = copyFromNode.getChildNodes();
658
		}
659
		for(TaxonNode childNode:childNodes){
660
			TaxonNode clone = childNode.clone();
661
			result.addUnChangedObject(clone);
662
			if(childNode.hasChildNodes()){
663
				copyAllChildrenToTaxonNode(childNode, clone, result);
664
			}
665
			//FIXME: citation from node instead of classification
666
//			copyToNode.addChildNode(clone,classification.getCitation(), classification.getMicroReference());
667
			copyToNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
668
		}
669
		return childNodes;
670
	}
671

    
672
    @Override
673
    public ClassificationLookupDTO classificationLookup(Classification classification) {
674
        return dao.classificationLookup(classification);
675
    }
676

    
677
    @Override
678
    @Transactional
679
    public DeleteResult delete(UUID classificationUuid, TaxonDeletionConfigurator config){
680
        DeleteResult result = new DeleteResult();
681
        Classification classification = dao.findByUuid(classificationUuid);
682
        if (classification == null){
683
            result.addException(new IllegalArgumentException("The classification does not exist in database."));
684
            result.setAbort();
685
            return result;
686
        }
687
        if (!classification.hasChildNodes()){
688
            dao.delete(classification);
689
            result.addDeletedObject(classification);
690
            return result;
691
        }
692
        if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){
693
//            TaxonNode root = classification.getRootNode();
694
//            result.includeResult(taxonNodeService.deleteTaxonNode(HibernateProxyHelper.deproxy(root), config));
695
//            result.addDeletedObject(classification);
696
            dao.delete(classification);
697
            result.addDeletedObject(classification);
698
            return result;
699
        }
700

    
701

    
702
        return result;
703
    }
704

    
705
    @Override
706
    public List<GroupedTaxonDTO> groupTaxaByHigherTaxon(List<UUID> originalTaxonUuids, UUID classificationUuid, Rank minRank, Rank maxRank){
707
        List<GroupedTaxonDTO> result = new ArrayList<>();
708

    
709
        //get treeindex for each taxonUUID
710
        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
711

    
712
        //build treeindex list (or tree)
713
        //TODO make it work with TreeIndex or move there
714
        List<String> treeIndexClosureStr = new ArrayList<>();
715
        for (TreeIndex treeIndex : taxonIdTreeIndexMap.values()){
716
            String[] splits = treeIndex.toString().substring(1).split(ITreeNode.separator);
717
            String currentIndex = ITreeNode.separator;
718
            for (String split : splits){
719
                if (split.equals("")){
720
                    continue;
721
                }
722
                currentIndex += split + ITreeNode.separator;
723
                if (!treeIndexClosureStr.contains(currentIndex) && !split.startsWith(ITreeNode.treePrefix)){
724
                    treeIndexClosureStr.add(currentIndex);
725
                }
726
            }
727
        }
728

    
729
        //get rank sortindex for all parent taxa with sortindex <= minRank and sortIndex >= maxRank (if available)
730
        Integer minRankOrderIndex = minRank == null ? null : minRank.getOrderIndex();
731
        Integer maxRankOrderIndex = maxRank == null ? null : maxRank.getOrderIndex();
732
        List<TreeIndex> treeIndexClosure = TreeIndex.NewListInstance(treeIndexClosureStr);
733

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

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

    
739
        Map<TreeIndex, Integer> treeIndexSortIndexMap = new HashMap<>();
740
        TreeIndex lastTreeIndex = null;
741
        for (TreeIndex treeIndex : treeIndexList){
742
            if (lastTreeIndex != null && lastTreeIndex.hasChild(treeIndex)){
743
                treeIndexSortIndexMap.remove(lastTreeIndex);
744
            }
745
            treeIndexSortIndexMap.put(treeIndex, treeIndexSortIndexMapTmp.get(treeIndex));
746
            lastTreeIndex = treeIndex;
747
        }
748

    
749
        //get taxonID for treeIndexes
750
        Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(treeIndexSortIndexMap.keySet());
751

    
752
        //fill result list
753
        for (UUID originalTaxonUuid : originalTaxonUuids){
754
            GroupedTaxonDTO item = new GroupedTaxonDTO();
755
            result.add(item);
756
            item.setTaxonUuid(originalTaxonUuid);
757
            TreeIndex groupTreeIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
758
            String groupIndexX = TreeIndex.toString(groupTreeIndex);
759
            while (groupTreeIndex != null){
760
                if (treeIndexTaxonIdMap.get(groupTreeIndex) != null){
761
                    UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupTreeIndex);
762
                    item.setGroupTaxonUuid(uuidAndLabel.getUuid());
763
                    item.setGroupTaxonName(uuidAndLabel.getTitleCache());
764
                    break;
765
                }else{
766
                    groupTreeIndex = groupTreeIndex.parent();
767
//                    int index = groupIndex.substring(0, groupIndex.length()-1).lastIndexOf(ITreeNode.separator);
768
//                    groupIndex = index < 0 ? null : groupIndex.substring(0, index+1);
769
                }
770
            }
771
        }
772

    
773
        return result;
774
    }
775

    
776
    @Override
777
    public List<GroupedTaxonDTO> groupTaxaByMarkedParents(List<UUID> originalTaxonUuids, UUID classificationUuid,
778
            MarkerType markerType, Boolean flag) {
779

    
780
        List<GroupedTaxonDTO> result = new ArrayList<>();
781

    
782
        //get treeindex for each taxonUUID
783
        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
784

    
785
        //get all marked tree indexes
786
        Set<TreeIndex> markedTreeIndexes = dao.getMarkedTreeIndexes(markerType, flag);
787

    
788
        Map<TreeIndex, TreeIndex> groupedMap = TreeIndex.group(markedTreeIndexes, taxonIdTreeIndexMap.values());
789
        Set<TreeIndex> notNullGroups = new HashSet<>(groupedMap.values());
790
        notNullGroups.remove(null);
791

    
792
        //get taxonInfo for treeIndexes
793
        Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(notNullGroups);
794

    
795
        //fill result list
796
        for (UUID originalTaxonUuid : originalTaxonUuids){
797
            GroupedTaxonDTO item = new GroupedTaxonDTO();
798
            result.add(item);
799
            item.setTaxonUuid(originalTaxonUuid);
800

    
801
            TreeIndex toBeGroupedTreeIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
802
            TreeIndex groupTreeIndex = groupedMap.get(toBeGroupedTreeIndex);
803
            UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupTreeIndex);
804
            if (uuidAndLabel != null){
805
                item.setGroupTaxonUuid(uuidAndLabel.getUuid());
806
                item.setGroupTaxonName(uuidAndLabel.getTitleCache());
807
            }
808
        }
809

    
810
        return result;
811
    }
812

    
813
    @Override
814
    public UUID getTaxonNodeUuidByTaxonUuid(UUID classificationUuid, UUID taxonUuid) {
815
        Map<UUID, UUID> map = dao.getTaxonNodeUuidByTaxonUuid(classificationUuid, Arrays.asList(taxonUuid));
816
        UUID taxonNodeUuid = map.get(taxonUuid);
817
        return taxonNodeUuid;
818
    }
819

    
820
    @Override
821
    public TaxonInContextDTO getTaxonInContext(UUID classificationUuid, UUID taxonBaseUuid,
822
            Boolean doChildren, Boolean doSynonyms, boolean includeUnpublished, List<UUID> ancestorMarkers,
823
            TaxonNodeSortMode sortMode) {
824

    
825
        TaxonInContextDTO result = new TaxonInContextDTO();
826

    
827
        TaxonBase<?> taxonBase = taxonDao.load(taxonBaseUuid);
828
        if (taxonBase == null){
829
            throw new EntityNotFoundException("Taxon with uuid " + taxonBaseUuid + " not found in datasource");
830
        }
831
        boolean isSynonym = false;
832
        Taxon acceptedTaxon;
833
        if (taxonBase.isInstanceOf(Synonym.class)){
834
            isSynonym = true;
835
            Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
836
            acceptedTaxon = synonym.getAcceptedTaxon();
837
            if (acceptedTaxon == null) {
838
                throw new EntityNotFoundException("Accepted taxon not found for synonym"  );
839
            }
840
            TaxonStatus taxonStatus = TaxonStatus.Synonym;
841
            if (synonym.getName()!= null && acceptedTaxon.getName() != null
842
                    && synonym.getName().getHomotypicalGroup().equals(acceptedTaxon.getName().getHomotypicalGroup())){
843
                taxonStatus = TaxonStatus.SynonymObjective;
844
            }
845
            result.setTaxonStatus(taxonStatus);
846

    
847
        }else{
848
            acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
849
            result.setTaxonStatus(TaxonStatus.Accepted);
850
        }
851
        UUID acceptedTaxonUuid = acceptedTaxon.getUuid();
852

    
853
        UUID taxonNodeUuid = getTaxonNodeUuidByTaxonUuid(classificationUuid, acceptedTaxonUuid);
854
        if (taxonNodeUuid == null) {
855
            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 );
856
        }
857
        result.setTaxonNodeUuid(taxonNodeUuid);
858

    
859
        //TODO make it a dao call
860
        Taxon parentTaxon = getParentTaxon(classificationUuid, acceptedTaxon);
861
        if (parentTaxon != null){
862
            result.setParentTaxonUuid(parentTaxon.getUuid());
863
            result.setParentTaxonLabel(parentTaxon.getTitleCache());
864
            if (parentTaxon.getName() != null){
865
                result.setParentNameLabel(parentTaxon.getName().getTitleCache());
866
            }
867
        }
868

    
869
        result.setTaxonUuid(taxonBaseUuid);
870
        result.setClassificationUuid(classificationUuid);
871
        if (taxonBase.getSec() != null){
872
            result.setSecundumUuid(taxonBase.getSec().getUuid());
873
            result.setSecundumLabel(taxonBase.getSec().getTitleCache());
874
        }
875
        result.setTaxonLabel(taxonBase.getTitleCache());
876

    
877
        TaxonName name = taxonBase.getName();
878
        result.setNameUuid(name.getUuid());
879
        result.setNameLabel(name.getTitleCache());
880
        result.setNameWithoutAuthor(name.getNameCache());
881
        result.setGenusOrUninomial(name.getGenusOrUninomial());
882
        result.setInfraGenericEpithet(name.getInfraGenericEpithet());
883
        result.setSpeciesEpithet(name.getSpecificEpithet());
884
        result.setInfraSpecificEpithet(name.getInfraSpecificEpithet());
885

    
886
        result.setAuthorship(name.getAuthorshipCache());
887

    
888
        Rank rank = name.getRank();
889
        if (rank != null){
890
            result.setRankUuid(rank.getUuid());
891
            String rankLabel = rank.getAbbreviation();
892
            if (StringUtils.isBlank(rankLabel)){
893
                rankLabel = rank.getLabel();
894
            }
895
            result.setRankLabel(rankLabel);
896
        }
897

    
898
        boolean recursive = false;
899
        Integer pageSize = null;
900
        Integer pageIndex = null;
901
        Pager<TaxonNodeDto> children = taxonNodeService.pageChildNodesDTOs(taxonNodeUuid, recursive, includeUnpublished, doSynonyms,
902
                sortMode, pageSize, pageIndex);
903

    
904
        //children
905
        if(! isSynonym) {
906
            for (TaxonNodeDto childDto : children.getRecords()){
907
                if (doChildren && childDto.getTaxonStatus().equals(TaxonStatus.Accepted)){
908
                    EntityDTO<Taxon> child = new EntityDTO<Taxon>(childDto.getTaxonUuid(), childDto.getTitleCache());
909
                    result.addChild(child);
910
                }else if (doSynonyms && childDto.getTaxonStatus().isSynonym()){
911
                    EntityDTO<Synonym> child = new EntityDTO<>(childDto.getTaxonUuid(), childDto.getTitleCache());
912
                    result.addSynonym(child);
913
                }
914
            }
915
        }else{
916
            result.setAcceptedTaxonUuid(acceptedTaxonUuid);
917
            String nameTitel = acceptedTaxon.getName() == null ? null : acceptedTaxon.getName().getTitleCache();
918
            result.setAcceptedTaxonLabel(acceptedTaxon.getTitleCache());
919
            result.setAcceptedNameLabel(nameTitel);
920
        }
921

    
922
        //marked ancestors
923
        if (ancestorMarkers != null && !ancestorMarkers.isEmpty()){
924
            @SuppressWarnings("rawtypes")
925
            List<DefinedTermBase> markerTypesTerms = termDao.list(ancestorMarkers, pageSize, null, null, null);
926
            List<MarkerType> markerTypes = new ArrayList<>();
927
            for (DefinedTermBase<?> term : markerTypesTerms){
928
                if (term.isInstanceOf(MarkerType.class)){
929
                    markerTypes.add(CdmBase.deproxy(term, MarkerType.class));
930
                }
931
            }
932
            if (! markerTypes.isEmpty()){
933
                TaxonNode node = taxonNodeDao.findByUuid(taxonNodeUuid);
934
                handleAncestorsForMarkersRecursive(result, markerTypes, node);
935
            }
936
        }
937

    
938
        return result;
939
    }
940

    
941
    private Taxon getParentTaxon(UUID classificationUuid, Taxon acceptedTaxon) {
942
        if (classificationUuid == null){
943
            return null;
944
        }
945
        TaxonNode parent = null;
946
        for (TaxonNode node : acceptedTaxon.getTaxonNodes()){
947
            if (classificationUuid.equals(node.getClassification().getUuid())){
948
                parent = node.getParent();
949
            }
950
        }
951
        if (parent != null){
952
            return parent.getTaxon();
953
        }
954
        return null;
955
    }
956

    
957
    private void handleAncestorsForMarkersRecursive(TaxonInContextDTO result, List<MarkerType> markerTypes, TaxonNode node) {
958
       for (MarkerType type : markerTypes){
959
            Taxon taxon = node.getTaxon();
960
            if (taxon != null && taxon.hasMarker(type, true)){
961
                String label =  taxon.getName() == null? taxon.getTitleCache() : taxon.getName().getTitleCache();
962
                MarkedEntityDTO<Taxon> dto = new MarkedEntityDTO<>(type, true, taxon.getUuid(), label);
963
                result.addMarkedAncestor(dto);
964
            }
965
        }
966
        TaxonNode parentNode = node.getParent();
967
        if (parentNode != null){
968
            handleAncestorsForMarkersRecursive(result, markerTypes, parentNode);
969
        }
970
    }
971

    
972
    @Override
973
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
974
            Classification classification) {
975
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, false);
976
    }
977

    
978
    @Override
979
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
980
            UUID classificationUuid) {
981
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classificationUuid, false);
982
    }
983

    
984
    @Override
985
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
986
            UUID classificationUuid, Integer limit, String pattern) {
987
        return  getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classificationUuid,  limit, pattern, false);
988
    }
989

    
990
    @Override
991
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
992
            Classification classification, Integer limit, String pattern) {
993
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, limit, pattern, false);
994
    }
995

    
996
    @Override
997
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
998
            UUID classificationUuid, Integer limit, String pattern, boolean searchForClassifications) {
999
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1000
                classificationUuid, limit, pattern, searchForClassifications, false);
1001
    }
1002
}
(5-5/100)