ref #8162 move term related classes from persistence(.dao.)common to .term
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / ClassificationServiceImpl.java
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.FilterException;
47 import eu.etaxonomy.cdm.exception.UnpublishedException;
48 import eu.etaxonomy.cdm.hibernate.HHH_9751_Util;
49 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
50 import eu.etaxonomy.cdm.model.common.CdmBase;
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.model.term.DefinedTermBase;
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.dao.term.IDefinedTermDao;
77 import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
78 import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
79 import eu.etaxonomy.cdm.persistence.dto.TaxonStatus;
80 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
81 import eu.etaxonomy.cdm.persistence.query.OrderHint;
82 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
83 import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
84
85 /**
86 * @author n.hoffmann
87 * @since Sep 21, 2009
88 */
89 @Service
90 @Transactional(readOnly = true)
91 public class ClassificationServiceImpl
92 extends IdentifiableServiceBase<Classification, IClassificationDao>
93 implements IClassificationService {
94 private static final Logger logger = Logger.getLogger(ClassificationServiceImpl.class);
95
96 @Autowired
97 private ITaxonNodeDao taxonNodeDao;
98
99 @Autowired
100 private ITaxonDao taxonDao;
101
102 @Autowired
103 private ITaxonNodeService taxonNodeService;
104
105 @Autowired
106 private IDefinedTermDao termDao;
107
108 @Autowired
109 private IBeanInitializer defaultBeanInitializer;
110
111 @Override
112 @Autowired
113 protected void setDao(IClassificationDao dao) {
114 this.dao = dao;
115 }
116
117 private Comparator<? super TaxonNode> taxonNodeComparator;
118
119 @Autowired
120 public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){
121 this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;
122 }
123
124 @Override
125 public TaxonNode loadTaxonNodeByTaxon(Taxon taxon, UUID classificationUuid, List<String> propertyPaths){
126 Classification tree = dao.load(classificationUuid);
127 TaxonNode node = tree.getNode(taxon);
128
129 return loadTaxonNode(node.getUuid(), propertyPaths);
130 }
131
132 public TaxonNode loadTaxonNode(UUID taxonNodeUuid, List<String> propertyPaths){
133 return taxonNodeDao.load(taxonNodeUuid, propertyPaths);
134 }
135
136 @Override
137 @Transactional(readOnly = false)
138 public UpdateResult cloneClassification(UUID classificationUuid,
139 String name, Reference sec, TaxonRelationshipType relationshipType) {
140 UpdateResult result = new UpdateResult();
141 Classification classification = load(classificationUuid);
142 Classification clone = Classification.NewInstance(name);
143 clone.setReference(sec);
144
145 //clone taxa and taxon nodes
146 List<TaxonNode> childNodes = classification.getRootNode().getChildNodes();
147 for (TaxonNode taxonNode : childNodes) {
148 addChildTaxa(taxonNode, null, clone, relationshipType);
149 }
150 dao.saveOrUpdate(clone);
151 result.setCdmEntity(clone);
152 return result;
153 }
154
155 private void addChildTaxa(TaxonNode originalParentNode, TaxonNode cloneParentNode, Classification classification, TaxonRelationshipType relationshipType){
156 Reference reference = classification.getReference();
157 Taxon cloneTaxon = (Taxon) HibernateProxyHelper.deproxy(originalParentNode.getTaxon(), Taxon.class).clone();
158 cloneTaxon.setSec(reference);
159 String microReference = null;
160 List<TaxonNode> originalChildNodes = originalParentNode.getChildNodes();
161 HHH_9751_Util.removeAllNull(originalChildNodes);
162
163 //add relation between taxa
164 if (relationshipType != null){
165 cloneTaxon.addTaxonRelation(originalParentNode.getTaxon(), relationshipType, reference, microReference);
166 }
167
168 TaxonNode cloneChildNode = null;
169 //add taxon node to either parent node or classification (no parent node)
170 if(cloneParentNode==null){
171 cloneChildNode = classification.addChildTaxon(cloneTaxon, reference, microReference);
172 }
173 else{
174 cloneChildNode = cloneParentNode.addChildTaxon(cloneTaxon, reference, microReference);
175 }
176 taxonNodeDao.saveOrUpdate(cloneChildNode);
177 //add children
178 for (TaxonNode originalChildNode : originalChildNodes) {
179 addChildTaxa(originalChildNode, cloneChildNode, classification, relationshipType);
180 }
181 }
182
183 @Override
184 public List<TaxonNode> listRankSpecificRootNodes(Classification classification, Rank rank,
185 boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
186 return listRankSpecificRootNodes(classification, null, rank, includeUnpublished, pageSize, pageIndex, propertyPaths);
187 }
188
189 /**
190 * {@inheritDoc}
191 */
192 @Override
193 public List<TaxonNode> listRankSpecificRootNodes(Classification classification,
194 TaxonNode subtree, Rank rank,
195 boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
196 return pageRankSpecificRootNodes(classification, subtree, rank, includeUnpublished, pageSize, pageIndex, propertyPaths).getRecords();
197 }
198
199 @Override
200 public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, Rank rank,
201 boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
202 return pageRankSpecificRootNodes(classification, null, rank, includeUnpublished, pageSize, pageIndex, propertyPaths);
203 }
204
205 @Override
206 public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, TaxonNode subtree, Rank rank,
207 boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
208 long[] numberOfResults = dao.countRankSpecificRootNodes(classification, subtree, includeUnpublished, rank);
209 long totalNumberOfResults = numberOfResults[0] + (numberOfResults.length > 1 ? numberOfResults[1] : 0);
210
211 List<TaxonNode> results = new ArrayList<>();
212
213 if (AbstractPagerImpl.hasResultsInRange(totalNumberOfResults, pageIndex, pageSize)) { // no point checking again
214 Integer limit = PagerUtils.limitFor(pageSize);
215 Integer start = PagerUtils.startFor(pageSize, pageIndex);
216
217 Integer remainingLimit = limit;
218 int[] queryIndexes = rank == null ? new int[]{0} : new int[]{0,1};
219
220 for(int queryIndex: queryIndexes) {
221 if(start != null && start > numberOfResults[queryIndex]) {
222 // start in next query with new start value
223 start = start - (int)numberOfResults[queryIndex];
224 continue;
225 }
226
227 List<TaxonNode> perQueryResults = dao.listRankSpecificRootNodes(classification,
228 subtree, rank, includeUnpublished, remainingLimit,
229 start, propertyPaths, queryIndex);
230 results.addAll(perQueryResults);
231 if(remainingLimit != null ){
232 remainingLimit = remainingLimit - results.size();
233 if(remainingLimit <= 0) {
234 // no need to run further queries if first query returned enough items!
235 break;
236 }
237 // start at with fist item of next query to fetch the remaining items
238 start = 0;
239 }
240 }
241 }
242 // long start_t = System.currentTimeMillis();
243 Collections.sort(results, taxonNodeComparator); // TODO is ordering during the hibernate query in the dao possible?
244 // System.err.println("service.pageRankSpecificRootNodes() - Collections.sort(results, taxonNodeComparator) " + (System.currentTimeMillis() - start_t));
245 return new DefaultPagerImpl<>(pageIndex, totalNumberOfResults, pageSize, results);
246
247 }
248
249 @Override
250 public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, Rank baseRank,
251 boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
252 return loadTreeBranch(taxonNode, null, baseRank, includeUnpublished, propertyPaths);
253 }
254
255 /**
256 * {@inheritDoc}
257 */
258 @Override
259 public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, TaxonNode subtree, Rank baseRank,
260 boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
261
262 TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
263 if(baseRank != null){
264 baseRank = (Rank) termDao.load(baseRank.getUuid());
265 }
266 if (!includeUnpublished && thisNode.getTaxon() != null && !thisNode.getTaxon().isPublish()){
267 throw new UnpublishedException("Final taxon in tree branch is unpublished.");
268 }
269
270 List<TaxonNode> pathToRoot = new ArrayList<>();
271 pathToRoot.add(thisNode);
272
273 while(!thisNode.isTopmostNode()){
274 //TODO why do we need to deproxy here?
275 // without this thisNode.getParent() will return NULL in
276 // some cases (environment dependend?) even if the parent exits
277 TaxonNode parentNode = CdmBase.deproxy(thisNode).getParent();
278
279 if(parentNode == null){
280 throw new NullPointerException("Taxon node " + thisNode + " must have a parent since it is not top most");
281 }
282 if(parentNode.getTaxon() == null){
283 throw new NullPointerException("The taxon associated with taxon node " + parentNode + " is NULL");
284 }
285 if(!includeUnpublished && !parentNode.getTaxon().isPublish()){
286 throw new UnpublishedException("Some taxon in tree branch is unpublished.");
287 }
288 if(parentNode.getTaxon().getName() == null){
289 throw new NullPointerException("The name of the taxon associated with taxonNode " + parentNode + " is NULL");
290 }
291
292 Rank parentNodeRank = (parentNode.getTaxon().getName() == null) ? null : parentNode.getTaxon().getName().getRank();
293 // stop if the next parent is higher than the baseRank
294 if(baseRank != null && parentNodeRank != null && baseRank.isLower(parentNodeRank)){
295 break;
296 }
297 if((subtree!= null && !subtree.isAncestor(parentNode) )){
298 break;
299 }
300
301 pathToRoot.add(parentNode);
302 thisNode = parentNode;
303 }
304
305 // initialize and invert order of nodes in list
306 defaultBeanInitializer.initializeAll(pathToRoot, propertyPaths);
307 Collections.reverse(pathToRoot);
308
309 return pathToRoot;
310 }
311
312 @Override
313 public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification, Rank baseRank,
314 boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
315 return loadTreeBranchToTaxon(taxon, classification, null, baseRank, includeUnpublished, propertyPaths);
316 }
317
318 @Override
319 public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification,
320 TaxonNode subtree, Rank baseRank,
321 boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
322
323 UUID nodeUuid = getTaxonNodeUuidByTaxonUuid(classification.getUuid(), taxon.getUuid());
324 TaxonNode node = taxonNodeService.find(nodeUuid);
325 if(node == null){
326 logger.warn("The specified taxon is not found in the given tree.");
327 return null;
328 }else if (subtree != null && !node.isDescendant(subtree)){
329 //TODO handle as exception? E.g. FilterException, AccessDeniedException?
330 logger.warn("The specified taxon is not found for the given subtree.");
331 return null;
332 }
333
334 return loadTreeBranch(node, subtree, baseRank, includeUnpublished, propertyPaths);
335 }
336
337
338 @Override
339 public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
340 List<String> propertyPaths) {
341 taxonNode = taxonNodeDao.load(taxonNode.getUuid());
342 List<TaxonNode> childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
343 defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
344 Collections.sort(childNodes, taxonNodeComparator);
345 return childNodes;
346 }
347
348 @Override
349 public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid,
350 boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths){
351 try {
352 return listChildNodesOfTaxon(taxonUuid, classificationUuid, null, includeUnpublished, pageSize, pageIndex, propertyPaths);
353 } catch (FilterException e) {
354 throw new RuntimeException(e); //this should not happen as filter is null
355 }
356 }
357
358 @Override
359 public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid, UUID subtreeUuid,
360 boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) throws FilterException{
361
362 Classification classification = dao.load(classificationUuid);
363 Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
364 TaxonNode subtree = taxonNodeDao.load(subtreeUuid);
365 if (subtreeUuid != null && subtree == null){
366 throw new FilterException("Taxon node for subtree filter can not be found in database", true);
367 }
368
369 List<TaxonNode> results = dao.listChildrenOf(
370 taxon, classification, subtree, includeUnpublished, pageSize, pageIndex, propertyPaths);
371 Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
372 return results;
373 }
374
375 @Override
376 public Pager<TaxonNode> pageSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, boolean includeUnpublished,
377 Integer pageSize, Integer pageIndex, List<String> propertyPaths){
378
379 Classification classification = dao.load(classificationUuid);
380 Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
381
382 long numberOfResults = dao.countSiblingsOf(taxon, classification, includeUnpublished);
383
384 List<TaxonNode> results;
385 if(PagerUtils.hasResultsInRange(numberOfResults, pageIndex, pageSize)) {
386 results = dao.listSiblingsOf(taxon, classification, includeUnpublished, pageSize, pageIndex, propertyPaths);
387 Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
388 } else {
389 results = new ArrayList<>();
390 }
391
392 return new DefaultPagerImpl<>(pageIndex, numberOfResults, pageSize, results);
393 }
394
395 @Override
396 public List<TaxonNode> listSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, boolean includeUnpublished,
397 Integer pageSize, Integer pageIndex, List<String> propertyPaths){
398
399 Pager<TaxonNode> pager = pageSiblingsOfTaxon(taxonUuid, classificationUuid, includeUnpublished, pageSize, pageIndex, propertyPaths);
400 return pager.getRecords();
401 }
402
403 @Override
404 public ITaxonTreeNode getTreeNodeByUuid(UUID uuid){
405 ITaxonTreeNode treeNode = taxonNodeDao.findByUuid(uuid);
406 if(treeNode == null){
407 treeNode = dao.findByUuid(uuid);
408 }
409
410 return treeNode;
411 }
412
413 @Override
414 public TaxonNode getRootNode(UUID classificationUuid){
415 return dao.getRootNode(classificationUuid);
416 }
417
418 @Override
419 public List<Classification> listClassifications(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
420 return dao.list(limit, start, orderHints, propertyPaths);
421 }
422
423 @Override
424 public UUID removeTaxonNode(TaxonNode taxonNode) {
425 return taxonNodeDao.delete(taxonNode);
426 }
427 @Override
428 public UUID removeTreeNode(ITaxonTreeNode treeNode) {
429 if(treeNode instanceof Classification){
430 return dao.delete((Classification) treeNode);
431 }else if(treeNode instanceof TaxonNode){
432 return taxonNodeDao.delete((TaxonNode)treeNode);
433 }
434 return null;
435 }
436 @Override
437 public UUID saveTaxonNode(TaxonNode taxonNode) {
438 return taxonNodeDao.save(taxonNode).getUuid();
439 }
440
441 @Override
442 public Map<UUID, TaxonNode> saveTaxonNodeAll(
443 Collection<TaxonNode> taxonNodeCollection) {
444 return taxonNodeDao.saveAll(taxonNodeCollection);
445 }
446
447 @Override
448 public UUID saveClassification(Classification classification) {
449
450 taxonNodeDao.saveOrUpdateAll(classification.getAllNodes());
451 UUID result =dao.saveOrUpdate(classification);
452 return result;
453 }
454
455 @Override
456 public UUID saveTreeNode(ITaxonTreeNode treeNode) {
457 if(treeNode instanceof Classification){
458 return dao.save((Classification) treeNode).getUuid();
459 }else if(treeNode instanceof TaxonNode){
460 return taxonNodeDao.save((TaxonNode)treeNode).getUuid();
461 }
462 return null;
463 }
464
465 @Override
466 public List<TaxonNode> getAllNodes(){
467 return taxonNodeDao.list(null,null);
468 }
469
470 @Override
471 public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, Integer limit, String pattern, boolean searchForClassifications) {
472 return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), limit, pattern, searchForClassifications);
473 }
474
475 @Override
476 public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, Integer limit, String pattern, boolean searchForClassifications) {
477 return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, limit, pattern, searchForClassifications);
478 }
479
480 @Override
481 public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, boolean searchForClassifications ) {
482 return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), null, null, searchForClassifications);
483 }
484
485 @Override
486 public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, boolean searchForClassifications ) {
487 return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, null, null, searchForClassifications);
488 }
489
490 @Override
491 public List<UuidAndTitleCache<Classification>> getUuidAndTitleCache(Integer limit, String pattern) {
492 return dao.getUuidAndTitleCache(limit, pattern);
493 }
494
495 @Override
496 public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(
497 TaxonNode taxonNode, List<String> propertyPaths, int size,
498 int height, int widthOrDuration, String[] mimeTypes) {
499
500 TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<>();
501 List<MediaRepresentation> mediaRepresentations = new ArrayList<>();
502
503 //add all media of the children to the result map
504 if (taxonNode != null){
505
506 List<TaxonNode> nodes = new ArrayList<>();
507
508 nodes.add(loadTaxonNode(taxonNode.getUuid(), propertyPaths));
509 nodes.addAll(loadChildNodesOfTaxonNode(taxonNode, propertyPaths));
510
511 for(TaxonNode node : nodes){
512 Taxon taxon = node.getTaxon();
513 for (TaxonDescription taxonDescription: taxon.getDescriptions()){
514 for (DescriptionElementBase descriptionElement: taxonDescription.getElements()){
515 for(Media media : descriptionElement.getMedia()){
516 //find the best matching representation
517 mediaRepresentations.add(MediaUtils.findBestMatchingRepresentation(media,null, size, height, widthOrDuration, mimeTypes));
518 }
519 }
520 }
521 result.put(taxon.getUuid(), mediaRepresentations);
522 }
523 }
524
525 return result;
526 }
527
528 @Override
529 @Transactional(readOnly = false)
530 public UpdateResult updateCaches(Class<? extends Classification> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Classification> cacheStrategy, IProgressMonitor monitor) {
531 if (clazz == null){
532 clazz = Classification.class;
533 }
534 return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
535 }
536
537 /**
538 *
539 * @param allNodesOfClassification
540 * @return null - if allNodesOfClassification is empty <br>
541 */
542
543 private Map<String, List<TaxonNode>> getSortedGenusList(Collection<TaxonNode> allNodesOfClassification){
544
545 if(allNodesOfClassification == null || allNodesOfClassification.isEmpty()){
546 return null;
547 }
548 Map<String, List<TaxonNode>> sortedGenusMap = new HashMap<>();
549 for(TaxonNode node:allNodesOfClassification){
550 Taxon taxon = node.getTaxon();
551 INonViralName name = taxon.getName();
552 String genusOrUninomial = name.getGenusOrUninomial();
553 //if rank unknown split string and take first word
554 if(genusOrUninomial == null){
555 String titleCache = taxon.getTitleCache();
556 String[] split = titleCache.split("\\s+");
557 for(String s:split){
558 genusOrUninomial = s;
559 break;
560 }
561 }
562 //if node has children
563
564 //retrieve list from map if not create List
565 if(sortedGenusMap.containsKey(genusOrUninomial)){
566 List<TaxonNode> list = sortedGenusMap.get(genusOrUninomial);
567 list.add(node);
568 sortedGenusMap.put(genusOrUninomial, list);
569 }else{
570 //create List for genus
571 List<TaxonNode> list = new ArrayList<>();
572 list.add(node);
573 sortedGenusMap.put(genusOrUninomial, list);
574 }
575 }
576 return sortedGenusMap;
577 }
578
579 /**
580 *
581 * creates new Classification and parent TaxonNodes at genus level
582 *
583 *
584 * @param map GenusMap which holds a name (Genus) and all the same Taxa as a list
585 * @param classification you want to improve the hierarchy (will not be modified)
586 * @param configurator to change certain settings, if null then standard settings will be taken
587 * @return new classification with parentNodes for each entry in the map
588 */
589 @SuppressWarnings({ "unchecked" })
590 @Transactional(readOnly = false)
591 @Override
592 public UpdateResult createHierarchyInClassification(Classification classification, CreateHierarchyForClassificationConfigurator configurator){
593 UpdateResult result = new UpdateResult();
594 classification = dao.findByUuid(classification.getUuid());
595 Map<String, List<TaxonNode>> map = getSortedGenusList(classification.getAllNodes());
596
597 final String APPENDIX = "repaired";
598 String titleCache = StringUtils.isBlank(classification.getTitleCache()) ? " " : classification.getTitleCache() ;
599 //TODO classification clone???
600 Classification newClassification = Classification.NewInstance(titleCache +" "+ APPENDIX);
601 newClassification.setReference(classification.getReference());
602
603 for(Map.Entry<String, List<TaxonNode>> entry:map.entrySet()){
604 String genus = entry.getKey();
605 List<TaxonNode> listOfTaxonNodes = entry.getValue();
606 TaxonNode parentNode = null;
607 //Search for genus in list
608 for(TaxonNode tNode:listOfTaxonNodes){
609 //take that taxonNode as parent and remove from list with all it possible children
610 //FIXME NPE for name
611 TaxonName name = tNode.getTaxon().getName();
612 if(name.getNameCache().equalsIgnoreCase(genus)){
613 TaxonNode clone = (TaxonNode) tNode.clone();
614 if(!tNode.hasChildNodes()){
615 //FIXME remove classification
616 // parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
617 parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
618 //remove taxonNode from list because just added to classification
619 result.addUpdatedObject(tNode);
620 listOfTaxonNodes.remove(tNode);
621 }else{
622 //get all childNodes
623 //save prior Hierarchy and remove them from the list
624 List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tNode, clone, result);
625 // parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
626 //FIXME remove classification
627 parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
628 //remove taxonNode from list because just added to classification
629 result.addUpdatedObject(tNode);
630 listOfTaxonNodes.remove(tNode);
631 if(copyAllChildrenToTaxonNode != null){
632 listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
633 }
634 }
635 break;
636 }
637 }
638 if(parentNode == null){
639 //if no match found in list, create parentNode
640 NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
641 TaxonName TaxonName = (TaxonName)parser.parseFullName(genus);
642 //TODO Sec via configurator
643 Taxon taxon = Taxon.NewInstance(TaxonName, null);
644 parentNode = newClassification.addChildTaxon(taxon, 0, null, null);
645 result.addUpdatedObject(parentNode);
646 }
647 //iterate over the rest of the list
648 for(TaxonNode tn : listOfTaxonNodes){
649 //if TaxonNode has a parent and this is not the classification then skip it
650 //and add to new classification via the parentNode as children of it
651 //this should assures to keep the already existing hierarchy
652 //FIXME: Assert is not rootnode --> entrypoint is not classification in future but rather rootNode
653
654 if(!tn.isTopmostNode()){
655 continue; //skip to next taxonNode
656 }
657
658 TaxonNode clone = (TaxonNode) tn.clone();
659 //FIXME: citation from node
660 //TODO: addchildNode without citation and references
661 // TaxonNode taxonNode = parentNode.addChildNode(clone, classification.getCitation(), classification.getMicroReference());
662 TaxonNode taxonNode = parentNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
663 result.addUnChangedObject(clone);
664 if(tn.hasChildNodes()){
665 //save hierarchy in new classification
666 List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tn, taxonNode, result);
667 if(copyAllChildrenToTaxonNode != null){
668 listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
669 }
670 }
671 }
672 }
673 dao.saveOrUpdate(newClassification);
674 result.setCdmEntity(newClassification);
675 return result;
676 }
677
678 /**
679 *
680 * recursive method to get all childnodes of taxonNode in classification.
681 *
682 * @param classification just for References and Citation, can be null
683 * @param copyFromNode TaxonNode with Children
684 * @param copyToNode TaxonNode which will receive the children
685 * @return List of ChildNode which has been added. If node has no children returns null
686 */
687 private List<TaxonNode> copyAllChildrenToTaxonNode(TaxonNode copyFromNode, TaxonNode copyToNode, UpdateResult result) {
688 List<TaxonNode> childNodes;
689 if(!copyFromNode.hasChildNodes()){
690 return null;
691 }else{
692 childNodes = copyFromNode.getChildNodes();
693 }
694 for(TaxonNode childNode:childNodes){
695 TaxonNode clone = (TaxonNode) childNode.clone();
696 result.addUnChangedObject(clone);
697 if(childNode.hasChildNodes()){
698 copyAllChildrenToTaxonNode(childNode, clone, result);
699 }
700 //FIXME: citation from node instead of classification
701 // copyToNode.addChildNode(clone,classification.getCitation(), classification.getMicroReference());
702 copyToNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
703 }
704 return childNodes;
705 }
706
707 /**
708 * {@inheritDoc}
709 */
710 @Override
711 public ClassificationLookupDTO classificationLookup(Classification classification) {
712 return dao.classificationLookup(classification);
713 }
714
715
716 @Override
717 @Transactional
718 public DeleteResult delete(UUID classificationUuid, TaxonDeletionConfigurator config){
719 DeleteResult result = new DeleteResult();
720 Classification classification = dao.findByUuid(classificationUuid);
721 if (classification == null){
722 result.addException(new IllegalArgumentException("The classification does not exist in database."));
723 result.setAbort();
724 return result;
725 }
726 if (!classification.hasChildNodes()){
727 dao.delete(classification);
728 result.addDeletedObject(classification);
729 return result;
730 }
731 if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){
732 // TaxonNode root = classification.getRootNode();
733 // result.includeResult(taxonNodeService.deleteTaxonNode(HibernateProxyHelper.deproxy(root), config));
734 // result.addDeletedObject(classification);
735 dao.delete(classification);
736 result.addDeletedObject(classification);
737 return result;
738 }
739
740
741 return result;
742 }
743
744 @Override
745 public List<GroupedTaxonDTO> groupTaxaByHigherTaxon(List<UUID> originalTaxonUuids, UUID classificationUuid, Rank minRank, Rank maxRank){
746 List<GroupedTaxonDTO> result = new ArrayList<>();
747
748 //get treeindex for each taxonUUID
749 Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
750
751 //build treeindex list (or tree)
752 //TODO make it work with TreeIndex or move there
753 List<String> treeIndexClosureStr = new ArrayList<>();
754 for (TreeIndex treeIndex : taxonIdTreeIndexMap.values()){
755 String[] splits = treeIndex.toString().substring(1).split(ITreeNode.separator);
756 String currentIndex = ITreeNode.separator;
757 for (String split : splits){
758 if (split.equals("")){
759 continue;
760 }
761 currentIndex += split + ITreeNode.separator;
762 if (!treeIndexClosureStr.contains(currentIndex) && !split.startsWith(ITreeNode.treePrefix)){
763 treeIndexClosureStr.add(currentIndex);
764 }
765 }
766 }
767
768 //get rank sortindex for all parent taxa with sortindex <= minRank and sortIndex >= maxRank (if available)
769 Integer minRankOrderIndex = minRank == null ? null : minRank.getOrderIndex();
770 Integer maxRankOrderIndex = maxRank == null ? null : maxRank.getOrderIndex();
771 List<TreeIndex> treeIndexClosure = TreeIndex.NewListInstance(treeIndexClosureStr);
772
773 Map<TreeIndex, Integer> treeIndexSortIndexMapTmp = taxonNodeDao.rankOrderIndexForTreeIndex(treeIndexClosure, minRankOrderIndex, maxRankOrderIndex);
774
775 //remove all treeindex with "exists child in above map(and child.sortindex > xxx)
776 List<TreeIndex> treeIndexList = TreeIndex.sort(treeIndexSortIndexMapTmp.keySet());
777
778 Map<TreeIndex, Integer> treeIndexSortIndexMap = new HashMap<>();
779 TreeIndex lastTreeIndex = null;
780 for (TreeIndex treeIndex : treeIndexList){
781 if (lastTreeIndex != null && lastTreeIndex.hasChild(treeIndex)){
782 treeIndexSortIndexMap.remove(lastTreeIndex);
783 }
784 treeIndexSortIndexMap.put(treeIndex, treeIndexSortIndexMapTmp.get(treeIndex));
785 lastTreeIndex = treeIndex;
786 }
787
788 //get taxonID for treeIndexes
789 Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(treeIndexSortIndexMap.keySet());
790
791 //fill result list
792 for (UUID originalTaxonUuid : originalTaxonUuids){
793 GroupedTaxonDTO item = new GroupedTaxonDTO();
794 result.add(item);
795 item.setTaxonUuid(originalTaxonUuid);
796 TreeIndex groupTreeIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
797 String groupIndexX = TreeIndex.toString(groupTreeIndex);
798 while (groupTreeIndex != null){
799 if (treeIndexTaxonIdMap.get(groupTreeIndex) != null){
800 UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupTreeIndex);
801 item.setGroupTaxonUuid(uuidAndLabel.getUuid());
802 item.setGroupTaxonName(uuidAndLabel.getTitleCache());
803 break;
804 }else{
805 groupTreeIndex = groupTreeIndex.parent();
806 // int index = groupIndex.substring(0, groupIndex.length()-1).lastIndexOf(ITreeNode.separator);
807 // groupIndex = index < 0 ? null : groupIndex.substring(0, index+1);
808 }
809 }
810 }
811
812 return result;
813 }
814
815 /**
816 * {@inheritDoc}
817 */
818 @Override
819 public List<GroupedTaxonDTO> groupTaxaByMarkedParents(List<UUID> originalTaxonUuids, UUID classificationUuid,
820 MarkerType markerType, Boolean flag) {
821
822 List<GroupedTaxonDTO> result = new ArrayList<>();
823
824 //get treeindex for each taxonUUID
825 Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
826
827 //get all marked tree indexes
828 Set<TreeIndex> markedTreeIndexes = dao.getMarkedTreeIndexes(markerType, flag);
829
830
831 Map<TreeIndex, TreeIndex> groupedMap = TreeIndex.group(markedTreeIndexes, taxonIdTreeIndexMap.values());
832 Set<TreeIndex> notNullGroups = new HashSet<>(groupedMap.values());
833 notNullGroups.remove(null);
834
835 //get taxonInfo for treeIndexes
836 Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(notNullGroups);
837
838 //fill result list
839 for (UUID originalTaxonUuid : originalTaxonUuids){
840 GroupedTaxonDTO item = new GroupedTaxonDTO();
841 result.add(item);
842 item.setTaxonUuid(originalTaxonUuid);
843
844 TreeIndex toBeGroupedTreeIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
845 TreeIndex groupTreeIndex = groupedMap.get(toBeGroupedTreeIndex);
846 UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupTreeIndex);
847 if (uuidAndLabel != null){
848 item.setGroupTaxonUuid(uuidAndLabel.getUuid());
849 item.setGroupTaxonName(uuidAndLabel.getTitleCache());
850 }
851 }
852
853 return result;
854 }
855
856 /**
857 * {@inheritDoc}
858 */
859 @Override
860 public UUID getTaxonNodeUuidByTaxonUuid(UUID classificationUuid, UUID taxonUuid) {
861 Map<UUID, UUID> map = dao.getTaxonNodeUuidByTaxonUuid(classificationUuid, Arrays.asList(taxonUuid));
862 UUID taxonNodeUuid = map.get(taxonUuid);
863 return taxonNodeUuid;
864 }
865
866 /**
867 * {@inheritDoc}
868 */
869 @Override
870 public TaxonInContextDTO getTaxonInContext(UUID classificationUuid, UUID taxonBaseUuid,
871 Boolean doChildren, Boolean doSynonyms, boolean includeUnpublished, List<UUID> ancestorMarkers,
872 NodeSortMode sortMode) {
873 TaxonInContextDTO result = new TaxonInContextDTO();
874
875 TaxonBase<?> taxonBase = taxonDao.load(taxonBaseUuid);
876 if (taxonBase == null){
877 throw new EntityNotFoundException("Taxon with uuid " + taxonBaseUuid + " not found in datasource");
878 }
879 boolean isSynonym = false;
880 Taxon acceptedTaxon;
881 if (taxonBase.isInstanceOf(Synonym.class)){
882 isSynonym = true;
883 Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
884 acceptedTaxon = synonym.getAcceptedTaxon();
885 if (acceptedTaxon == null) {
886 throw new EntityNotFoundException("Accepted taxon not found for synonym" );
887 }
888 TaxonStatus taxonStatus = TaxonStatus.Synonym;
889 if (synonym.getName()!= null && acceptedTaxon.getName() != null
890 && synonym.getName().getHomotypicalGroup().equals(acceptedTaxon.getName().getHomotypicalGroup())){
891 taxonStatus = TaxonStatus.SynonymObjective;
892 }
893 result.setTaxonStatus(taxonStatus);
894
895 }else{
896 acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
897 result.setTaxonStatus(TaxonStatus.Accepted);
898 }
899 UUID acceptedTaxonUuid = acceptedTaxon.getUuid();
900
901 UUID taxonNodeUuid = getTaxonNodeUuidByTaxonUuid(classificationUuid, acceptedTaxonUuid);
902 if (taxonNodeUuid == null) {
903 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 );
904 }
905 result.setTaxonNodeUuid(taxonNodeUuid);
906
907 //TODO make it a dao call
908 Taxon parentTaxon = getParentTaxon(classificationUuid, acceptedTaxon);
909 if (parentTaxon != null){
910 result.setParentTaxonUuid(parentTaxon.getUuid());
911 result.setParentTaxonLabel(parentTaxon.getTitleCache());
912 if (parentTaxon.getName() != null){
913 result.setParentNameLabel(parentTaxon.getName().getTitleCache());
914 }
915 }
916
917 result.setTaxonUuid(taxonBaseUuid);
918 result.setClassificationUuid(classificationUuid);
919 if (taxonBase.getSec() != null){
920 result.setSecundumUuid(taxonBase.getSec().getUuid());
921 result.setSecundumLabel(taxonBase.getSec().getTitleCache());
922 }
923 result.setTaxonLabel(taxonBase.getTitleCache());
924
925 TaxonName name = taxonBase.getName();
926 result.setNameUuid(name.getUuid());
927 result.setNameLabel(name.getTitleCache());
928 result.setNameWithoutAuthor(name.getNameCache());
929 result.setGenusOrUninomial(name.getGenusOrUninomial());
930 result.setInfraGenericEpithet(name.getInfraGenericEpithet());
931 result.setSpeciesEpithet(name.getSpecificEpithet());
932 result.setInfraSpecificEpithet(name.getInfraSpecificEpithet());
933
934 result.setAuthorship(name.getAuthorshipCache());
935
936 Rank rank = name.getRank();
937 if (rank != null){
938 result.setRankUuid(rank.getUuid());
939 String rankLabel = rank.getAbbreviation();
940 if (StringUtils.isBlank(rankLabel)){
941 rankLabel = rank.getLabel();
942 }
943 result.setRankLabel(rankLabel);
944 }
945
946 boolean recursive = false;
947 Integer pageSize = null;
948 Integer pageIndex = null;
949 Pager<TaxonNodeDto> children = taxonNodeService.pageChildNodesDTOs(taxonNodeUuid, recursive, includeUnpublished, doSynonyms,
950 sortMode, pageSize, pageIndex);
951
952 //children
953 if(! isSynonym) {
954 for (TaxonNodeDto childDto : children.getRecords()){
955 if (doChildren && childDto.getStatus().equals(TaxonStatus.Accepted)){
956 EntityDTO<Taxon> child = new EntityDTO<Taxon>(childDto.getTaxonUuid(), childDto.getTitleCache());
957 result.addChild(child);
958 }else if (doSynonyms && childDto.getStatus().isSynonym()){
959 EntityDTO<Synonym> child = new EntityDTO<>(childDto.getTaxonUuid(), childDto.getTitleCache());
960 result.addSynonym(child);
961 }
962 }
963 }else{
964 result.setAcceptedTaxonUuid(acceptedTaxonUuid);
965 String nameTitel = acceptedTaxon.getName() == null ? null : acceptedTaxon.getName().getTitleCache();
966 result.setAcceptedTaxonLabel(acceptedTaxon.getTitleCache());
967 result.setAcceptedNameLabel(nameTitel);
968 }
969
970 //marked ancestors
971 if (ancestorMarkers != null && !ancestorMarkers.isEmpty()){
972 @SuppressWarnings("rawtypes")
973 List<DefinedTermBase> markerTypesTerms = termDao.list(ancestorMarkers, pageSize, null, null, null);
974 List<MarkerType> markerTypes = new ArrayList<>();
975 for (DefinedTermBase<?> term : markerTypesTerms){
976 if (term.isInstanceOf(MarkerType.class)){
977 markerTypes.add(CdmBase.deproxy(term, MarkerType.class));
978 }
979 }
980 if (! markerTypes.isEmpty()){
981 TaxonNode node = taxonNodeDao.findByUuid(taxonNodeUuid);
982 handleAncestorsForMarkersRecursive(result, markerTypes, node);
983 }
984 }
985
986 return result;
987 }
988
989 /**
990 * @param classificationUuid
991 * @param acceptedTaxon
992 * @return
993 */
994 private Taxon getParentTaxon(UUID classificationUuid, Taxon acceptedTaxon) {
995 if (classificationUuid == null){
996 return null;
997 }
998 TaxonNode parent = null;
999 for (TaxonNode node : acceptedTaxon.getTaxonNodes()){
1000 if (classificationUuid.equals(node.getClassification().getUuid())){
1001 parent = node.getParent();
1002 }
1003 }
1004 if (parent != null){
1005 return parent.getTaxon();
1006 }
1007 return null;
1008 }
1009
1010 /**
1011 * @param result
1012 * @param markerTypes
1013 * @param node
1014 */
1015 private void handleAncestorsForMarkersRecursive(TaxonInContextDTO result, List<MarkerType> markerTypes, TaxonNode node) {
1016 for (MarkerType type : markerTypes){
1017 Taxon taxon = node.getTaxon();
1018 if (taxon != null && taxon.hasMarker(type, true)){
1019 String label = taxon.getName() == null? taxon.getTitleCache() : taxon.getName().getTitleCache();
1020 MarkedEntityDTO<Taxon> dto = new MarkedEntityDTO<>(type, true, taxon.getUuid(), label);
1021 result.addMarkedAncestor(dto);
1022 }
1023 }
1024 TaxonNode parentNode = node.getParent();
1025 if (parentNode != null){
1026 handleAncestorsForMarkersRecursive(result, markerTypes, parentNode);
1027 }
1028 }
1029
1030 /**
1031 * {@inheritDoc}
1032 */
1033 @Override
1034 public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1035 Classification classification) {
1036 return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, false);
1037 }
1038
1039 /**
1040 * {@inheritDoc}
1041 */
1042 @Override
1043 public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1044 UUID classificationUuid) {
1045 return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classificationUuid, false);
1046 }
1047
1048 /**
1049 * {@inheritDoc}
1050 */
1051 @Override
1052 public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1053 UUID classificationUuid, Integer limit, String pattern) {
1054 return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classificationUuid, limit, pattern, false);
1055 }
1056
1057 /**
1058 * {@inheritDoc}
1059 */
1060 @Override
1061 public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1062 Classification classification, Integer limit, String pattern) {
1063 return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, limit, pattern, false);
1064 }
1065 }