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