Project

General

Profile

Download (43.3 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

    
25
import javax.persistence.EntityNotFoundException;
26

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

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

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

    
95
    @Autowired
96
    private ITaxonNodeDao taxonNodeDao;
97

    
98
    @Autowired
99
    private ITaxonDao taxonDao;
100

    
101
    @Autowired
102
    private ITaxonNodeService taxonNodeService;
103

    
104
    @Autowired
105
    private IDefinedTermDao termDao;
106

    
107
    @Autowired
108
    private IBeanInitializer defaultBeanInitializer;
109

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

    
116
    private Comparator<? super TaxonNode> taxonNodeComparator;
117

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

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

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

    
131
    @Override
132
    @Deprecated // use loadTaxonNode(UUID, List<String>) instead
133
    public TaxonNode loadTaxonNode(TaxonNode taxonNode, List<String> propertyPaths){
134
        return taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
135
    }
136

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

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

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

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

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

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

    
188
    /**
189
     * {@inheritDoc}
190
     */
191
    @Override
192
    public List<TaxonNode> listRankSpecificRootNodes(Classification classification, Rank rank,
193
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
194
        return pageRankSpecificRootNodes(classification, rank, includeUnpublished, pageSize, pageIndex, propertyPaths).getRecords();
195
    }
196

    
197
    @Override
198
    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, Rank rank,
199
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
200
        long[] numberOfResults = dao.countRankSpecificRootNodes(classification, includeUnpublished, rank);
201
        long totalNumberOfResults = numberOfResults[0] + (numberOfResults.length > 1 ? numberOfResults[1] : 0);
202

    
203
        List<TaxonNode> results = new ArrayList<>();
204

    
205
        if (AbstractPagerImpl.hasResultsInRange(totalNumberOfResults, pageIndex, pageSize)) { // no point checking again
206
            Integer limit = PagerUtils.limitFor(pageSize);
207
            Integer start = PagerUtils.startFor(pageSize, pageIndex);
208

    
209
            Integer remainingLimit = limit;
210
            int[] queryIndexes = rank == null ? new int[]{0} : new int[]{0,1};
211

    
212
            for(int queryIndex: queryIndexes) {
213
                if(start != null && start > numberOfResults[queryIndex]) {
214
                    // start in next query with new start value
215
                    start = start - (int)numberOfResults[queryIndex];
216
                    continue;
217
                }
218

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

    
238
    }
239

    
240

    
241
    /**
242
     * {@inheritDoc}
243
     */
244
    @Override
245
    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, Rank baseRank,
246
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
247

    
248
        TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
249
        if(baseRank != null){
250
            baseRank = (Rank) termDao.load(baseRank.getUuid());
251
        }
252
        if (!includeUnpublished && thisNode.getTaxon() != null && !thisNode.getTaxon().isPublish()){
253
            throw new UnpublishedException("Final taxon in tree branch is unpublished.");
254
        }
255

    
256
        List<TaxonNode> pathToRoot = new ArrayList<>();
257
        pathToRoot.add(thisNode);
258

    
259
        while(!thisNode.isTopmostNode()){
260
            //TODO why do we need to deproxy here?
261
            //     without this thisNode.getParent() will return NULL in
262
            //     some cases (environment dependend?) even if the parent exits
263
            TaxonNode parentNode = CdmBase.deproxy(thisNode).getParent();
264

    
265
            if(parentNode == null){
266
                throw new NullPointerException("Taxon node " + thisNode + " must have a parent since it is not top most");
267
            }
268
            if(parentNode.getTaxon() == null){
269
                throw new NullPointerException("The taxon associated with taxon node " + parentNode + " is NULL");
270
            }
271
            if(!includeUnpublished && !parentNode.getTaxon().isPublish()){
272
                throw new UnpublishedException("Some taxon in tree branch is unpublished.");
273
            }
274
            if(parentNode.getTaxon().getName() == null){
275
                throw new NullPointerException("The name of the taxon associated with taxonNode " + parentNode + " is NULL");
276
            }
277

    
278
            Rank parentNodeRank = (parentNode.getTaxon().getName() == null) ? null : parentNode.getTaxon().getName().getRank();
279
            // stop if the next parent is higher than the baseRank
280
            if(baseRank != null && parentNodeRank != null && baseRank.isLower(parentNodeRank)){
281
                break;
282
            }
283

    
284
            pathToRoot.add(parentNode);
285
            thisNode = parentNode;
286
        }
287

    
288
        // initialize and invert order of nodes in list
289
        defaultBeanInitializer.initializeAll(pathToRoot, propertyPaths);
290
        Collections.reverse(pathToRoot);
291

    
292
        return pathToRoot;
293
    }
294

    
295
    @Override
296
    public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification, Rank baseRank,
297
            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
298

    
299
        UUID nodeUuid = getTaxonNodeUuidByTaxonUuid(classification.getUuid(), taxon.getUuid());
300
        TaxonNode node = taxonNodeService.find(nodeUuid);
301
        if(node == null){
302
            logger.warn("The specified taxon is not found in the given tree.");
303
            return null;
304
        }
305
        return loadTreeBranch(node, baseRank, includeUnpublished, propertyPaths);
306
    }
307

    
308

    
309
    @Override
310
    public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
311
            List<String> propertyPaths) {
312
        taxonNode = taxonNodeDao.load(taxonNode.getUuid());
313
        List<TaxonNode> childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
314
        defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
315
        Collections.sort(childNodes, taxonNodeComparator);
316
        return childNodes;
317
    }
318

    
319
    @Override
320
    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid,
321
            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths){
322

    
323
        Classification classification = dao.load(classificationUuid);
324
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
325

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

    
332
    @Override
333
    public Pager<TaxonNode> pageSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, boolean includeUnpublished,
334
            Integer pageSize, Integer pageIndex, List<String> propertyPaths){
335

    
336
        Classification classification = dao.load(classificationUuid);
337
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
338

    
339
        long numberOfResults = dao.countSiblingsOf(taxon, classification, includeUnpublished);
340

    
341
        List<TaxonNode> results;
342
        if(PagerUtils.hasResultsInRange(numberOfResults, pageIndex, pageSize)) {
343
            results = dao.listSiblingsOf(taxon, classification, includeUnpublished, pageSize, pageIndex, propertyPaths);
344
            Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
345
        } else {
346
            results = new ArrayList<>();
347
        }
348

    
349
        return new DefaultPagerImpl<TaxonNode>(pageIndex, numberOfResults, pageSize, results);
350
    }
351

    
352
    @Override
353
    public List<TaxonNode> listSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, boolean includeUnpublished,
354
            Integer pageSize, Integer pageIndex, List<String> propertyPaths){
355

    
356
        Pager<TaxonNode> pager = pageSiblingsOfTaxon(taxonUuid, classificationUuid, includeUnpublished, pageSize, pageIndex, propertyPaths);
357
        return pager.getRecords();
358
    }
359

    
360
    @Override
361
    public ITaxonTreeNode getTreeNodeByUuid(UUID uuid){
362
        ITaxonTreeNode treeNode = taxonNodeDao.findByUuid(uuid);
363
        if(treeNode == null){
364
            treeNode = dao.findByUuid(uuid);
365
        }
366

    
367
        return treeNode;
368
    }
369

    
370
    @Override
371
    public TaxonNode getRootNode(UUID classificationUuid){
372
        return dao.getRootNode(classificationUuid);
373
    }
374

    
375
    @Override
376
    public List<Classification> listClassifications(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
377
        return dao.list(limit, start, orderHints, propertyPaths);
378
    }
379

    
380
    @Override
381
    public UUID removeTaxonNode(TaxonNode taxonNode) {
382
        return taxonNodeDao.delete(taxonNode);
383
    }
384
    @Override
385
    public UUID removeTreeNode(ITaxonTreeNode treeNode) {
386
        if(treeNode instanceof Classification){
387
            return dao.delete((Classification) treeNode);
388
        }else if(treeNode instanceof TaxonNode){
389
            return taxonNodeDao.delete((TaxonNode)treeNode);
390
        }
391
        return null;
392
    }
393
    @Override
394
    public UUID saveTaxonNode(TaxonNode taxonNode) {
395
        return taxonNodeDao.save(taxonNode).getUuid();
396
    }
397

    
398
    @Override
399
    public Map<UUID, TaxonNode> saveTaxonNodeAll(
400
            Collection<TaxonNode> taxonNodeCollection) {
401
        return taxonNodeDao.saveAll(taxonNodeCollection);
402
    }
403

    
404
    @Override
405
    public UUID saveClassification(Classification classification) {
406

    
407
       taxonNodeDao.saveOrUpdateAll(classification.getAllNodes());
408
       UUID result =dao.saveOrUpdate(classification);
409
       return result;
410
    }
411

    
412
    @Override
413
    public UUID saveTreeNode(ITaxonTreeNode treeNode) {
414
        if(treeNode instanceof Classification){
415
            return dao.save((Classification) treeNode).getUuid();
416
        }else if(treeNode instanceof TaxonNode){
417
            return taxonNodeDao.save((TaxonNode)treeNode).getUuid();
418
        }
419
        return null;
420
    }
421

    
422
    @Override
423
    public List<TaxonNode> getAllNodes(){
424
        return taxonNodeDao.list(null,null);
425
    }
426

    
427
    @Override
428
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, Integer limit, String pattern, boolean searchForClassifications) {
429
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid),  limit, pattern, searchForClassifications);
430
    }
431

    
432
    @Override
433
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification,  Integer limit, String pattern, boolean searchForClassifications) {
434
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification,  limit, pattern, searchForClassifications);
435
    }
436

    
437
    @Override
438
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, boolean searchForClassifications ) {
439
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), null, null, searchForClassifications);
440
    }
441

    
442
    @Override
443
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, boolean searchForClassifications ) {
444
        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, null, null, searchForClassifications);
445
    }
446

    
447
    @Override
448
    public List<UuidAndTitleCache<Classification>> getUuidAndTitleCache(Integer limit, String pattern) {
449
        return dao.getUuidAndTitleCache(limit, pattern);
450
    }
451

    
452
    @Override
453
    public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(
454
            TaxonNode taxonNode, List<String> propertyPaths, int size,
455
            int height, int widthOrDuration, String[] mimeTypes) {
456

    
457
        TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<UUID, List<MediaRepresentation>>();
458
        List<Media> taxonMedia = new ArrayList<Media>();
459
        List<MediaRepresentation> mediaRepresentations = new ArrayList<MediaRepresentation>();
460

    
461
        //add all media of the children to the result map
462
        if (taxonNode != null){
463

    
464
            List<TaxonNode> nodes = new ArrayList<TaxonNode>();
465

    
466
            nodes.add(loadTaxonNode(taxonNode, propertyPaths));
467
            nodes.addAll(loadChildNodesOfTaxonNode(taxonNode, propertyPaths));
468

    
469
            if (nodes != null){
470
                for(TaxonNode node : nodes){
471
                    Taxon taxon = node.getTaxon();
472
                    for (TaxonDescription taxonDescription: taxon.getDescriptions()){
473
                        for (DescriptionElementBase descriptionElement: taxonDescription.getElements()){
474
                            for(Media media : descriptionElement.getMedia()){
475
                                taxonMedia.add(media);
476

    
477
                                //find the best matching representation
478
                                mediaRepresentations.add(MediaUtils.findBestMatchingRepresentation(media,null, size, height, widthOrDuration, mimeTypes));
479

    
480
                            }
481
                        }
482
                    }
483
                    result.put(taxon.getUuid(), mediaRepresentations);
484

    
485
                }
486
            }
487

    
488
        }
489

    
490
        return result;
491

    
492
    }
493

    
494

    
495
    @Override
496
    @Transactional(readOnly = false)
497
    public void updateTitleCache(Class<? extends Classification> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Classification> cacheStrategy, IProgressMonitor monitor) {
498
        if (clazz == null){
499
            clazz = Classification.class;
500
        }
501
        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
502
    }
503

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

    
510
    private Map<String, List<TaxonNode>> getSortedGenusList(Collection<TaxonNode> allNodesOfClassification){
511

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

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

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

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

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

    
622
    			if(!tn.isTopmostNode()){
623
    				continue; //skip to next taxonNode
624
    			}
625

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

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

    
675
    /**
676
     * {@inheritDoc}
677
     */
678
    @Override
679
    public ClassificationLookupDTO classificationLookup(Classification classification) {
680
        return dao.classificationLookup(classification);
681
    }
682

    
683

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

    
708

    
709
        return result;
710
    }
711

    
712
    @Override
713
    public List<GroupedTaxonDTO> groupTaxaByHigherTaxon(List<UUID> originalTaxonUuids, UUID classificationUuid, Rank minRank, Rank maxRank){
714
        List<GroupedTaxonDTO> result = new ArrayList<>();
715

    
716
        //get treeindex for each taxonUUID
717
        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
718

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

    
736
        //get rank sortindex for all parent taxa with sortindex <= minRank and sortIndex >= maxRank (if available)
737
        Integer minRankOrderIndex = minRank == null ? null : minRank.getOrderIndex();
738
        Integer maxRankOrderIndex = maxRank == null ? null : maxRank.getOrderIndex();
739
        List<TreeIndex> treeIndexClosure = TreeIndex.NewListInstance(treeIndexClosureStr);
740

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

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

    
746
        Map<TreeIndex, Integer> treeIndexSortIndexMap = new HashMap<>();
747
        TreeIndex lastTreeIndex = null;
748
        for (TreeIndex treeIndex : treeIndexList){
749
            if (lastTreeIndex != null && lastTreeIndex.hasChild(treeIndex)){
750
                treeIndexSortIndexMap.remove(lastTreeIndex);
751
            }
752
            treeIndexSortIndexMap.put(treeIndex, treeIndexSortIndexMapTmp.get(treeIndex));
753
            lastTreeIndex = treeIndex;
754
        }
755

    
756
        //get taxonID for treeIndexes
757
        Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(treeIndexSortIndexMap.keySet());
758

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

    
780
        return result;
781
    }
782

    
783
    /**
784
     * {@inheritDoc}
785
     */
786
    @Override
787
    public List<GroupedTaxonDTO> groupTaxaByMarkedParents(List<UUID> originalTaxonUuids, UUID classificationUuid,
788
            MarkerType markerType, Boolean flag) {
789

    
790
        List<GroupedTaxonDTO> result = new ArrayList<>();
791

    
792
        //get treeindex for each taxonUUID
793
        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
794

    
795
        //get all marked tree indexes
796
        Set<TreeIndex> markedTreeIndexes = dao.getMarkedTreeIndexes(markerType, flag);
797

    
798

    
799
        Map<TreeIndex, TreeIndex> groupedMap = TreeIndex.group(markedTreeIndexes, taxonIdTreeIndexMap.values());
800
        Set<TreeIndex> notNullGroups = new HashSet<>(groupedMap.values());
801
        notNullGroups.remove(null);
802

    
803
        //get taxonInfo for treeIndexes
804
        Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(notNullGroups);
805

    
806
        //fill result list
807
        for (UUID originalTaxonUuid : originalTaxonUuids){
808
            GroupedTaxonDTO item = new GroupedTaxonDTO();
809
            result.add(item);
810
            item.setTaxonUuid(originalTaxonUuid);
811

    
812
            TreeIndex toBeGroupedTreeIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
813
            TreeIndex groupTreeIndex = groupedMap.get(toBeGroupedTreeIndex);
814
            UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupTreeIndex);
815
            if (uuidAndLabel != null){
816
                item.setGroupTaxonUuid(uuidAndLabel.getUuid());
817
                item.setGroupTaxonName(uuidAndLabel.getTitleCache());
818
            }
819
        }
820

    
821
        return result;
822
    }
823

    
824
    /**
825
     * {@inheritDoc}
826
     */
827
    @Override
828
    public UUID getTaxonNodeUuidByTaxonUuid(UUID classificationUuid, UUID taxonUuid) {
829
        Map<UUID, UUID> map = dao.getTaxonNodeUuidByTaxonUuid(classificationUuid, Arrays.asList(taxonUuid));
830
        UUID taxonNodeUuid = map.get(taxonUuid);
831
        return taxonNodeUuid;
832
    }
833

    
834
    /**
835
     * {@inheritDoc}
836
     */
837
    @Override
838
    public TaxonInContextDTO getTaxonInContext(UUID classificationUuid, UUID taxonBaseUuid,
839
            Boolean doChildren, Boolean doSynonyms, boolean includeUnpublished, List<UUID> ancestorMarkers,
840
            NodeSortMode sortMode) {
841
        TaxonInContextDTO result = new TaxonInContextDTO();
842

    
843
        TaxonBase<?> taxonBase = taxonDao.load(taxonBaseUuid);
844
        if (taxonBase == null){
845
            throw new EntityNotFoundException("Taxon with uuid " + taxonBaseUuid + " not found in datasource");
846
        }
847
        boolean isSynonym = false;
848
        Taxon acceptedTaxon;
849
        if (taxonBase.isInstanceOf(Synonym.class)){
850
            isSynonym = true;
851
            Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
852
            acceptedTaxon = synonym.getAcceptedTaxon();
853
            if (acceptedTaxon == null) {
854
                throw new EntityNotFoundException("Accepted taxon not found for synonym"  );
855
            }
856
            TaxonStatus taxonStatus = TaxonStatus.Synonym;
857
            if (synonym.getName()!= null && acceptedTaxon.getName() != null
858
                    && synonym.getName().getHomotypicalGroup().equals(acceptedTaxon.getName().getHomotypicalGroup())){
859
                taxonStatus = TaxonStatus.SynonymObjective;
860
            }
861
            result.setTaxonStatus(taxonStatus);
862

    
863
        }else{
864
            acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
865
            result.setTaxonStatus(TaxonStatus.Accepted);
866
        }
867
        UUID acceptedTaxonUuid = acceptedTaxon.getUuid();
868

    
869
        UUID taxonNodeUuid = getTaxonNodeUuidByTaxonUuid(classificationUuid, acceptedTaxonUuid);
870
        if (taxonNodeUuid == null) {
871
            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 );
872
        }
873
        result.setTaxonNodeUuid(taxonNodeUuid);
874

    
875
        //TODO make it a dao call
876
        Taxon parentTaxon = getParentTaxon(classificationUuid, acceptedTaxon);
877
        if (parentTaxon != null){
878
            result.setParentTaxonUuid(parentTaxon.getUuid());
879
            result.setParentTaxonLabel(parentTaxon.getTitleCache());
880
            if (parentTaxon.getName() != null){
881
                result.setParentNameLabel(parentTaxon.getName().getTitleCache());
882
            }
883
        }
884

    
885
        result.setTaxonUuid(taxonBaseUuid);
886
        result.setClassificationUuid(classificationUuid);
887
        if (taxonBase.getSec() != null){
888
            result.setSecundumUuid(taxonBase.getSec().getUuid());
889
            result.setSecundumLabel(taxonBase.getSec().getTitleCache());
890
        }
891
        result.setTaxonLabel(taxonBase.getTitleCache());
892

    
893
        TaxonName name = taxonBase.getName();
894
        result.setNameUuid(name.getUuid());
895
        result.setNameLabel(name.getTitleCache());
896
        result.setNameWithoutAuthor(name.getNameCache());
897
        result.setGenusOrUninomial(name.getGenusOrUninomial());
898
        result.setInfraGenericEpithet(name.getInfraGenericEpithet());
899
        result.setSpeciesEpithet(name.getSpecificEpithet());
900
        result.setInfraSpecificEpithet(name.getInfraSpecificEpithet());
901

    
902
        result.setAuthorship(name.getAuthorshipCache());
903

    
904
        Rank rank = name.getRank();
905
        if (rank != null){
906
            result.setRankUuid(rank.getUuid());
907
            String rankLabel = rank.getAbbreviation();
908
            if (StringUtils.isBlank(rankLabel)){
909
                rankLabel = rank.getLabel();
910
            }
911
            result.setRankLabel(rankLabel);
912
        }
913

    
914
        boolean recursive = false;
915
        Integer pageSize = null;
916
        Integer pageIndex = null;
917
        Pager<TaxonNodeDto> children = taxonNodeService.pageChildNodesDTOs(taxonNodeUuid, recursive, includeUnpublished, doSynonyms,
918
                sortMode, pageSize, pageIndex);
919

    
920
        //children
921
        if(! isSynonym) {
922
            for (TaxonNodeDto childDto : children.getRecords()){
923
                if (doChildren && childDto.getStatus().equals(TaxonStatus.Accepted)){
924
                    EntityDTO<Taxon> child = new EntityDTO<Taxon>(childDto.getTaxonUuid(), childDto.getTitleCache());
925
                    result.addChild(child);
926
                }else if (doSynonyms && childDto.getStatus().isSynonym()){
927
                    EntityDTO<Synonym> child = new EntityDTO<Synonym>(childDto.getTaxonUuid(), childDto.getTitleCache());
928
                    result.addSynonym(child);
929
                }
930
            }
931
        }else{
932
            result.setAcceptedTaxonUuid(acceptedTaxonUuid);
933
            String nameTitel = acceptedTaxon.getName() == null ? null : acceptedTaxon.getName().getTitleCache();
934
            result.setAcceptedTaxonLabel(acceptedTaxon.getTitleCache());
935
            result.setAcceptedNameLabel(nameTitel);
936
        }
937

    
938
        //marked ancestors
939
        if (ancestorMarkers != null && !ancestorMarkers.isEmpty()){
940
            List<DefinedTermBase> markerTypesTerms = termDao.list(ancestorMarkers, pageSize, null, null, null);
941
            List<MarkerType> markerTypes = new ArrayList<>();
942
            for (DefinedTermBase<?> term : markerTypesTerms){
943
                if (term.isInstanceOf(MarkerType.class)){
944
                    markerTypes.add(CdmBase.deproxy(term, MarkerType.class));
945
                }
946
            }
947
            if (! markerTypes.isEmpty()){
948
                TaxonNode node = taxonNodeDao.findByUuid(taxonNodeUuid);
949
                handleAncestorsForMarkersRecursive(result, markerTypes, node);
950
            }
951
        }
952

    
953
        return result;
954
    }
955

    
956
    /**
957
     * @param classificationUuid
958
     * @param acceptedTaxon
959
     * @return
960
     */
961
    private Taxon getParentTaxon(UUID classificationUuid, Taxon acceptedTaxon) {
962
        if (classificationUuid == null){
963
            return null;
964
        }
965
        TaxonNode parent = null;
966
        for (TaxonNode node : acceptedTaxon.getTaxonNodes()){
967
            if (classificationUuid.equals(node.getClassification().getUuid())){
968
                parent = node.getParent();
969
            }
970
        }
971
        if (parent != null){
972
            return parent.getTaxon();
973
        }
974
        return null;
975
    }
976

    
977
    /**
978
     * @param result
979
     * @param markerTypes
980
     * @param node
981
     */
982
    private void handleAncestorsForMarkersRecursive(TaxonInContextDTO result, List<MarkerType> markerTypes, TaxonNode node) {
983
       for (MarkerType type : markerTypes){
984
            Taxon taxon = node.getTaxon();
985
            if (taxon != null && taxon.hasMarker(type, true)){
986
                String label =  taxon.getName() == null? taxon.getTitleCache() : taxon.getName().getTitleCache();
987
                MarkedEntityDTO<Taxon> dto = new MarkedEntityDTO<>(type, true, taxon.getUuid(), label);
988
                result.addMarkedAncestor(dto);
989
            }
990
        }
991
        TaxonNode parentNode = node.getParent();
992
        if (parentNode != null){
993
            handleAncestorsForMarkersRecursive(result, markerTypes, parentNode);
994
        }
995
    }
996

    
997
    /**
998
     * {@inheritDoc}
999
     */
1000
    @Override
1001
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1002
            Classification classification) {
1003
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, false);
1004
    }
1005

    
1006
    /**
1007
     * {@inheritDoc}
1008
     */
1009
    @Override
1010
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1011
            UUID classificationUuid) {
1012
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classificationUuid, false);
1013
    }
1014

    
1015
    /**
1016
     * {@inheritDoc}
1017
     */
1018
    @Override
1019
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1020
            UUID classificationUuid, Integer limit, String pattern) {
1021
        return  getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classificationUuid,  limit, pattern, false);
1022
    }
1023

    
1024
    /**
1025
     * {@inheritDoc}
1026
     */
1027
    @Override
1028
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1029
            Classification classification, Integer limit, String pattern) {
1030
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, limit, pattern, false);
1031
    }
1032

    
1033

    
1034
}
(7-7/106)