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