ref #6066 add getChildNodes to classification service
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / ClassificationServiceImpl.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10
11 package eu.etaxonomy.cdm.api.service;
12
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.Comparator;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.TreeMap;
22 import java.util.UUID;
23
24 import org.apache.commons.collections.CollectionUtils;
25 import org.apache.log4j.Logger;
26 import org.springframework.beans.factory.annotation.Autowired;
27 import org.springframework.stereotype.Service;
28 import org.springframework.transaction.annotation.Transactional;
29
30 import eu.etaxonomy.cdm.api.service.config.CreateHierarchyForClassificationConfigurator;
31 import eu.etaxonomy.cdm.api.service.config.NodeDeletionConfigurator.ChildHandling;
32 import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
33 import eu.etaxonomy.cdm.api.service.dto.GroupedTaxonDTO;
34 import eu.etaxonomy.cdm.api.service.pager.Pager;
35 import eu.etaxonomy.cdm.api.service.pager.PagerUtils;
36 import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
37 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
38 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
39 import eu.etaxonomy.cdm.model.common.CdmBase;
40 import eu.etaxonomy.cdm.model.common.ITreeNode;
41 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
42 import eu.etaxonomy.cdm.model.description.TaxonDescription;
43 import eu.etaxonomy.cdm.model.media.Media;
44 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
45 import eu.etaxonomy.cdm.model.media.MediaUtils;
46 import eu.etaxonomy.cdm.model.name.NonViralName;
47 import eu.etaxonomy.cdm.model.name.Rank;
48 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
49 import eu.etaxonomy.cdm.model.taxon.Classification;
50 import eu.etaxonomy.cdm.model.taxon.ITaxonNodeComparator;
51 import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
52 import eu.etaxonomy.cdm.model.taxon.Taxon;
53 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
54 import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
55 import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
56 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
57 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
58 import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
59 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
60 import eu.etaxonomy.cdm.persistence.query.OrderHint;
61 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
62 import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
63
64 /**
65 * @author n.hoffmann
66 * @created Sep 21, 2009
67 */
68 @Service
69 @Transactional(readOnly = true)
70 public class ClassificationServiceImpl extends IdentifiableServiceBase<Classification, IClassificationDao>
71 implements IClassificationService {
72 private static final Logger logger = Logger.getLogger(ClassificationServiceImpl.class);
73
74 @Autowired
75 private ITaxonNodeDao taxonNodeDao;
76
77 @Autowired
78 private ITaxonDao taxonDao;
79
80 @Autowired
81 private IBeanInitializer defaultBeanInitializer;
82
83 @Override
84 @Autowired
85 protected void setDao(IClassificationDao dao) {
86 this.dao = dao;
87 }
88
89 private Comparator<? super TaxonNode> taxonNodeComparator;
90
91 @Autowired
92 public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){
93 this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;
94 }
95
96 @Override
97 public TaxonNode loadTaxonNodeByTaxon(Taxon taxon, UUID classificationUuid, List<String> propertyPaths){
98 Classification tree = dao.load(classificationUuid);
99 TaxonNode node = tree.getNode(taxon);
100
101 return loadTaxonNode(node.getUuid(), propertyPaths);
102 }
103
104 @Override
105 @Deprecated // use loadTaxonNode(UUID, List<String>) instead
106 public TaxonNode loadTaxonNode(TaxonNode taxonNode, List<String> propertyPaths){
107 return taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
108 }
109
110 public TaxonNode loadTaxonNode(UUID taxonNodeUuid, List<String> propertyPaths){
111 return taxonNodeDao.load(taxonNodeUuid, propertyPaths);
112 }
113
114 @Override
115 public List<TaxonNode> listRankSpecificRootNodes(Classification classification, Rank rank, Integer pageSize,
116 Integer pageIndex, List<String> propertyPaths) {
117 return pageRankSpecificRootNodes(classification, rank, pageSize, pageIndex, propertyPaths).getRecords();
118 }
119
120 @Override
121 public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, Rank rank, Integer pageSize,
122 Integer pageIndex, List<String> propertyPaths) {
123 long[] numberOfResults = dao.countRankSpecificRootNodes(classification, rank);
124 long totalNumberOfResults = numberOfResults[0] + (numberOfResults.length > 1 ? numberOfResults[1] : 0);
125
126 List<TaxonNode> results = new ArrayList<TaxonNode>();
127
128 if (AbstractPagerImpl.hasResultsInRange(totalNumberOfResults, pageIndex, pageSize)) { // no point checking again
129 Integer limit = PagerUtils.limitFor(pageSize);
130 Integer start = PagerUtils.startFor(pageSize, pageIndex);
131
132 Integer remainingLimit = limit;
133 int[] queryIndexes = rank == null ? new int[]{0} : new int[]{0,1};
134
135 for(int queryIndex: queryIndexes) {
136 if(start != null && start > numberOfResults[queryIndex]) {
137 // start in next query with new start value
138 start = start - (int)numberOfResults[queryIndex];
139 continue;
140 }
141
142 List<TaxonNode> perQueryResults = dao.listRankSpecificRootNodes(classification, rank, remainingLimit, start, propertyPaths, queryIndex);
143 results.addAll(perQueryResults);
144 if(remainingLimit != null ){
145 remainingLimit = remainingLimit - results.size();
146 if(remainingLimit <= 0) {
147 // no need to run further queries if first query returned enough items!
148 break;
149 }
150 // start at with fist item of next query to fetch the remaining items
151 start = 0;
152 }
153 }
154 }
155 // long start_t = System.currentTimeMillis();
156 Collections.sort(results, taxonNodeComparator); // TODO is ordering during the hibernate query in the dao possible?
157 // System.err.println("service.pageRankSpecificRootNodes() - Collections.sort(results, taxonNodeComparator) " + (System.currentTimeMillis() - start_t));
158 return new DefaultPagerImpl<TaxonNode>(pageIndex, (int) totalNumberOfResults, pageSize, results);
159
160 }
161
162 /**
163 * @implements {@link IClassificationService#loadTreeBranch(TaxonNode, Rank, List)
164 * @see eu.etaxonomy.cdm.api.service.ITaxonService#loadTreeBranchTo(eu.etaxonomy.cdm.model.taxon.TaxonNode, eu.etaxonomy.cdm.model.name.Rank, java.util.List)
165 * FIXME Candidate for harmonization
166 * move to classification service
167 */
168 @Override
169 public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, Rank baseRank, List<String> propertyPaths){
170
171 TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
172 List<TaxonNode> pathToRoot = new ArrayList<TaxonNode>();
173 pathToRoot.add(thisNode);
174
175 while(!thisNode.isTopmostNode()){
176 //TODO why do we need to deproxy here?
177 // without this thisNode.getParent() will return NULL in
178 // some cases (environment dependend?) even if the parent exits
179 TaxonNode parentNode = CdmBase.deproxy(thisNode, TaxonNode.class).getParent();
180
181 if(parentNode == null){
182 throw new NullPointerException("taxonNode " + thisNode + " must have a parent since it is not top most");
183 }
184 if(parentNode.getTaxon() == null){
185 throw new NullPointerException("The taxon associated with taxonNode " + parentNode + " is NULL");
186 }
187 if(parentNode.getTaxon().getName() == null){
188 throw new NullPointerException("The name of the taxon associated with taxonNode " + parentNode + " is NULL");
189 }
190
191 Rank parentNodeRank = parentNode.getTaxon().getName() == null ? null : parentNode.getTaxon().getName().getRank();
192 // stop if the next parent is higher than the baseRank
193 if(baseRank != null && parentNodeRank != null && baseRank.isLower(parentNodeRank)){
194 break;
195 }
196
197 pathToRoot.add(parentNode);
198 thisNode = parentNode;
199 }
200
201 // initialize and invert order of nodes in list
202 defaultBeanInitializer.initializeAll(pathToRoot, propertyPaths);
203 Collections.reverse(pathToRoot);
204
205 return pathToRoot;
206 }
207
208 @Override
209 public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification, Rank baseRank, List<String> propertyPaths){
210 Classification tree = dao.load(classification.getUuid());
211 taxon = (Taxon) taxonDao.load(taxon.getUuid());
212 TaxonNode node = tree.getNode(taxon);
213 if(node == null){
214 logger.warn("The specified taxon is not found in the given tree.");
215 return null;
216 }
217 return loadTreeBranch(node, baseRank, propertyPaths);
218 }
219
220
221 @Override
222 public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
223 List<String> propertyPaths) {
224 taxonNode = taxonNodeDao.load(taxonNode.getUuid());
225 List<TaxonNode> childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
226 defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
227 Collections.sort(childNodes, taxonNodeComparator);
228 return childNodes;
229 }
230
231 @Override
232 public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
233 Integer pageIndex, List<String> propertyPaths){
234
235 Classification classification = dao.load(classificationUuid);
236 Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
237
238 List<TaxonNode> results = dao.listChildrenOf(taxon, classification, pageSize, pageIndex, propertyPaths);
239 Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
240 return results;
241 }
242
243 @Override
244 public Pager<TaxonNode> pageSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
245 Integer pageIndex, List<String> propertyPaths){
246
247 Classification classification = dao.load(classificationUuid);
248 Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
249
250 long numberOfResults = dao.countSiblingsOf(taxon, classification);
251
252 List<TaxonNode> results;
253 if(PagerUtils.hasResultsInRange(numberOfResults, pageIndex, pageSize)) {
254 results = dao.listSiblingsOf(taxon, classification, pageSize, pageIndex, propertyPaths);
255 Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
256 } else {
257 results = new ArrayList<>();
258 }
259
260 return new DefaultPagerImpl<TaxonNode>(pageIndex, numberOfResults, pageSize, results);
261 }
262
263 @Override
264 public List<TaxonNode> listSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
265 Integer pageIndex, List<String> propertyPaths){
266
267 Pager<TaxonNode> pager = pageSiblingsOfTaxon(taxonUuid, classificationUuid, pageSize, pageIndex, propertyPaths);
268 return pager.getRecords();
269 }
270
271 @Override
272 public TaxonNode getTaxonNodeByUuid(UUID uuid) {
273 return taxonNodeDao.findByUuid(uuid);
274 }
275
276 @Override
277 public ITaxonTreeNode getTreeNodeByUuid(UUID uuid){
278 ITaxonTreeNode treeNode = taxonNodeDao.findByUuid(uuid);
279 if(treeNode == null){
280 treeNode = dao.findByUuid(uuid);
281 }
282
283 return treeNode;
284 }
285
286 @Override
287 public TaxonNode getRootNode(UUID classificationUuid){
288 return dao.getRootNode(classificationUuid);
289 }
290
291 @Override
292 public List<Classification> listClassifications(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
293 return dao.list(limit, start, orderHints, propertyPaths);
294 }
295
296 @Override
297 public UUID removeTaxonNode(TaxonNode taxonNode) {
298 return taxonNodeDao.delete(taxonNode);
299 }
300 @Override
301 public UUID removeTreeNode(ITaxonTreeNode treeNode) {
302 if(treeNode instanceof Classification){
303 return dao.delete((Classification) treeNode);
304 }else if(treeNode instanceof TaxonNode){
305 return taxonNodeDao.delete((TaxonNode)treeNode);
306 }
307 return null;
308 }
309 @Override
310 public UUID saveTaxonNode(TaxonNode taxonNode) {
311 return taxonNodeDao.save(taxonNode).getUuid();
312 }
313
314 @Override
315 public Map<UUID, TaxonNode> saveTaxonNodeAll(
316 Collection<TaxonNode> taxonNodeCollection) {
317 return taxonNodeDao.saveAll(taxonNodeCollection);
318 }
319
320 @Override
321 public UUID saveTreeNode(ITaxonTreeNode treeNode) {
322 if(treeNode instanceof Classification){
323 return dao.save((Classification) treeNode).getUuid();
324 }else if(treeNode instanceof TaxonNode){
325 return taxonNodeDao.save((TaxonNode)treeNode).getUuid();
326 }
327 return null;
328 }
329
330 @Override
331 public List<TaxonNode> getAllNodes(){
332 return taxonNodeDao.list(null,null);
333 }
334
335 @Override
336 public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, Integer limit, String pattern) {
337 return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), limit, pattern);
338 }
339
340 @Override
341 public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, Integer limit, String pattern) {
342 return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, limit, pattern);
343 }
344
345 @Override
346 public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid ) {
347 return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), null, null);
348 }
349
350 @Override
351 public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification ) {
352 return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, null, null);
353 }
354
355 @Override
356 public List<UuidAndTitleCache<Classification>> getUuidAndTitleCache(Integer limit, String pattern) {
357 return dao.getUuidAndTitleCache(limit, pattern);
358 }
359
360 @Override
361 public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(
362 TaxonNode taxonNode, List<String> propertyPaths, int size,
363 int height, int widthOrDuration, String[] mimeTypes) {
364
365 TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<UUID, List<MediaRepresentation>>();
366 List<Media> taxonMedia = new ArrayList<Media>();
367 List<MediaRepresentation> mediaRepresentations = new ArrayList<MediaRepresentation>();
368
369 //add all media of the children to the result map
370 if (taxonNode != null){
371
372 List<TaxonNode> nodes = new ArrayList<TaxonNode>();
373
374 nodes.add(loadTaxonNode(taxonNode, propertyPaths));
375 nodes.addAll(loadChildNodesOfTaxonNode(taxonNode, propertyPaths));
376
377 if (nodes != null){
378 for(TaxonNode node : nodes){
379 Taxon taxon = node.getTaxon();
380 for (TaxonDescription taxonDescription: taxon.getDescriptions()){
381 for (DescriptionElementBase descriptionElement: taxonDescription.getElements()){
382 for(Media media : descriptionElement.getMedia()){
383 taxonMedia.add(media);
384
385 //find the best matching representation
386 mediaRepresentations.add(MediaUtils.findBestMatchingRepresentation(media,null, size, height, widthOrDuration, mimeTypes));
387
388 }
389 }
390 }
391 result.put(taxon.getUuid(), mediaRepresentations);
392
393 }
394 }
395
396 }
397
398 return result;
399
400 }
401
402 @Override
403 public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(Taxon taxon, Classification taxTree, List<String> propertyPaths, int size, int height, int widthOrDuration, String[] mimeTypes){
404 TaxonNode node = taxTree.getNode(taxon);
405
406 return getAllMediaForChildNodes(node, propertyPaths, size, height, widthOrDuration, mimeTypes);
407 }
408
409 @Override
410 @Transactional(readOnly = false)
411 public void updateTitleCache(Class<? extends Classification> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Classification> cacheStrategy, IProgressMonitor monitor) {
412 if (clazz == null){
413 clazz = Classification.class;
414 }
415 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
416 }
417
418 /**
419 *
420 * @param allNodesOfClassification
421 * @return null - if allNodesOfClassification is empty <br>
422 */
423
424 private Map<String, List<TaxonNode>> getSortedGenusList(Collection<TaxonNode> allNodesOfClassification){
425
426 if(allNodesOfClassification == null || allNodesOfClassification.isEmpty()){
427 return null;
428 }
429 Map<String, List<TaxonNode>> sortedGenusMap = new HashMap<String, List<TaxonNode>>();
430 for(TaxonNode node:allNodesOfClassification){
431 final TaxonNode tn = node;
432 Taxon taxon = node.getTaxon();
433 NonViralName name = CdmBase.deproxy(taxon.getName(), NonViralName.class);
434 String genusOrUninomial = name.getGenusOrUninomial();
435 //if rank unknown split string and take first word
436 if(genusOrUninomial == null){
437 String titleCache = taxon.getTitleCache();
438 String[] split = titleCache.split("\\s+");
439 for(String s:split){
440 genusOrUninomial = s;
441 break;
442 }
443 }
444 //if node has children
445
446 //retrieve list from map if not create List
447 if(sortedGenusMap.containsKey(genusOrUninomial)){
448 List<TaxonNode> list = sortedGenusMap.get(genusOrUninomial);
449 list.add(node);
450 sortedGenusMap.put(genusOrUninomial, list);
451 }else{
452 //create List for genus
453 List<TaxonNode> list = new ArrayList<TaxonNode>();
454 list.add(node);
455 sortedGenusMap.put(genusOrUninomial, list);
456 }
457 }
458 return sortedGenusMap;
459 }
460
461 /**
462 *
463 * creates new Classification and parent TaxonNodes at genus level
464 *
465 *
466 * @param map GenusMap which holds a name (Genus) and all the same Taxa as a list
467 * @param classification you want to improve the hierarchy (will not be modified)
468 * @param configurator to change certain settings, if null then standard settings will be taken
469 * @return new classification with parentNodes for each entry in the map
470 */
471 @SuppressWarnings({ "rawtypes", "unchecked" })
472 @Transactional(readOnly = false)
473 @Override
474 public UpdateResult createHierarchyInClassification(Classification classification, CreateHierarchyForClassificationConfigurator configurator){
475 UpdateResult result = new UpdateResult();
476 classification = dao.findByUuid(classification.getUuid());
477 Map<String, List<TaxonNode>> map = getSortedGenusList(classification.getAllNodes());
478
479 final String APPENDIX = "repaired";
480 String titleCache = org.apache.commons.lang.StringUtils.isBlank(classification.getTitleCache()) ? " " : classification.getTitleCache() ;
481 //TODO classification clone???
482 Classification newClassification = Classification.NewInstance(titleCache +" "+ APPENDIX);
483 newClassification.setReference(classification.getReference());
484
485 for(Map.Entry<String, List<TaxonNode>> entry:map.entrySet()){
486 String genus = entry.getKey();
487 List<TaxonNode> listOfTaxonNodes = entry.getValue();
488 TaxonNode parentNode = null;
489 //Search for genus in list
490 for(TaxonNode tNode:listOfTaxonNodes){
491 //take that taxonNode as parent and remove from list with all it possible children
492 //FIXME NPE for name
493 TaxonNameBase name = tNode.getTaxon().getName();
494 NonViralName nonViralName = CdmBase.deproxy(name, NonViralName.class);
495 if(nonViralName.getNameCache().equalsIgnoreCase(genus)){
496 TaxonNode clone = (TaxonNode) tNode.clone();
497 if(!tNode.hasChildNodes()){
498 //FIXME remove classification
499 // parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
500 parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
501 //remove taxonNode from list because just added to classification
502 result.addUpdatedObject(tNode);
503 listOfTaxonNodes.remove(tNode);
504 }else{
505 //get all childNodes
506 //save prior Hierarchy and remove them from the list
507 List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tNode, clone, result);
508 // parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
509 //FIXME remove classification
510 parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
511 //remove taxonNode from list because just added to classification
512 result.addUpdatedObject(tNode);
513 listOfTaxonNodes.remove(tNode);
514 if(copyAllChildrenToTaxonNode != null){
515 listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
516 }
517 }
518 break;
519 }
520 }
521 if(parentNode == null){
522 //if no match found in list, create parentNode
523 NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
524 NonViralName nonViralName = parser.parseFullName(genus);
525 TaxonNameBase taxonNameBase = nonViralName;
526 //TODO Sec via configurator
527 Taxon taxon = Taxon.NewInstance(taxonNameBase, null);
528 parentNode = newClassification.addChildTaxon(taxon, 0, null, null);
529 result.addUpdatedObject(parentNode);
530 }
531 //iterate over the rest of the list
532 for(TaxonNode tn : listOfTaxonNodes){
533 //if TaxonNode has a parent and this is not the classification then skip it
534 //and add to new classification via the parentNode as children of it
535 //this should assures to keep the already existing hierarchy
536 //FIXME: Assert is not rootnode --> entrypoint is not classification in future but rather rootNode
537
538 if(!tn.isTopmostNode()){
539 continue; //skip to next taxonNode
540 }
541
542 TaxonNode clone = (TaxonNode) tn.clone();
543 //FIXME: citation from node
544 //TODO: addchildNode without citation and references
545 // TaxonNode taxonNode = parentNode.addChildNode(clone, classification.getCitation(), classification.getMicroReference());
546 TaxonNode taxonNode = parentNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
547 result.addUnChangedObject(clone);
548 if(tn.hasChildNodes()){
549 //save hierarchy in new classification
550 List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tn, taxonNode, result);
551 if(copyAllChildrenToTaxonNode != null){
552 listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
553 }
554 }
555 }
556 }
557 dao.saveOrUpdate(newClassification);
558 result.setCdmEntity(newClassification);
559 return result;
560 }
561
562 /**
563 *
564 * recursive method to get all childnodes of taxonNode in classification.
565 *
566 * @param classification just for References and Citation, can be null
567 * @param copyFromNode TaxonNode with Children
568 * @param copyToNode TaxonNode which will receive the children
569 * @return List of ChildNode which has been added. If node has no children returns null
570 */
571 private List<TaxonNode> copyAllChildrenToTaxonNode(TaxonNode copyFromNode, TaxonNode copyToNode, UpdateResult result) {
572 List<TaxonNode> childNodes;
573 if(!copyFromNode.hasChildNodes()){
574 return null;
575 }else{
576 childNodes = copyFromNode.getChildNodes();
577 }
578 for(TaxonNode childNode:childNodes){
579 TaxonNode clone = (TaxonNode) childNode.clone();
580 result.addUnChangedObject(clone);
581 if(childNode.hasChildNodes()){
582 copyAllChildrenToTaxonNode(childNode, clone, result);
583 }
584 //FIXME: citation from node instead of classification
585 // copyToNode.addChildNode(clone,classification.getCitation(), classification.getMicroReference());
586 copyToNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
587 }
588 return childNodes;
589 }
590
591 /**
592 * {@inheritDoc}
593 */
594 @Override
595 public ClassificationLookupDTO classificationLookup(Classification classification) {
596 return dao.classificationLookup(classification);
597 }
598
599
600 @Override
601 public DeleteResult delete(UUID classificationUuid, TaxonDeletionConfigurator config){
602 DeleteResult result = new DeleteResult();
603 Classification classification = dao.findByUuid(classificationUuid);
604 if (classification == null){
605 result.addException(new IllegalArgumentException("The classification does not exist in database."));
606 result.setAbort();
607 return result;
608 }
609 if (!classification.hasChildNodes()){
610 dao.delete(classification);
611 }
612 if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE) ){
613 TaxonNode root = classification.getRootNode();
614 taxonNodeDao.delete(root, true);
615 dao.delete(classification);
616 }
617
618
619 return result;
620 }
621
622 @Override
623 public List<GroupedTaxonDTO> groupTaxaByHigherTaxon(List<UUID> originalTaxonUuids, UUID classificationUuid, Rank minRank, Rank maxRank){
624 List<GroupedTaxonDTO> result = new ArrayList<>();
625
626 //get treeindex for each taxonUUID
627 Map<UUID, String> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
628
629 //build treeindex list (or tree)
630 List<String> treeIndexClosure = new ArrayList<>();
631 for (String treeIndex : taxonIdTreeIndexMap.values()){
632 String[] splits = treeIndex.substring(1).split(ITreeNode.separator);
633 String currentIndex = ITreeNode.separator;
634 for (String split : splits){
635 if (split.equals("")){
636 continue;
637 }
638 currentIndex += split + ITreeNode.separator;
639 if (!treeIndexClosure.contains(currentIndex) && !split.startsWith(ITreeNode.treePrefix)){
640 treeIndexClosure.add(currentIndex);
641 }
642 }
643 }
644
645 //get rank sortindex for all parent taxa with sortindex <= minRank and sortIndex >= maxRank (if available)
646 Integer minRankOrderIndex = minRank == null ? null : minRank.getOrderIndex();
647 Integer maxRankOrderIndex = maxRank == null ? null : maxRank.getOrderIndex();
648 Map<String, Integer> treeIndexSortIndexMapTmp = taxonNodeDao.rankOrderIndexForTreeIndex(treeIndexClosure, minRankOrderIndex, maxRankOrderIndex);
649
650 //remove all treeindex with "exists child in above map(and child.sortindex > xxx)
651 List<String> treeIndexList = new ArrayList<>(treeIndexSortIndexMapTmp.keySet());
652 Collections.sort(treeIndexList, new TreeIndexComparator());
653 Map<String, Integer> treeIndexSortIndexMap = new HashMap<>();
654 String lastTreeIndex = null;
655 for (String treeIndex : treeIndexList){
656 if (lastTreeIndex != null && treeIndex.startsWith(lastTreeIndex)){
657 treeIndexSortIndexMap.remove(lastTreeIndex);
658 }
659 treeIndexSortIndexMap.put(treeIndex, treeIndexSortIndexMapTmp.get(treeIndex));
660 lastTreeIndex = treeIndex;
661 }
662
663 //get taxonID for treeIndexes
664 Map<String, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(treeIndexSortIndexMap.keySet());
665
666 //fill result list
667 for (UUID originalTaxonUuid : originalTaxonUuids){
668 GroupedTaxonDTO item = new GroupedTaxonDTO();
669 result.add(item);
670 item.setTaxonUuid(originalTaxonUuid);
671 String groupIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
672 while (groupIndex != null){
673 if (treeIndexTaxonIdMap.get(groupIndex) != null){
674 UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupIndex);
675 item.setGroupTaxonUuid(uuidAndLabel.getUuid());
676 item.setGroupTaxonName(uuidAndLabel.getTitleCache());
677 break;
678 }else{
679 int index = groupIndex.substring(0, groupIndex.length()-1).lastIndexOf(ITreeNode.separator);
680 groupIndex = index<0 ? null : groupIndex.substring(0, index+1);
681 }
682 }
683 }
684
685 return result;
686 }
687
688 /**
689 * {@inheritDoc}
690 */
691 @Override
692 public UUID getTaxonNodeUuidByTaxonUuid(UUID classificationUuid, UUID taxonUuid) {
693 Map<UUID, UUID> map = dao.getTaxonNodeUuidByTaxonUuid(classificationUuid, Arrays.asList(taxonUuid));
694 UUID taxonNodeUuid = map.get(taxonUuid);
695 return taxonNodeUuid;
696 }
697
698 }