3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
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.
11 package eu
.etaxonomy
.cdm
.api
.service
;
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
;
21 import java
.util
.TreeMap
;
22 import java
.util
.UUID
;
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
;
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
;
66 * @created Sep 21, 2009
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);
75 private ITaxonNodeDao taxonNodeDao
;
78 private ITaxonDao taxonDao
;
81 private IBeanInitializer defaultBeanInitializer
;
85 protected void setDao(IClassificationDao dao
) {
89 private Comparator
<?
super TaxonNode
> taxonNodeComparator
;
92 public void setTaxonNodeComparator(ITaxonNodeComparator
<?
super TaxonNode
> taxonNodeComparator
){
93 this.taxonNodeComparator
= (Comparator
<?
super TaxonNode
>) taxonNodeComparator
;
97 public TaxonNode
loadTaxonNodeByTaxon(Taxon taxon
, UUID classificationUuid
, List
<String
> propertyPaths
){
98 Classification tree
= dao
.load(classificationUuid
);
99 TaxonNode node
= tree
.getNode(taxon
);
101 return loadTaxonNode(node
.getUuid(), propertyPaths
);
105 @Deprecated // use loadTaxonNode(UUID, List<String>) instead
106 public TaxonNode
loadTaxonNode(TaxonNode taxonNode
, List
<String
> propertyPaths
){
107 return taxonNodeDao
.load(taxonNode
.getUuid(), propertyPaths
);
110 public TaxonNode
loadTaxonNode(UUID taxonNodeUuid
, List
<String
> propertyPaths
){
111 return taxonNodeDao
.load(taxonNodeUuid
, propertyPaths
);
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();
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);
126 List
<TaxonNode
> results
= new ArrayList
<TaxonNode
>();
128 if (AbstractPagerImpl
.hasResultsInRange(totalNumberOfResults
, pageIndex
, pageSize
)) { // no point checking again
129 Integer limit
= PagerUtils
.limitFor(pageSize
);
130 Integer start
= PagerUtils
.startFor(pageSize
, pageIndex
);
132 Integer remainingLimit
= limit
;
133 int[] queryIndexes
= rank
== null ?
new int[]{0} : new int[]{0,1};
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
];
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!
150 // start at with fist item of next query to fetch the remaining items
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
);
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
169 public List
<TaxonNode
> loadTreeBranch(TaxonNode taxonNode
, Rank baseRank
, List
<String
> propertyPaths
){
171 TaxonNode thisNode
= taxonNodeDao
.load(taxonNode
.getUuid(), propertyPaths
);
172 List
<TaxonNode
> pathToRoot
= new ArrayList
<TaxonNode
>();
173 pathToRoot
.add(thisNode
);
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();
181 if(parentNode
== null){
182 throw new NullPointerException("taxonNode " + thisNode
+ " must have a parent since it is not top most");
184 if(parentNode
.getTaxon() == null){
185 throw new NullPointerException("The taxon associated with taxonNode " + parentNode
+ " is NULL");
187 if(parentNode
.getTaxon().getName() == null){
188 throw new NullPointerException("The name of the taxon associated with taxonNode " + parentNode
+ " is NULL");
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
)){
197 pathToRoot
.add(parentNode
);
198 thisNode
= parentNode
;
201 // initialize and invert order of nodes in list
202 defaultBeanInitializer
.initializeAll(pathToRoot
, propertyPaths
);
203 Collections
.reverse(pathToRoot
);
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
);
214 logger
.warn("The specified taxon is not found in the given tree.");
217 return loadTreeBranch(node
, baseRank
, propertyPaths
);
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
);
232 public List
<TaxonNode
> listChildNodesOfTaxon(UUID taxonUuid
, UUID classificationUuid
, Integer pageSize
,
233 Integer pageIndex
, List
<String
> propertyPaths
){
235 Classification classification
= dao
.load(classificationUuid
);
236 Taxon taxon
= (Taxon
) taxonDao
.load(taxonUuid
);
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
244 public Pager
<TaxonNode
> pageSiblingsOfTaxon(UUID taxonUuid
, UUID classificationUuid
, Integer pageSize
,
245 Integer pageIndex
, List
<String
> propertyPaths
){
247 Classification classification
= dao
.load(classificationUuid
);
248 Taxon taxon
= (Taxon
) taxonDao
.load(taxonUuid
);
250 long numberOfResults
= dao
.countSiblingsOf(taxon
, classification
);
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
257 results
= new ArrayList
<>();
260 return new DefaultPagerImpl
<TaxonNode
>(pageIndex
, numberOfResults
, pageSize
, results
);
264 public List
<TaxonNode
> listSiblingsOfTaxon(UUID taxonUuid
, UUID classificationUuid
, Integer pageSize
,
265 Integer pageIndex
, List
<String
> propertyPaths
){
267 Pager
<TaxonNode
> pager
= pageSiblingsOfTaxon(taxonUuid
, classificationUuid
, pageSize
, pageIndex
, propertyPaths
);
268 return pager
.getRecords();
272 public TaxonNode
getTaxonNodeByUuid(UUID uuid
) {
273 return taxonNodeDao
.findByUuid(uuid
);
277 public ITaxonTreeNode
getTreeNodeByUuid(UUID uuid
){
278 ITaxonTreeNode treeNode
= taxonNodeDao
.findByUuid(uuid
);
279 if(treeNode
== null){
280 treeNode
= dao
.findByUuid(uuid
);
287 public TaxonNode
getRootNode(UUID classificationUuid
){
288 return dao
.getRootNode(classificationUuid
);
292 public List
<Classification
> listClassifications(Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
293 return dao
.list(limit
, start
, orderHints
, propertyPaths
);
297 public UUID
removeTaxonNode(TaxonNode taxonNode
) {
298 return taxonNodeDao
.delete(taxonNode
);
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
);
310 public UUID
saveTaxonNode(TaxonNode taxonNode
) {
311 return taxonNodeDao
.save(taxonNode
).getUuid();
315 public Map
<UUID
, TaxonNode
> saveTaxonNodeAll(
316 Collection
<TaxonNode
> taxonNodeCollection
) {
317 return taxonNodeDao
.saveAll(taxonNodeCollection
);
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();
331 public List
<TaxonNode
> getAllNodes(){
332 return taxonNodeDao
.list(null,null);
336 public List
<UuidAndTitleCache
<TaxonNode
>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid
, Integer limit
, String pattern
) {
337 return taxonDao
.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao
.load(classificationUuid
), limit
, pattern
);
341 public List
<UuidAndTitleCache
<TaxonNode
>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification
, Integer limit
, String pattern
) {
342 return taxonDao
.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification
, limit
, pattern
);
346 public List
<UuidAndTitleCache
<TaxonNode
>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid
) {
347 return taxonDao
.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao
.load(classificationUuid
), null, null);
351 public List
<UuidAndTitleCache
<TaxonNode
>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification
) {
352 return taxonDao
.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification
, null, null);
356 public List
<UuidAndTitleCache
<Classification
>> getUuidAndTitleCache(Integer limit
, String pattern
) {
357 return dao
.getUuidAndTitleCache(limit
, pattern
);
361 public Map
<UUID
, List
<MediaRepresentation
>> getAllMediaForChildNodes(
362 TaxonNode taxonNode
, List
<String
> propertyPaths
, int size
,
363 int height
, int widthOrDuration
, String
[] mimeTypes
) {
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
>();
369 //add all media of the children to the result map
370 if (taxonNode
!= null){
372 List
<TaxonNode
> nodes
= new ArrayList
<TaxonNode
>();
374 nodes
.add(loadTaxonNode(taxonNode
, propertyPaths
));
375 nodes
.addAll(loadChildNodesOfTaxonNode(taxonNode
, propertyPaths
));
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
);
385 //find the best matching representation
386 mediaRepresentations
.add(MediaUtils
.findBestMatchingRepresentation(media
,null, size
, height
, widthOrDuration
, mimeTypes
));
391 result
.put(taxon
.getUuid(), mediaRepresentations
);
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
);
406 return getAllMediaForChildNodes(node
, propertyPaths
, size
, height
, widthOrDuration
, mimeTypes
);
410 @Transactional(readOnly
= false)
411 public void updateTitleCache(Class
<?
extends Classification
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<Classification
> cacheStrategy
, IProgressMonitor monitor
) {
413 clazz
= Classification
.class;
415 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
420 * @param allNodesOfClassification
421 * @return null - if allNodesOfClassification is empty <br>
424 private Map
<String
, List
<TaxonNode
>> getSortedGenusList(Collection
<TaxonNode
> allNodesOfClassification
){
426 if(allNodesOfClassification
== null || allNodesOfClassification
.isEmpty()){
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+");
440 genusOrUninomial
= s
;
444 //if node has children
446 //retrieve list from map if not create List
447 if(sortedGenusMap
.containsKey(genusOrUninomial
)){
448 List
<TaxonNode
> list
= sortedGenusMap
.get(genusOrUninomial
);
450 sortedGenusMap
.put(genusOrUninomial
, list
);
452 //create List for genus
453 List
<TaxonNode
> list
= new ArrayList
<TaxonNode
>();
455 sortedGenusMap
.put(genusOrUninomial
, list
);
458 return sortedGenusMap
;
463 * creates new Classification and parent TaxonNodes at genus level
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
471 @SuppressWarnings({ "rawtypes", "unchecked" })
472 @Transactional(readOnly
= false)
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());
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());
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
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
);
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
);
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
);
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
538 if(!tn
.isTopmostNode()){
539 continue; //skip to next taxonNode
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
);
557 dao
.saveOrUpdate(newClassification
);
558 result
.setCdmEntity(newClassification
);
564 * recursive method to get all childnodes of taxonNode in classification.
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
571 private List
<TaxonNode
> copyAllChildrenToTaxonNode(TaxonNode copyFromNode
, TaxonNode copyToNode
, UpdateResult result
) {
572 List
<TaxonNode
> childNodes
;
573 if(!copyFromNode
.hasChildNodes()){
576 childNodes
= copyFromNode
.getChildNodes();
578 for(TaxonNode childNode
:childNodes
){
579 TaxonNode clone
= (TaxonNode
) childNode
.clone();
580 result
.addUnChangedObject(clone
);
581 if(childNode
.hasChildNodes()){
582 copyAllChildrenToTaxonNode(childNode
, clone
, result
);
584 //FIXME: citation from node instead of classification
585 // copyToNode.addChildNode(clone,classification.getCitation(), classification.getMicroReference());
586 copyToNode
.addChildNode(clone
, clone
.getReference(), clone
.getMicroReference());
595 public ClassificationLookupDTO
classificationLookup(Classification classification
) {
596 return dao
.classificationLookup(classification
);
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."));
609 if (!classification
.hasChildNodes()){
610 dao
.delete(classification
);
612 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
) ){
613 TaxonNode root
= classification
.getRootNode();
614 taxonNodeDao
.delete(root
, true);
615 dao
.delete(classification
);
623 public List
<GroupedTaxonDTO
> groupTaxaByHigherTaxon(List
<UUID
> originalTaxonUuids
, UUID classificationUuid
, Rank minRank
, Rank maxRank
){
624 List
<GroupedTaxonDTO
> result
= new ArrayList
<>();
626 //get treeindex for each taxonUUID
627 Map
<UUID
, String
> taxonIdTreeIndexMap
= dao
.treeIndexForTaxonUuids(classificationUuid
, originalTaxonUuids
);
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("")){
638 currentIndex
+= split
+ ITreeNode
.separator
;
639 if (!treeIndexClosure
.contains(currentIndex
) && !split
.startsWith(ITreeNode
.treePrefix
)){
640 treeIndexClosure
.add(currentIndex
);
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
);
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
);
659 treeIndexSortIndexMap
.put(treeIndex
, treeIndexSortIndexMapTmp
.get(treeIndex
));
660 lastTreeIndex
= treeIndex
;
663 //get taxonID for treeIndexes
664 Map
<String
, UuidAndTitleCache
<?
>> treeIndexTaxonIdMap
= taxonNodeDao
.taxonUuidsForTreeIndexes(treeIndexSortIndexMap
.keySet());
667 for (UUID originalTaxonUuid
: originalTaxonUuids
){
668 GroupedTaxonDTO item
= new GroupedTaxonDTO();
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());
679 int index
= groupIndex
.substring(0, groupIndex
.length()-1).lastIndexOf(ITreeNode
.separator
);
680 groupIndex
= index
<0 ?
null : groupIndex
.substring(0, index
+1);
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
;