2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.api
.service
;
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
;
22 import java
.util
.TreeMap
;
23 import java
.util
.UUID
;
25 import javax
.persistence
.EntityNotFoundException
;
27 import org
.apache
.commons
.collections
.CollectionUtils
;
28 import org
.apache
.commons
.lang
.StringUtils
;
29 import org
.apache
.log4j
.Logger
;
30 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
31 import org
.springframework
.stereotype
.Service
;
32 import org
.springframework
.transaction
.annotation
.Transactional
;
34 import eu
.etaxonomy
.cdm
.api
.service
.config
.CreateHierarchyForClassificationConfigurator
;
35 import eu
.etaxonomy
.cdm
.api
.service
.config
.NodeDeletionConfigurator
.ChildHandling
;
36 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
37 import eu
.etaxonomy
.cdm
.api
.service
.dto
.EntityDTO
;
38 import eu
.etaxonomy
.cdm
.api
.service
.dto
.GroupedTaxonDTO
;
39 import eu
.etaxonomy
.cdm
.api
.service
.dto
.MarkedEntityDTO
;
40 import eu
.etaxonomy
.cdm
.api
.service
.dto
.TaxonInContextDTO
;
41 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
42 import eu
.etaxonomy
.cdm
.api
.service
.pager
.PagerUtils
;
43 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.AbstractPagerImpl
;
44 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
45 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
46 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
47 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
48 import eu
.etaxonomy
.cdm
.model
.common
.DefinedTermBase
;
49 import eu
.etaxonomy
.cdm
.model
.common
.ITreeNode
;
50 import eu
.etaxonomy
.cdm
.model
.common
.MarkerType
;
51 import eu
.etaxonomy
.cdm
.model
.common
.TreeIndex
;
52 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
53 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
54 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
55 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
56 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
57 import eu
.etaxonomy
.cdm
.model
.name
.INonViralName
;
58 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
59 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
60 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
61 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
62 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
63 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonNodeComparator
;
64 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonTreeNode
;
65 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
66 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
67 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
68 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
69 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
70 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IDefinedTermDao
;
71 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.IBeanInitializer
;
72 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.IClassificationDao
;
73 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
74 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonNodeDao
;
75 import eu
.etaxonomy
.cdm
.persistence
.dto
.ClassificationLookupDTO
;
76 import eu
.etaxonomy
.cdm
.persistence
.dto
.TaxonNodeDto
;
77 import eu
.etaxonomy
.cdm
.persistence
.dto
.TaxonStatus
;
78 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
79 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
80 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
81 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImpl
;
85 * @created Sep 21, 2009
88 @Transactional(readOnly
= true)
89 public class ClassificationServiceImpl
extends IdentifiableServiceBase
<Classification
, IClassificationDao
>
90 implements IClassificationService
{
91 private static final Logger logger
= Logger
.getLogger(ClassificationServiceImpl
.class);
94 private ITaxonNodeDao taxonNodeDao
;
97 private ITaxonDao taxonDao
;
100 private ITaxonNodeService taxonNodeService
;
103 private IDefinedTermDao termDao
;
106 private IBeanInitializer defaultBeanInitializer
;
110 protected void setDao(IClassificationDao dao
) {
114 private Comparator
<?
super TaxonNode
> taxonNodeComparator
;
117 public void setTaxonNodeComparator(ITaxonNodeComparator
<?
super TaxonNode
> taxonNodeComparator
){
118 this.taxonNodeComparator
= (Comparator
<?
super TaxonNode
>) taxonNodeComparator
;
122 public TaxonNode
loadTaxonNodeByTaxon(Taxon taxon
, UUID classificationUuid
, List
<String
> propertyPaths
){
123 Classification tree
= dao
.load(classificationUuid
);
124 TaxonNode node
= tree
.getNode(taxon
);
126 return loadTaxonNode(node
.getUuid(), propertyPaths
);
130 @Deprecated // use loadTaxonNode(UUID, List<String>) instead
131 public TaxonNode
loadTaxonNode(TaxonNode taxonNode
, List
<String
> propertyPaths
){
132 return taxonNodeDao
.load(taxonNode
.getUuid(), propertyPaths
);
135 public TaxonNode
loadTaxonNode(UUID taxonNodeUuid
, List
<String
> propertyPaths
){
136 return taxonNodeDao
.load(taxonNodeUuid
, propertyPaths
);
140 @Transactional(readOnly
= false)
141 public UpdateResult
cloneClassification(UUID classificationUuid
,
142 String name
, Reference sec
, TaxonRelationshipType relationshipType
) {
143 UpdateResult result
= new UpdateResult();
144 Classification classification
= load(classificationUuid
);
145 Classification clone
= Classification
.NewInstance(name
);
146 clone
.setReference(sec
);
148 //clone taxa and taxon nodes
149 List
<TaxonNode
> childNodes
= classification
.getRootNode().getChildNodes();
150 for (TaxonNode taxonNode
: childNodes
) {
151 addChildTaxa(taxonNode
, null, clone
, relationshipType
);
153 dao
.saveOrUpdate(clone
);
154 result
.setCdmEntity(clone
);
158 private void addChildTaxa(TaxonNode originalParentNode
, TaxonNode cloneParentNode
, Classification classification
, TaxonRelationshipType relationshipType
){
159 Reference reference
= classification
.getReference();
160 Taxon cloneTaxon
= (Taxon
) HibernateProxyHelper
.deproxy(originalParentNode
.getTaxon(), Taxon
.class).clone();
161 cloneTaxon
.setSec(reference
);
162 String microReference
= null;
163 List
<TaxonNode
> originalChildNodes
= originalParentNode
.getChildNodes();
165 //add relation between taxa
166 if (relationshipType
!= null){
167 cloneTaxon
.addTaxonRelation(originalParentNode
.getTaxon(), relationshipType
, reference
, microReference
);
170 TaxonNode cloneChildNode
= null;
171 //add taxon node to either parent node or classification (no parent node)
172 if(cloneParentNode
==null){
173 cloneChildNode
= classification
.addChildTaxon(cloneTaxon
, reference
, microReference
);
176 cloneChildNode
= cloneParentNode
.addChildTaxon(cloneTaxon
, reference
, microReference
);
178 taxonNodeDao
.saveOrUpdate(cloneChildNode
);
180 for (TaxonNode originalChildNode
: originalChildNodes
) {
181 addChildTaxa(originalChildNode
, cloneChildNode
, classification
, relationshipType
);
186 public List
<TaxonNode
> listRankSpecificRootNodes(Classification classification
, Rank rank
, Integer pageSize
,
187 Integer pageIndex
, List
<String
> propertyPaths
) {
188 return pageRankSpecificRootNodes(classification
, rank
, pageSize
, pageIndex
, propertyPaths
).getRecords();
192 public Pager
<TaxonNode
> pageRankSpecificRootNodes(Classification classification
, Rank rank
, Integer pageSize
,
193 Integer pageIndex
, List
<String
> propertyPaths
) {
194 long[] numberOfResults
= dao
.countRankSpecificRootNodes(classification
, rank
);
195 long totalNumberOfResults
= numberOfResults
[0] + (numberOfResults
.length
> 1 ? numberOfResults
[1] : 0);
197 List
<TaxonNode
> results
= new ArrayList
<TaxonNode
>();
199 if (AbstractPagerImpl
.hasResultsInRange(totalNumberOfResults
, pageIndex
, pageSize
)) { // no point checking again
200 Integer limit
= PagerUtils
.limitFor(pageSize
);
201 Integer start
= PagerUtils
.startFor(pageSize
, pageIndex
);
203 Integer remainingLimit
= limit
;
204 int[] queryIndexes
= rank
== null ?
new int[]{0} : new int[]{0,1};
206 for(int queryIndex
: queryIndexes
) {
207 if(start
!= null && start
> numberOfResults
[queryIndex
]) {
208 // start in next query with new start value
209 start
= start
- (int)numberOfResults
[queryIndex
];
213 List
<TaxonNode
> perQueryResults
= dao
.listRankSpecificRootNodes(classification
, rank
, remainingLimit
, start
, propertyPaths
, queryIndex
);
214 results
.addAll(perQueryResults
);
215 if(remainingLimit
!= null ){
216 remainingLimit
= remainingLimit
- results
.size();
217 if(remainingLimit
<= 0) {
218 // no need to run further queries if first query returned enough items!
221 // start at with fist item of next query to fetch the remaining items
226 // long start_t = System.currentTimeMillis();
227 Collections
.sort(results
, taxonNodeComparator
); // TODO is ordering during the hibernate query in the dao possible?
228 // System.err.println("service.pageRankSpecificRootNodes() - Collections.sort(results, taxonNodeComparator) " + (System.currentTimeMillis() - start_t));
229 return new DefaultPagerImpl
<TaxonNode
>(pageIndex
, (int) totalNumberOfResults
, pageSize
, results
);
234 * @implements {@link IClassificationService#loadTreeBranch(TaxonNode, Rank, List)
235 * @see eu.etaxonomy.cdm.api.service.ITaxonService#loadTreeBranchTo(eu.etaxonomy.cdm.model.taxon.TaxonNode, eu.etaxonomy.cdm.model.name.Rank, java.util.List)
236 * FIXME Candidate for harmonization
237 * move to classification service
240 public List
<TaxonNode
> loadTreeBranch(TaxonNode taxonNode
, Rank baseRank
, List
<String
> propertyPaths
){
242 TaxonNode thisNode
= taxonNodeDao
.load(taxonNode
.getUuid(), propertyPaths
);
243 List
<TaxonNode
> pathToRoot
= new ArrayList
<TaxonNode
>();
244 pathToRoot
.add(thisNode
);
246 while(!thisNode
.isTopmostNode()){
247 //TODO why do we need to deproxy here?
248 // without this thisNode.getParent() will return NULL in
249 // some cases (environment dependend?) even if the parent exits
250 TaxonNode parentNode
= CdmBase
.deproxy(thisNode
, TaxonNode
.class).getParent();
252 if(parentNode
== null){
253 throw new NullPointerException("taxonNode " + thisNode
+ " must have a parent since it is not top most");
255 if(parentNode
.getTaxon() == null){
256 throw new NullPointerException("The taxon associated with taxonNode " + parentNode
+ " is NULL");
258 if(parentNode
.getTaxon().getName() == null){
259 throw new NullPointerException("The name of the taxon associated with taxonNode " + parentNode
+ " is NULL");
262 Rank parentNodeRank
= parentNode
.getTaxon().getName() == null ?
null : parentNode
.getTaxon().getName().getRank();
263 // stop if the next parent is higher than the baseRank
264 if(baseRank
!= null && parentNodeRank
!= null && baseRank
.isLower(parentNodeRank
)){
268 pathToRoot
.add(parentNode
);
269 thisNode
= parentNode
;
272 // initialize and invert order of nodes in list
273 defaultBeanInitializer
.initializeAll(pathToRoot
, propertyPaths
);
274 Collections
.reverse(pathToRoot
);
280 public List
<TaxonNode
> loadTreeBranchToTaxon(Taxon taxon
, Classification classification
, Rank baseRank
, List
<String
> propertyPaths
){
281 Classification tree
= dao
.load(classification
.getUuid());
282 taxon
= (Taxon
) taxonDao
.load(taxon
.getUuid());
283 TaxonNode node
= tree
.getNode(taxon
);
285 logger
.warn("The specified taxon is not found in the given tree.");
288 return loadTreeBranch(node
, baseRank
, propertyPaths
);
293 public List
<TaxonNode
> loadChildNodesOfTaxonNode(TaxonNode taxonNode
,
294 List
<String
> propertyPaths
) {
295 taxonNode
= taxonNodeDao
.load(taxonNode
.getUuid());
296 List
<TaxonNode
> childNodes
= new ArrayList
<TaxonNode
>(taxonNode
.getChildNodes());
297 defaultBeanInitializer
.initializeAll(childNodes
, propertyPaths
);
298 Collections
.sort(childNodes
, taxonNodeComparator
);
303 public List
<TaxonNode
> listChildNodesOfTaxon(UUID taxonUuid
, UUID classificationUuid
, Integer pageSize
,
304 Integer pageIndex
, List
<String
> propertyPaths
){
306 Classification classification
= dao
.load(classificationUuid
);
307 Taxon taxon
= (Taxon
) taxonDao
.load(taxonUuid
);
309 List
<TaxonNode
> results
= dao
.listChildrenOf(taxon
, classification
, pageSize
, pageIndex
, propertyPaths
);
310 Collections
.sort(results
, taxonNodeComparator
); // FIXME this is only a HACK, order during the hibernate query in the dao
315 public Pager
<TaxonNode
> pageSiblingsOfTaxon(UUID taxonUuid
, UUID classificationUuid
, Integer pageSize
,
316 Integer pageIndex
, List
<String
> propertyPaths
){
318 Classification classification
= dao
.load(classificationUuid
);
319 Taxon taxon
= (Taxon
) taxonDao
.load(taxonUuid
);
321 long numberOfResults
= dao
.countSiblingsOf(taxon
, classification
);
323 List
<TaxonNode
> results
;
324 if(PagerUtils
.hasResultsInRange(numberOfResults
, pageIndex
, pageSize
)) {
325 results
= dao
.listSiblingsOf(taxon
, classification
, pageSize
, pageIndex
, propertyPaths
);
326 Collections
.sort(results
, taxonNodeComparator
); // FIXME this is only a HACK, order during the hibernate query in the dao
328 results
= new ArrayList
<>();
331 return new DefaultPagerImpl
<TaxonNode
>(pageIndex
, numberOfResults
, pageSize
, results
);
335 public List
<TaxonNode
> listSiblingsOfTaxon(UUID taxonUuid
, UUID classificationUuid
, Integer pageSize
,
336 Integer pageIndex
, List
<String
> propertyPaths
){
338 Pager
<TaxonNode
> pager
= pageSiblingsOfTaxon(taxonUuid
, classificationUuid
, pageSize
, pageIndex
, propertyPaths
);
339 return pager
.getRecords();
343 public TaxonNode
getTaxonNodeByUuid(UUID uuid
) {
344 return taxonNodeDao
.findByUuid(uuid
);
348 public ITaxonTreeNode
getTreeNodeByUuid(UUID uuid
){
349 ITaxonTreeNode treeNode
= taxonNodeDao
.findByUuid(uuid
);
350 if(treeNode
== null){
351 treeNode
= dao
.findByUuid(uuid
);
358 public TaxonNode
getRootNode(UUID classificationUuid
){
359 return dao
.getRootNode(classificationUuid
);
363 public List
<Classification
> listClassifications(Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
364 return dao
.list(limit
, start
, orderHints
, propertyPaths
);
368 public UUID
removeTaxonNode(TaxonNode taxonNode
) {
369 return taxonNodeDao
.delete(taxonNode
);
372 public UUID
removeTreeNode(ITaxonTreeNode treeNode
) {
373 if(treeNode
instanceof Classification
){
374 return dao
.delete((Classification
) treeNode
);
375 }else if(treeNode
instanceof TaxonNode
){
376 return taxonNodeDao
.delete((TaxonNode
)treeNode
);
381 public UUID
saveTaxonNode(TaxonNode taxonNode
) {
382 return taxonNodeDao
.save(taxonNode
).getUuid();
386 public Map
<UUID
, TaxonNode
> saveTaxonNodeAll(
387 Collection
<TaxonNode
> taxonNodeCollection
) {
388 return taxonNodeDao
.saveAll(taxonNodeCollection
);
392 public UUID
saveClassification(Classification classification
) {
394 taxonNodeDao
.saveOrUpdateAll(classification
.getAllNodes());
395 UUID result
=dao
.saveOrUpdate(classification
);
400 public UUID
saveTreeNode(ITaxonTreeNode treeNode
) {
401 if(treeNode
instanceof Classification
){
402 return dao
.save((Classification
) treeNode
).getUuid();
403 }else if(treeNode
instanceof TaxonNode
){
404 return taxonNodeDao
.save((TaxonNode
)treeNode
).getUuid();
410 public List
<TaxonNode
> getAllNodes(){
411 return taxonNodeDao
.list(null,null);
415 public List
<UuidAndTitleCache
<TaxonNode
>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid
, Integer limit
, String pattern
) {
416 return taxonDao
.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao
.load(classificationUuid
), limit
, pattern
);
420 public List
<UuidAndTitleCache
<TaxonNode
>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification
, Integer limit
, String pattern
) {
421 return taxonDao
.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification
, limit
, pattern
);
425 public List
<UuidAndTitleCache
<TaxonNode
>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid
) {
426 return taxonDao
.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao
.load(classificationUuid
), null, null);
430 public List
<UuidAndTitleCache
<TaxonNode
>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification
) {
431 return taxonDao
.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification
, null, null);
435 public List
<UuidAndTitleCache
<Classification
>> getUuidAndTitleCache(Integer limit
, String pattern
) {
436 return dao
.getUuidAndTitleCache(limit
, pattern
);
440 public Map
<UUID
, List
<MediaRepresentation
>> getAllMediaForChildNodes(
441 TaxonNode taxonNode
, List
<String
> propertyPaths
, int size
,
442 int height
, int widthOrDuration
, String
[] mimeTypes
) {
444 TreeMap
<UUID
, List
<MediaRepresentation
>> result
= new TreeMap
<UUID
, List
<MediaRepresentation
>>();
445 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
446 List
<MediaRepresentation
> mediaRepresentations
= new ArrayList
<MediaRepresentation
>();
448 //add all media of the children to the result map
449 if (taxonNode
!= null){
451 List
<TaxonNode
> nodes
= new ArrayList
<TaxonNode
>();
453 nodes
.add(loadTaxonNode(taxonNode
, propertyPaths
));
454 nodes
.addAll(loadChildNodesOfTaxonNode(taxonNode
, propertyPaths
));
457 for(TaxonNode node
: nodes
){
458 Taxon taxon
= node
.getTaxon();
459 for (TaxonDescription taxonDescription
: taxon
.getDescriptions()){
460 for (DescriptionElementBase descriptionElement
: taxonDescription
.getElements()){
461 for(Media media
: descriptionElement
.getMedia()){
462 taxonMedia
.add(media
);
464 //find the best matching representation
465 mediaRepresentations
.add(MediaUtils
.findBestMatchingRepresentation(media
,null, size
, height
, widthOrDuration
, mimeTypes
));
470 result
.put(taxon
.getUuid(), mediaRepresentations
);
482 public Map
<UUID
, List
<MediaRepresentation
>> getAllMediaForChildNodes(Taxon taxon
, Classification taxTree
, List
<String
> propertyPaths
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
483 TaxonNode node
= taxTree
.getNode(taxon
);
485 return getAllMediaForChildNodes(node
, propertyPaths
, size
, height
, widthOrDuration
, mimeTypes
);
489 @Transactional(readOnly
= false)
490 public void updateTitleCache(Class
<?
extends Classification
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<Classification
> cacheStrategy
, IProgressMonitor monitor
) {
492 clazz
= Classification
.class;
494 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
499 * @param allNodesOfClassification
500 * @return null - if allNodesOfClassification is empty <br>
503 private Map
<String
, List
<TaxonNode
>> getSortedGenusList(Collection
<TaxonNode
> allNodesOfClassification
){
505 if(allNodesOfClassification
== null || allNodesOfClassification
.isEmpty()){
508 Map
<String
, List
<TaxonNode
>> sortedGenusMap
= new HashMap
<>();
509 for(TaxonNode node
:allNodesOfClassification
){
510 final TaxonNode tn
= node
;
511 Taxon taxon
= node
.getTaxon();
512 INonViralName
<?
> name
= taxon
.getName();
513 String genusOrUninomial
= name
.getGenusOrUninomial();
514 //if rank unknown split string and take first word
515 if(genusOrUninomial
== null){
516 String titleCache
= taxon
.getTitleCache();
517 String
[] split
= titleCache
.split("\\s+");
519 genusOrUninomial
= s
;
523 //if node has children
525 //retrieve list from map if not create List
526 if(sortedGenusMap
.containsKey(genusOrUninomial
)){
527 List
<TaxonNode
> list
= sortedGenusMap
.get(genusOrUninomial
);
529 sortedGenusMap
.put(genusOrUninomial
, list
);
531 //create List for genus
532 List
<TaxonNode
> list
= new ArrayList
<TaxonNode
>();
534 sortedGenusMap
.put(genusOrUninomial
, list
);
537 return sortedGenusMap
;
542 * creates new Classification and parent TaxonNodes at genus level
545 * @param map GenusMap which holds a name (Genus) and all the same Taxa as a list
546 * @param classification you want to improve the hierarchy (will not be modified)
547 * @param configurator to change certain settings, if null then standard settings will be taken
548 * @return new classification with parentNodes for each entry in the map
550 @SuppressWarnings({ "rawtypes", "unchecked" })
551 @Transactional(readOnly
= false)
553 public UpdateResult
createHierarchyInClassification(Classification classification
, CreateHierarchyForClassificationConfigurator configurator
){
554 UpdateResult result
= new UpdateResult();
555 classification
= dao
.findByUuid(classification
.getUuid());
556 Map
<String
, List
<TaxonNode
>> map
= getSortedGenusList(classification
.getAllNodes());
558 final String APPENDIX
= "repaired";
559 String titleCache
= org
.apache
.commons
.lang
.StringUtils
.isBlank(classification
.getTitleCache()) ?
" " : classification
.getTitleCache() ;
560 //TODO classification clone???
561 Classification newClassification
= Classification
.NewInstance(titleCache
+" "+ APPENDIX
);
562 newClassification
.setReference(classification
.getReference());
564 for(Map
.Entry
<String
, List
<TaxonNode
>> entry
:map
.entrySet()){
565 String genus
= entry
.getKey();
566 List
<TaxonNode
> listOfTaxonNodes
= entry
.getValue();
567 TaxonNode parentNode
= null;
568 //Search for genus in list
569 for(TaxonNode tNode
:listOfTaxonNodes
){
570 //take that taxonNode as parent and remove from list with all it possible children
572 TaxonNameBase name
= tNode
.getTaxon().getName();
573 if(name
.getNameCache().equalsIgnoreCase(genus
)){
574 TaxonNode clone
= (TaxonNode
) tNode
.clone();
575 if(!tNode
.hasChildNodes()){
576 //FIXME remove classification
577 // parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
578 parentNode
= newClassification
.addChildNode(clone
, 0, clone
.getReference(), clone
.getMicroReference());
579 //remove taxonNode from list because just added to classification
580 result
.addUpdatedObject(tNode
);
581 listOfTaxonNodes
.remove(tNode
);
584 //save prior Hierarchy and remove them from the list
585 List
<TaxonNode
> copyAllChildrenToTaxonNode
= copyAllChildrenToTaxonNode(tNode
, clone
, result
);
586 // parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
587 //FIXME remove classification
588 parentNode
= newClassification
.addChildNode(clone
, 0, clone
.getReference(), clone
.getMicroReference());
589 //remove taxonNode from list because just added to classification
590 result
.addUpdatedObject(tNode
);
591 listOfTaxonNodes
.remove(tNode
);
592 if(copyAllChildrenToTaxonNode
!= null){
593 listOfTaxonNodes
= (List
<TaxonNode
>) CollectionUtils
.removeAll(listOfTaxonNodes
, copyAllChildrenToTaxonNode
);
599 if(parentNode
== null){
600 //if no match found in list, create parentNode
601 NonViralNameParserImpl parser
= NonViralNameParserImpl
.NewInstance();
602 NonViralName nonViralName
= parser
.parseFullName(genus
);
603 TaxonNameBase taxonNameBase
= nonViralName
;
604 //TODO Sec via configurator
605 Taxon taxon
= Taxon
.NewInstance(taxonNameBase
, null);
606 parentNode
= newClassification
.addChildTaxon(taxon
, 0, null, null);
607 result
.addUpdatedObject(parentNode
);
609 //iterate over the rest of the list
610 for(TaxonNode tn
: listOfTaxonNodes
){
611 //if TaxonNode has a parent and this is not the classification then skip it
612 //and add to new classification via the parentNode as children of it
613 //this should assures to keep the already existing hierarchy
614 //FIXME: Assert is not rootnode --> entrypoint is not classification in future but rather rootNode
616 if(!tn
.isTopmostNode()){
617 continue; //skip to next taxonNode
620 TaxonNode clone
= (TaxonNode
) tn
.clone();
621 //FIXME: citation from node
622 //TODO: addchildNode without citation and references
623 // TaxonNode taxonNode = parentNode.addChildNode(clone, classification.getCitation(), classification.getMicroReference());
624 TaxonNode taxonNode
= parentNode
.addChildNode(clone
, clone
.getReference(), clone
.getMicroReference());
625 result
.addUnChangedObject(clone
);
626 if(tn
.hasChildNodes()){
627 //save hierarchy in new classification
628 List
<TaxonNode
> copyAllChildrenToTaxonNode
= copyAllChildrenToTaxonNode(tn
, taxonNode
, result
);
629 if(copyAllChildrenToTaxonNode
!= null){
630 listOfTaxonNodes
= (List
<TaxonNode
>) CollectionUtils
.removeAll(listOfTaxonNodes
, copyAllChildrenToTaxonNode
);
635 dao
.saveOrUpdate(newClassification
);
636 result
.setCdmEntity(newClassification
);
642 * recursive method to get all childnodes of taxonNode in classification.
644 * @param classification just for References and Citation, can be null
645 * @param copyFromNode TaxonNode with Children
646 * @param copyToNode TaxonNode which will receive the children
647 * @return List of ChildNode which has been added. If node has no children returns null
649 private List
<TaxonNode
> copyAllChildrenToTaxonNode(TaxonNode copyFromNode
, TaxonNode copyToNode
, UpdateResult result
) {
650 List
<TaxonNode
> childNodes
;
651 if(!copyFromNode
.hasChildNodes()){
654 childNodes
= copyFromNode
.getChildNodes();
656 for(TaxonNode childNode
:childNodes
){
657 TaxonNode clone
= (TaxonNode
) childNode
.clone();
658 result
.addUnChangedObject(clone
);
659 if(childNode
.hasChildNodes()){
660 copyAllChildrenToTaxonNode(childNode
, clone
, result
);
662 //FIXME: citation from node instead of classification
663 // copyToNode.addChildNode(clone,classification.getCitation(), classification.getMicroReference());
664 copyToNode
.addChildNode(clone
, clone
.getReference(), clone
.getMicroReference());
673 public ClassificationLookupDTO
classificationLookup(Classification classification
) {
674 return dao
.classificationLookup(classification
);
679 public DeleteResult
delete(UUID classificationUuid
, TaxonDeletionConfigurator config
){
680 DeleteResult result
= new DeleteResult();
681 Classification classification
= dao
.findByUuid(classificationUuid
);
682 if (classification
== null){
683 result
.addException(new IllegalArgumentException("The classification does not exist in database."));
687 if (!classification
.hasChildNodes()){
688 dao
.delete(classification
);
690 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
) ){
691 TaxonNode root
= classification
.getRootNode();
692 taxonNodeDao
.delete(root
, true);
693 dao
.delete(classification
);
701 public List
<GroupedTaxonDTO
> groupTaxaByHigherTaxon(List
<UUID
> originalTaxonUuids
, UUID classificationUuid
, Rank minRank
, Rank maxRank
){
702 List
<GroupedTaxonDTO
> result
= new ArrayList
<>();
704 //get treeindex for each taxonUUID
705 Map
<UUID
, TreeIndex
> taxonIdTreeIndexMap
= dao
.treeIndexForTaxonUuids(classificationUuid
, originalTaxonUuids
);
707 //build treeindex list (or tree)
708 //TODO make it work with TreeIndex or move there
709 List
<String
> treeIndexClosureStr
= new ArrayList
<>();
710 for (TreeIndex treeIndex
: taxonIdTreeIndexMap
.values()){
711 String
[] splits
= treeIndex
.toString().substring(1).split(ITreeNode
.separator
);
712 String currentIndex
= ITreeNode
.separator
;
713 for (String split
: splits
){
714 if (split
.equals("")){
717 currentIndex
+= split
+ ITreeNode
.separator
;
718 if (!treeIndexClosureStr
.contains(currentIndex
) && !split
.startsWith(ITreeNode
.treePrefix
)){
719 treeIndexClosureStr
.add(currentIndex
);
724 //get rank sortindex for all parent taxa with sortindex <= minRank and sortIndex >= maxRank (if available)
725 Integer minRankOrderIndex
= minRank
== null ?
null : minRank
.getOrderIndex();
726 Integer maxRankOrderIndex
= maxRank
== null ?
null : maxRank
.getOrderIndex();
727 List
<TreeIndex
> treeIndexClosure
= TreeIndex
.NewListInstance(treeIndexClosureStr
);
729 Map
<TreeIndex
, Integer
> treeIndexSortIndexMapTmp
= taxonNodeDao
.rankOrderIndexForTreeIndex(treeIndexClosure
, minRankOrderIndex
, maxRankOrderIndex
);
731 //remove all treeindex with "exists child in above map(and child.sortindex > xxx)
732 List
<TreeIndex
> treeIndexList
= TreeIndex
.sort(treeIndexSortIndexMapTmp
.keySet());
734 Map
<TreeIndex
, Integer
> treeIndexSortIndexMap
= new HashMap
<>();
735 TreeIndex lastTreeIndex
= null;
736 for (TreeIndex treeIndex
: treeIndexList
){
737 if (lastTreeIndex
!= null && lastTreeIndex
.hasChild(treeIndex
)){
738 treeIndexSortIndexMap
.remove(lastTreeIndex
);
740 treeIndexSortIndexMap
.put(treeIndex
, treeIndexSortIndexMapTmp
.get(treeIndex
));
741 lastTreeIndex
= treeIndex
;
744 //get taxonID for treeIndexes
745 Map
<TreeIndex
, UuidAndTitleCache
<?
>> treeIndexTaxonIdMap
= taxonNodeDao
.taxonUuidsForTreeIndexes(treeIndexSortIndexMap
.keySet());
748 for (UUID originalTaxonUuid
: originalTaxonUuids
){
749 GroupedTaxonDTO item
= new GroupedTaxonDTO();
751 item
.setTaxonUuid(originalTaxonUuid
);
752 TreeIndex groupTreeIndex
= taxonIdTreeIndexMap
.get(originalTaxonUuid
);
753 String groupIndexX
= TreeIndex
.toString(groupTreeIndex
);
754 while (groupTreeIndex
!= null){
755 if (treeIndexTaxonIdMap
.get(groupTreeIndex
) != null){
756 UuidAndTitleCache
<?
> uuidAndLabel
= treeIndexTaxonIdMap
.get(groupTreeIndex
);
757 item
.setGroupTaxonUuid(uuidAndLabel
.getUuid());
758 item
.setGroupTaxonName(uuidAndLabel
.getTitleCache());
761 groupTreeIndex
= groupTreeIndex
.parent();
762 // int index = groupIndex.substring(0, groupIndex.length()-1).lastIndexOf(ITreeNode.separator);
763 // groupIndex = index < 0 ? null : groupIndex.substring(0, index+1);
775 public List
<GroupedTaxonDTO
> groupTaxaByMarkedParents(List
<UUID
> originalTaxonUuids
, UUID classificationUuid
,
776 MarkerType markerType
, Boolean flag
) {
778 List
<GroupedTaxonDTO
> result
= new ArrayList
<>();
780 //get treeindex for each taxonUUID
781 Map
<UUID
, TreeIndex
> taxonIdTreeIndexMap
= dao
.treeIndexForTaxonUuids(classificationUuid
, originalTaxonUuids
);
783 //get all marked tree indexes
784 Set
<TreeIndex
> markedTreeIndexes
= dao
.getMarkedTreeIndexes(markerType
, flag
);
787 Map
<TreeIndex
, TreeIndex
> groupedMap
= TreeIndex
.group(markedTreeIndexes
, taxonIdTreeIndexMap
.values());
788 Set
<TreeIndex
> notNullGroups
= new HashSet
<>(groupedMap
.values());
789 notNullGroups
.remove(null);
791 //get taxonInfo for treeIndexes
792 Map
<TreeIndex
, UuidAndTitleCache
<?
>> treeIndexTaxonIdMap
= taxonNodeDao
.taxonUuidsForTreeIndexes(notNullGroups
);
795 for (UUID originalTaxonUuid
: originalTaxonUuids
){
796 GroupedTaxonDTO item
= new GroupedTaxonDTO();
798 item
.setTaxonUuid(originalTaxonUuid
);
800 TreeIndex toBeGroupedTreeIndex
= taxonIdTreeIndexMap
.get(originalTaxonUuid
);
801 TreeIndex groupTreeIndex
= groupedMap
.get(toBeGroupedTreeIndex
);
802 UuidAndTitleCache
<?
> uuidAndLabel
= treeIndexTaxonIdMap
.get(groupTreeIndex
);
803 if (uuidAndLabel
!= null){
804 item
.setGroupTaxonUuid(uuidAndLabel
.getUuid());
805 item
.setGroupTaxonName(uuidAndLabel
.getTitleCache());
816 public UUID
getTaxonNodeUuidByTaxonUuid(UUID classificationUuid
, UUID taxonUuid
) {
817 Map
<UUID
, UUID
> map
= dao
.getTaxonNodeUuidByTaxonUuid(classificationUuid
, Arrays
.asList(taxonUuid
));
818 UUID taxonNodeUuid
= map
.get(taxonUuid
);
819 return taxonNodeUuid
;
826 public TaxonInContextDTO
getTaxonInContext(UUID classificationUuid
, UUID taxonBaseUuid
,
827 Boolean doChildren
, Boolean doSynonyms
, List
<UUID
> ancestorMarkers
,
828 NodeSortMode sortMode
) {
829 TaxonInContextDTO result
= new TaxonInContextDTO();
831 TaxonBase
<?
> taxonBase
= taxonDao
.load(taxonBaseUuid
);
832 if (taxonBase
== null){
833 throw new EntityNotFoundException("Taxon with uuid " + taxonBaseUuid
+ " not found in datasource");
835 boolean isSynonym
= false;
837 if (taxonBase
.isInstanceOf(Synonym
.class)){
839 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
840 acceptedTaxon
= synonym
.getAcceptedTaxon();
841 if (acceptedTaxon
== null) {
842 throw new EntityNotFoundException("Accepted taxon not found for synonym" );
844 TaxonStatus taxonStatus
= TaxonStatus
.Synonym
;
845 if (synonym
.getName()!= null && acceptedTaxon
.getName() != null
846 && synonym
.getName().getHomotypicalGroup().equals(acceptedTaxon
.getName().getHomotypicalGroup())){
847 taxonStatus
= TaxonStatus
.SynonymObjective
;
849 result
.setTaxonStatus(taxonStatus
);
852 acceptedTaxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
853 result
.setTaxonStatus(TaxonStatus
.Accepted
);
855 UUID acceptedTaxonUuid
= acceptedTaxon
.getUuid();
857 UUID taxonNodeUuid
= getTaxonNodeUuidByTaxonUuid(classificationUuid
, acceptedTaxonUuid
);
858 if (taxonNodeUuid
== null) {
859 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
);
861 result
.setTaxonNodeUuid(taxonNodeUuid
);
863 //TODO make it a dao call
864 Taxon parentTaxon
= getParentTaxon(classificationUuid
, acceptedTaxon
);
865 if (parentTaxon
!= null){
866 result
.setParentTaxonUuid(parentTaxon
.getUuid());
867 result
.setParentTaxonLabel(parentTaxon
.getTitleCache());
868 if (parentTaxon
.getName() != null){
869 result
.setParentNameLabel(parentTaxon
.getName().getTitleCache());
874 result
.setTaxonUuid(taxonBaseUuid
);
875 result
.setClassificationUuid(classificationUuid
);
876 if (taxonBase
.getSec() != null){
877 result
.setSecundumUuid(taxonBase
.getSec().getUuid());
878 result
.setSecundumLabel(taxonBase
.getSec().getTitleCache());
880 result
.setTaxonLabel(taxonBase
.getTitleCache());
882 TaxonNameBase
<?
,?
> name
= taxonBase
.getName();
883 result
.setNameUuid(name
.getUuid());
884 result
.setNameLabel(name
.getTitleCache());
885 result
.setNameWithoutAuthor(name
.getNameCache());
886 result
.setGenusOrUninomial(name
.getGenusOrUninomial());
887 result
.setInfraGenericEpithet(name
.getInfraGenericEpithet());
888 result
.setSpeciesEpithet(name
.getSpecificEpithet());
889 result
.setInfraSpecificEpithet(name
.getInfraSpecificEpithet());
891 result
.setAuthorship(name
.getAuthorshipCache());
893 Rank rank
= name
.getRank();
895 result
.setRankUuid(rank
.getUuid());
896 String rankLabel
= rank
.getAbbreviation();
897 if (StringUtils
.isBlank(rankLabel
)){
898 rankLabel
= rank
.getLabel();
900 result
.setRankLabel(rankLabel
);
903 boolean recursive
= false;
904 Integer pageSize
= null;
905 Integer pageIndex
= null;
906 Pager
<TaxonNodeDto
> children
= taxonNodeService
.pageChildNodesDTOs(taxonNodeUuid
, recursive
, doSynonyms
, sortMode
, pageSize
, pageIndex
);
910 for (TaxonNodeDto childDto
: children
.getRecords()){
911 if (doChildren
&& childDto
.getStatus().equals(TaxonStatus
.Accepted
)){
912 EntityDTO
<Taxon
> child
= new EntityDTO
<Taxon
>(childDto
.getTaxonUuid(), childDto
.getTitleCache());
913 result
.addChild(child
);
914 }else if (doSynonyms
&& childDto
.getStatus().isSynonym()){
915 EntityDTO
<Synonym
> child
= new EntityDTO
<Synonym
>(childDto
.getTaxonUuid(), childDto
.getTitleCache());
916 result
.addSynonym(child
);
920 result
.setAcceptedTaxonUuid(acceptedTaxonUuid
);
921 String nameTitel
= acceptedTaxon
.getName() == null ?
null : acceptedTaxon
.getName().getTitleCache();
922 result
.setAcceptedTaxonLabel(acceptedTaxon
.getTitleCache());
923 result
.setAcceptedNameLabel(nameTitel
);
927 if (ancestorMarkers
!= null && !ancestorMarkers
.isEmpty()){
928 List
<DefinedTermBase
> markerTypesTerms
= termDao
.list(ancestorMarkers
, pageSize
, null, null, null);
929 List
<MarkerType
> markerTypes
= new ArrayList
<>();
930 for (DefinedTermBase
<?
> term
: markerTypesTerms
){
931 if (term
.isInstanceOf(MarkerType
.class)){
932 markerTypes
.add(CdmBase
.deproxy(term
, MarkerType
.class));
935 if (! markerTypes
.isEmpty()){
936 TaxonNode node
= taxonNodeDao
.findByUuid(taxonNodeUuid
);
937 handleAncestorsForMarkersRecursive(result
, markerTypes
, node
);
945 * @param classificationUuid
946 * @param acceptedTaxon
949 private Taxon
getParentTaxon(UUID classificationUuid
, Taxon acceptedTaxon
) {
950 if (classificationUuid
== null){
953 TaxonNode parent
= null;
954 for (TaxonNode node
: acceptedTaxon
.getTaxonNodes()){
955 if (classificationUuid
.equals(node
.getClassification().getUuid())){
956 parent
= node
.getParent();
960 return parent
.getTaxon();
970 private void handleAncestorsForMarkersRecursive(TaxonInContextDTO result
, List
<MarkerType
> markerTypes
, TaxonNode node
) {
971 for (MarkerType type
: markerTypes
){
972 Taxon taxon
= node
.getTaxon();
973 if (taxon
!= null && taxon
.hasMarker(type
, true)){
974 String label
= taxon
.getName() == null? taxon
.getTitleCache() : taxon
.getName().getTitleCache();
975 MarkedEntityDTO
<Taxon
> dto
= new MarkedEntityDTO
<>(type
, true, taxon
.getUuid(), label
);
976 result
.addMarkedAncestor(dto
);
979 TaxonNode parentNode
= node
.getParent();
980 if (parentNode
!= null){
981 handleAncestorsForMarkersRecursive(result
, markerTypes
, parentNode
);