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