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.
9 package eu
.etaxonomy
.cdm
.api
.service
;
11 import java
.util
.ArrayList
;
12 import java
.util
.Arrays
;
13 import java
.util
.Collection
;
14 import java
.util
.Collections
;
15 import java
.util
.Comparator
;
16 import java
.util
.HashSet
;
17 import java
.util
.Iterator
;
18 import java
.util
.List
;
21 import java
.util
.UUID
;
22 import java
.util
.stream
.Collectors
;
24 import org
.apache
.logging
.log4j
.LogManager
;
25 import org
.apache
.logging
.log4j
.Logger
;
26 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
27 import org
.springframework
.security
.core
.Authentication
;
28 import org
.springframework
.stereotype
.Service
;
29 import org
.springframework
.transaction
.annotation
.Transactional
;
30 import org
.springframework
.transaction
.interceptor
.TransactionAspectSupport
;
32 import eu
.etaxonomy
.cdm
.api
.service
.UpdateResult
.Status
;
33 import eu
.etaxonomy
.cdm
.api
.service
.config
.ForSubtreeConfiguratorBase
;
34 import eu
.etaxonomy
.cdm
.api
.service
.config
.NodeDeletionConfigurator
.ChildHandling
;
35 import eu
.etaxonomy
.cdm
.api
.service
.config
.PublishForSubtreeConfigurator
;
36 import eu
.etaxonomy
.cdm
.api
.service
.config
.SecundumForSubtreeConfigurator
;
37 import eu
.etaxonomy
.cdm
.api
.service
.config
.SubtreeCloneConfigurator
;
38 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
39 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonNodeDeletionConfigurator
;
40 import eu
.etaxonomy
.cdm
.api
.service
.dto
.CdmEntityIdentifier
;
41 import eu
.etaxonomy
.cdm
.api
.service
.dto
.CreateTaxonDTO
;
42 import eu
.etaxonomy
.cdm
.api
.service
.dto
.TaxonDistributionDTO
;
43 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
44 import eu
.etaxonomy
.cdm
.api
.service
.pager
.PagerUtils
;
45 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.AbstractPagerImpl
;
46 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
47 import eu
.etaxonomy
.cdm
.common
.monitor
.DefaultProgressMonitor
;
48 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
49 import eu
.etaxonomy
.cdm
.common
.monitor
.SubProgressMonitor
;
50 import eu
.etaxonomy
.cdm
.compare
.taxon
.HomotypicGroupTaxonComparator
;
51 import eu
.etaxonomy
.cdm
.compare
.taxon
.TaxonNodeSortMode
;
52 import eu
.etaxonomy
.cdm
.filter
.TaxonNodeFilter
;
53 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
54 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
55 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
56 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
57 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
58 import eu
.etaxonomy
.cdm
.model
.common
.TreeIndex
;
59 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
60 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementSource
;
61 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveDataSet
;
62 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
63 import eu
.etaxonomy
.cdm
.model
.metadata
.SecReferenceHandlingEnum
;
64 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
65 import eu
.etaxonomy
.cdm
.model
.name
.HybridRelationship
;
66 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
67 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
68 import eu
.etaxonomy
.cdm
.model
.permission
.Operation
;
69 import eu
.etaxonomy
.cdm
.model
.reference
.NamedSource
;
70 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
71 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
72 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonTreeNode
;
73 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
74 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymType
;
75 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
76 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
77 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
78 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNodeAgentRelation
;
79 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNodeStatus
;
80 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
81 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
82 import eu
.etaxonomy
.cdm
.model
.term
.DefinedTerm
;
83 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.Restriction
;
84 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.IBeanInitializer
;
85 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.IHomotypicalGroupDao
;
86 import eu
.etaxonomy
.cdm
.persistence
.dao
.reference
.IOriginalSourceDao
;
87 import eu
.etaxonomy
.cdm
.persistence
.dao
.reference
.IReferenceDao
;
88 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.IClassificationDao
;
89 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonNodeDao
;
90 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonNodeFilterDao
;
91 import eu
.etaxonomy
.cdm
.persistence
.dto
.HomotypicGroupDto
;
92 import eu
.etaxonomy
.cdm
.persistence
.dto
.TaxonNodeDto
;
93 import eu
.etaxonomy
.cdm
.persistence
.permission
.ICdmPermissionEvaluator
;
94 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
101 @Transactional(readOnly
= true)
102 public class TaxonNodeServiceImpl
103 extends AnnotatableServiceBase
<TaxonNode
, ITaxonNodeDao
>
104 implements ITaxonNodeService
{
106 private static final Logger logger
= LogManager
.getLogger();
109 private IBeanInitializer defaultBeanInitializer
;
112 private ITaxonService taxonService
;
115 // private IReferenceService referenceService;
118 private IDescriptiveDataSetService dataSetService
;
121 private IAgentService agentService
;
124 private INameService nameService
;
127 private IOriginalSourceDao sourceDao
;
130 private ITaxonNodeFilterDao nodeFilterDao
;
133 private IReferenceDao referenceDao
;
136 private IClassificationDao classificationDao
;
139 private IHomotypicalGroupDao homotypicalGroupDao
;
142 IProgressMonitorService progressMonitorService
;
145 private ICdmPermissionEvaluator permissionEvaluator
;
148 public List
<TaxonNode
> loadChildNodesOfTaxonNode(TaxonNode taxonNode
,
149 List
<String
> propertyPaths
, boolean recursive
, boolean includeUnpublished
,
150 TaxonNodeSortMode sortMode
) {
152 taxonNode
= load(taxonNode
.getUuid());
153 List
<TaxonNode
> childNodes
;
154 if (recursive
== true){
155 Comparator
<TaxonNode
> comparator
= sortMode
== null?
null : sortMode
.comparator();
156 childNodes
= dao
.listChildrenOf(taxonNode
, null, null, recursive
, includeUnpublished
, propertyPaths
, comparator
);
157 }else if (includeUnpublished
){
158 childNodes
= new ArrayList
<>(taxonNode
.getChildNodes());
160 childNodes
= new ArrayList
<>();
161 for (TaxonNode node
:taxonNode
.getChildNodes()){
162 if (node
.getTaxon().isPublish()){
163 childNodes
.add(node
);
168 if (recursive
== false && sortMode
!= null){
169 Comparator
<TaxonNode
> comparator
= sortMode
.comparator();
170 Collections
.sort(childNodes
, comparator
);
172 defaultBeanInitializer
.initializeAll(childNodes
, propertyPaths
);
177 public List
<TaxonNode
> listChildrenOf(TaxonNode node
, Integer pageSize
, Integer pageIndex
,
178 boolean recursive
, boolean includeUnpublished
, List
<String
> propertyPaths
){
179 return dao
.listChildrenOf(node
, pageSize
, pageIndex
, recursive
, includeUnpublished
, propertyPaths
, null);
183 public TaxonNodeDto
getParentUuidAndTitleCache(ITaxonTreeNode child
) {
184 UUID uuid
= child
.getUuid();
185 int id
= child
.getId();
186 TaxonNodeDto uuidAndTitleCache
= new TaxonNodeDto(uuid
, id
, null);
187 return getParentUuidAndTitleCache(uuidAndTitleCache
);
191 public TaxonNodeDto
getParentUuidAndTitleCache(TaxonNodeDto child
) {
192 return dao
.getParentUuidAndTitleCache(child
);
196 public List
<TaxonNodeDto
> listChildNodesAsTaxonNodeDto(TaxonNodeDto parent
) {
197 return dao
.listChildNodesAsTaxonNodeDto(parent
);
201 public List
<TaxonNodeDto
> getUuidAndTitleCache(Integer limit
, String pattern
, UUID classificationUuid
) {
202 return dao
.getUuidAndTitleCache(limit
, pattern
, classificationUuid
, true);
206 public List
<TaxonNodeDto
> listChildNodesAsTaxonNodeDto(ITaxonTreeNode parent
) {
207 List
<String
> propertyPaths
= new ArrayList
<>();
208 propertyPaths
.add("parent");
209 parent
= dao
.load(parent
.getId(), propertyPaths
);
210 TaxonNodeDto uuidAndTitleCache
= new TaxonNodeDto(parent
);
211 return listChildNodesAsTaxonNodeDto(uuidAndTitleCache
);
215 public List
<TaxonNodeDto
> taxonNodeDtoParentRank(Classification classification
, Rank rank
, TaxonName name
) {
216 return dao
.getParentTaxonNodeDtoForRank(classification
, rank
, name
);
220 public List
<TaxonNodeDto
> taxonNodeDtoParentRank(Classification classification
, Rank rank
, TaxonBase
<?
> taxonBase
) {
221 return dao
.getParentTaxonNodeDtoForRank(classification
, rank
, taxonBase
);
225 public Pager
<TaxonNodeDto
> pageChildNodesDTOs(UUID taxonNodeUuid
, boolean recursive
, boolean includeUnpublished
,
226 boolean doSynonyms
, TaxonNodeSortMode sortMode
,
227 Integer pageSize
, Integer pageIndex
) {
229 TaxonNode parentNode
= dao
.load(taxonNodeUuid
);
231 List
<CdmBase
> allRecords
= new ArrayList
<>();
234 List
<TaxonNode
> childNodes
= loadChildNodesOfTaxonNode(parentNode
, null, recursive
, includeUnpublished
, sortMode
);
235 allRecords
.addAll(childNodes
);
237 //add synonyms if pager is not yet full synonyms
239 List
<Synonym
> synList
= new ArrayList
<>(parentNode
.getTaxon().getSynonyms());
240 Collections
.sort(synList
, new HomotypicGroupTaxonComparator(null));
243 allRecords
.addAll(synList
);
246 List
<TaxonNodeDto
> dtos
= new ArrayList
<>(pageSize
==null?
25:pageSize
);
247 long totalCount
= Long
.valueOf(allRecords
.size());
249 TaxonName parentName
= null;
251 for(CdmBase item
: PagerUtils
.pageList(allRecords
, pageIndex
, pageSize
)) {
252 if (item
.isInstanceOf(TaxonNode
.class)){
253 dtos
.add(new TaxonNodeDto(CdmBase
.deproxy(item
, TaxonNode
.class)));
254 }else if (item
.isInstanceOf(Synonym
.class)){
255 Synonym synonym
= CdmBase
.deproxy(item
, Synonym
.class);
256 parentName
= parentName
== null? parentNode
.getTaxon().getName(): parentName
;
257 boolean isHomotypic
= synonym
.getName().isHomotypic(parentName
);
258 dtos
.add(new TaxonNodeDto(synonym
, isHomotypic
));
261 return new DefaultPagerImpl
<>(pageIndex
, totalCount
, pageSize
, dtos
);
265 public TaxonNodeDto
parentDto(UUID taxonNodeUuid
) {
266 if (taxonNodeUuid
== null){
269 TaxonNode taxonNode
= dao
.load(taxonNodeUuid
);
270 if(taxonNode
.getParent() != null) {
271 return new TaxonNodeDto(taxonNode
.getParent());
277 public TaxonNodeDto
dto(UUID taxonNodeUuid
) {
278 if (taxonNodeUuid
== null){
281 return dao
.getTaxonNodeDto(taxonNodeUuid
);
285 public TaxonNodeDto
dto(UUID taxonUuid
, UUID classificationUuid
) {
286 if (taxonUuid
== null){
289 List
<TaxonNodeDto
> taxonNodes
= dao
.getTaxonNodeForTaxonInClassificationDto(taxonUuid
, classificationUuid
);
290 if (!taxonNodes
.isEmpty()){
291 return taxonNodes
.get(0);
298 protected void setDao(ITaxonNodeDao dao
) {
303 @Transactional(readOnly
= false)
304 public DeleteResult
makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode
, TaxonNode newAcceptedTaxonNode
,
305 SynonymType synonymType
, Reference citation
, String microReference
, SecReferenceHandlingEnum secHandling
, boolean setNameInSource
) {
307 // TODO at the moment this method only moves synonym-, concept relations and descriptions to the new accepted taxon
308 // in a future version we also want to move cdm data like annotations, marker, so., but we will need a policy for that
309 if (oldTaxonNode
== null || newAcceptedTaxonNode
== null || oldTaxonNode
.getTaxon().getName() == null){
310 throw new IllegalArgumentException("A mandatory parameter was null.");
313 if(oldTaxonNode
.equals(newAcceptedTaxonNode
)){
314 throw new IllegalArgumentException("Taxon can not be made synonym of its own.");
317 Classification classification
= oldTaxonNode
.getClassification();
318 Taxon oldTaxon
= HibernateProxyHelper
.deproxy(oldTaxonNode
.getTaxon());
319 Taxon newAcceptedTaxon
= (Taxon
)this.taxonService
.find(newAcceptedTaxonNode
.getTaxon().getUuid());
320 newAcceptedTaxon
= HibernateProxyHelper
.deproxy(newAcceptedTaxon
);
321 // Move oldTaxon to newTaxon
322 //TaxonName synonymName = oldTaxon.getName();
323 TaxonName newSynonymName
= CdmBase
.deproxy(oldTaxon
.getName());
324 HomotypicalGroup group
= CdmBase
.deproxy(newSynonymName
.getHomotypicalGroup());
325 if (synonymType
== null){
326 if (newSynonymName
.isHomotypic(newAcceptedTaxon
.getName())){
327 synonymType
= SynonymType
.HOMOTYPIC_SYNONYM_OF
;
329 synonymType
= SynonymType
.HETEROTYPIC_SYNONYM_OF
;
333 //set homotypic group
334 TaxonName newAcceptedTaxonName
= HibernateProxyHelper
.deproxy(newAcceptedTaxon
.getName(), TaxonName
.class);
335 newAcceptedTaxon
.setName(newAcceptedTaxonName
);
336 Reference secNewAccepted
= newAcceptedTaxon
.getSec();
337 Reference secOldAccepted
= oldTaxon
.getSec();
338 boolean uuidsEqual
= (secNewAccepted
!= null && secOldAccepted
!= null && secNewAccepted
.equals(secOldAccepted
)) || (secNewAccepted
== null && secOldAccepted
== null);
339 Reference newSec
= citation
;
340 //keep when same only warns in ui, the sec still
341 if (secHandling
!= null && secHandling
.equals(SecReferenceHandlingEnum
.KeepOrWarn
) ){
342 newSec
= oldTaxon
.getSec();
344 if (secHandling
!= null && secHandling
.equals(SecReferenceHandlingEnum
.AlwaysDelete
)){
348 Synonym newSyn
= newAcceptedTaxon
.addSynonymName(newSynonymName
, newSec
, microReference
, synonymType
);
350 newSyn
.setSec(newSec
);
352 newSyn
.setPublish(oldTaxon
.isPublish());
354 // Move Synonyms to new Taxon
355 // From ticket 3163 we can move taxon with accepted name having homotypic synonyms
356 List
<Synonym
> synonymsInHomotypicalGroup
= null;
358 //the synonyms of the homotypical group of the old taxon
359 if (synonymType
.equals(SynonymType
.HOMOTYPIC_SYNONYM_OF
)){
360 synonymsInHomotypicalGroup
= oldTaxon
.getSynonymsInGroup(group
);
363 Set
<Synonym
> syns
= new HashSet
<>(oldTaxon
.getSynonyms());
364 for(Synonym synonym
: syns
){
366 if(synonym
.getHomotypicGroup()!= null
367 && synonym
.getHomotypicGroup().equals(newAcceptedTaxonName
.getHomotypicalGroup())) {
368 srt
= SynonymType
.HOMOTYPIC_SYNONYM_OF
;
369 } else if(synonym
.getType() != null && synonym
.getType().equals(SynonymType
.HOMOTYPIC_SYNONYM_OF
)) {
370 if (synonymType
.equals(SynonymType
.HOMOTYPIC_SYNONYM_OF
)){
371 srt
= SynonymType
.HOMOTYPIC_SYNONYM_OF
;
373 srt
= SynonymType
.HETEROTYPIC_SYNONYM_OF
;
376 if (synonymsInHomotypicalGroup
!= null && synonymsInHomotypicalGroup
.contains(synonym
)){
377 srt
= SynonymType
.HOMOTYPIC_SYNONYM_OF
;
379 srt
= synonym
.getType();
382 if (secHandling
!= null && !secHandling
.equals(SecReferenceHandlingEnum
.KeepOrWarn
)){
383 synonym
.setSec(newSec
);
385 newAcceptedTaxon
.addSynonym(synonym
, srt
);
389 if(oldTaxonNode
.getChildNodes() != null && oldTaxonNode
.getChildNodes().size() != 0){
390 List
<TaxonNode
> childNodes
= new ArrayList
<>();
391 for (TaxonNode childNode
: oldTaxonNode
.getChildNodes()){
392 childNodes
.add(childNode
);
394 for(TaxonNode childNode
:childNodes
){
395 newAcceptedTaxonNode
.addChildNode(childNode
, childNode
.getReference(), childNode
.getMicroReference()); // childNode.getSynonymToBeUsed()
399 //Move Taxon RelationShips to new Taxon
400 for(TaxonRelationship taxonRelationship
: oldTaxon
.getTaxonRelations()){
401 Taxon fromTaxon
= HibernateProxyHelper
.deproxy(taxonRelationship
.getFromTaxon());
402 Taxon toTaxon
= HibernateProxyHelper
.deproxy(taxonRelationship
.getToTaxon());
403 if (fromTaxon
== oldTaxon
){
404 newAcceptedTaxon
.addTaxonRelation(taxonRelationship
.getToTaxon(), taxonRelationship
.getType(),
405 taxonRelationship
.getCitation(), taxonRelationship
.getCitationMicroReference());
407 }else if(toTaxon
== oldTaxon
){
408 fromTaxon
.addTaxonRelation(newAcceptedTaxon
, taxonRelationship
.getType(),
409 taxonRelationship
.getCitation(), taxonRelationship
.getCitationMicroReference());
410 taxonService
.saveOrUpdate(fromTaxon
);
413 logger
.warn("Taxon is not part of its own Taxonrelationship");
415 // Remove old relationships
417 fromTaxon
.removeTaxonRelation(taxonRelationship
);
418 toTaxon
.removeTaxonRelation(taxonRelationship
);
419 taxonRelationship
.setToTaxon(null);
420 taxonRelationship
.setFromTaxon(null);
423 //Move descriptions to new taxon
424 List
<TaxonDescription
> descriptions
= new ArrayList
<TaxonDescription
>( oldTaxon
.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())
425 for(TaxonDescription description
: descriptions
){
426 String message
= "Description copied from former accepted taxon: %s (Old title: %s)";
427 message
= String
.format(message
, oldTaxon
.getTitleCache(), description
.getTitleCache());
428 description
.setTitleCache(message
, true);
429 if (setNameInSource
) {
430 for (DescriptionElementBase element
: description
.getElements()){
431 for (DescriptionElementSource source
: element
.getSources()){
432 if (source
.getNameUsedInSource() == null){
433 source
.setNameUsedInSource(newSynonymName
);
438 //oldTaxon.removeDescription(description, false);
439 newAcceptedTaxon
.addDescription(description
);
441 oldTaxon
.clearDescriptions();
443 taxonService
.saveOrUpdate(newAcceptedTaxon
);
445 taxonService
.saveOrUpdate(oldTaxon
);
446 taxonService
.getSession().flush();
448 TaxonDeletionConfigurator conf
= new TaxonDeletionConfigurator();
449 conf
.setDeleteSynonymsIfPossible(false);
450 conf
.setDeleteNameIfPossible(false);
451 DeleteResult taxonDeleteResult
= taxonService
.isDeletable(oldTaxon
.getUuid(), conf
);
454 if (taxonDeleteResult
.isOk()){
455 result
= taxonService
.deleteTaxon(oldTaxon
.getUuid(), conf
, classification
.getUuid());
457 TaxonNodeDeletionConfigurator config
= new TaxonNodeDeletionConfigurator();
458 config
.setDeleteElement(false);
459 conf
.setTaxonNodeConfig(config
);
460 result
= deleteTaxonNode(oldTaxonNode
, conf
);
461 result
.getRelatedObjects().addAll(taxonDeleteResult
.getRelatedObjects()); //we want to know what causes that the taxon can not be deleted
462 result
.getExceptions().addAll(taxonDeleteResult
.getExceptions()); //same for the exceptions
465 result
.addUpdatedObject(newAcceptedTaxon
);
467 //oldTaxonNode.delete();
472 @Transactional(readOnly
= false)
473 public DeleteResult
makeTaxonNodeSynonymsOfAnotherTaxonNode( Set
<UUID
> oldTaxonNodeUuids
,
474 UUID newAcceptedTaxonNodeUUIDs
,
475 SynonymType synonymType
,
477 String microReference
,
478 SecReferenceHandlingEnum secHandling
,
479 boolean setNameInSource
) {
480 DeleteResult result
= new DeleteResult();
481 for (UUID nodeUuid
: oldTaxonNodeUuids
) {
482 result
.includeResult(makeTaxonNodeASynonymOfAnotherTaxonNode(nodeUuid
, newAcceptedTaxonNodeUUIDs
, synonymType
, citation
, microReference
, secHandling
, setNameInSource
));
488 @Transactional(readOnly
= false)
489 public DeleteResult
makeTaxonNodeASynonymOfAnotherTaxonNode(UUID oldTaxonNodeUuid
,
490 UUID newAcceptedTaxonNodeUUID
,
491 SynonymType synonymType
,
493 String microReference
,
494 SecReferenceHandlingEnum secHandling
,
495 boolean setNameInSource
) {
497 TaxonNode oldTaxonNode
= dao
.load(oldTaxonNodeUuid
);
498 TaxonNode oldTaxonParentNode
= oldTaxonNode
.getParent();
499 TaxonNode newTaxonNode
= dao
.load(newAcceptedTaxonNodeUUID
);
500 Reference citation
= referenceDao
.load(citationUuid
);
502 switch (secHandling
){
506 case UseNewParentSec
:
507 citation
= newTaxonNode
.getTaxon() != null? newTaxonNode
.getTaxon().getSec(): null;
511 Reference synSec
= oldTaxonNode
.getTaxon().getSec();
512 if (synSec
!= null ){
513 citation
= CdmBase
.deproxy(synSec
);
523 DeleteResult result
= makeTaxonNodeASynonymOfAnotherTaxonNode(oldTaxonNode
,
528 secHandling
, setNameInSource
);
530 result
.addUpdatedCdmId(CdmEntityIdentifier
.NewInstance(oldTaxonParentNode
));
531 result
.addUpdatedCdmId(CdmEntityIdentifier
.NewInstance(newTaxonNode
));
532 result
.setCdmEntity(oldTaxonParentNode
);
537 @Transactional(readOnly
= false)
538 public DeleteResult
deleteTaxonNodes(List
<TaxonNode
> list
, TaxonDeletionConfigurator config
) {
541 config
= new TaxonDeletionConfigurator();
543 DeleteResult result
= new DeleteResult();
544 Classification classification
= null;
545 List
<TaxonNode
> taxonNodes
= new ArrayList
<>(list
);
547 for (TaxonNode treeNode
:taxonNodes
){
548 if (treeNode
!= null){
551 taxonNode
= CdmBase
.deproxy(treeNode
);
552 TaxonNode parent
= taxonNode
.getParent();
553 //check whether the node has children or the children are already deleted
554 if(taxonNode
.hasChildNodes()) {
555 List
<TaxonNode
> children
= new ArrayList
<> ();
556 List
<TaxonNode
> childNodesList
= taxonNode
.getChildNodes();
557 children
.addAll(childNodesList
);
558 //To avoid NPE when child is also in list of taxonNodes, remove it from the list
559 Iterator
<TaxonNode
> it
= taxonNodes
.iterator();
560 for (TaxonNode child
: children
) {
561 while (it
.hasNext()) {
562 if (it
.next().equals(child
)) {
567 int compare
= config
.getTaxonNodeConfig().getChildHandling().compareTo(ChildHandling
.DELETE
);
568 boolean childHandling
= (compare
== 0)?
true: false;
570 boolean changeDeleteTaxon
= false;
571 if (!config
.getTaxonNodeConfig().isDeleteTaxon()){
572 config
.getTaxonNodeConfig().setDeleteTaxon(true);
573 changeDeleteTaxon
= true;
575 DeleteResult resultNodes
= deleteTaxonNodes(children
, config
);
576 if (!resultNodes
.isOk()){
577 result
.addExceptions(resultNodes
.getExceptions());
578 result
.setStatus(resultNodes
.getStatus());
580 if (changeDeleteTaxon
){
581 config
.getTaxonNodeConfig().setDeleteTaxon(false);
585 //move the children to the parent
587 for (TaxonNode child
: childNodesList
){
588 parent
.addChildNode(child
, child
.getReference(), child
.getMicroReference());
593 classification
= taxonNode
.getClassification();
595 if (classification
.getRootNode().equals(taxonNode
)){
596 classification
.removeRootNode();
597 classification
= null;
598 }else if (classification
.getChildNodes().contains(taxonNode
)){
599 Taxon taxon
= taxonNode
.getTaxon();
600 classification
.deleteChildNode(taxonNode
);
605 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
606 taxonService
.saveOrUpdate(taxon
);
607 saveOrUpdate(taxonNode
);
609 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
610 configNew
.setClassificationUuid(classification
.getUuid());
611 DeleteResult resultTaxon
= taxonService
.deleteTaxon(taxon
.getUuid(), configNew
, classification
.getUuid());
612 if (!resultTaxon
.isOk()){
613 result
.addExceptions(resultTaxon
.getExceptions());
614 result
.setStatus(resultTaxon
.getStatus());
619 classification
= null;
622 //classification = null;
623 Taxon taxon
= taxonNode
.getTaxon();
624 taxon
= CdmBase
.deproxy(taxon
);
626 taxon
.removeTaxonNode(taxonNode
);
627 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
628 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
629 saveOrUpdate(taxonNode
);
630 taxonService
.saveOrUpdate(taxon
);
631 DeleteResult resultTaxon
= taxonService
.deleteTaxon(taxon
.getUuid(), configNew
, classification
.getUuid());
633 if (!resultTaxon
.isOk()){
634 result
.addExceptions(resultTaxon
.getExceptions());
635 result
.setStatus(resultTaxon
.getStatus());
642 result
.addUpdatedObject(parent
);
643 if(result
.getCdmEntity() == null){
644 result
.setCdmEntity(taxonNode
);
646 UUID uuid
= dao
.delete(taxonNode
);
647 logger
.debug("Deleted node " +uuid
.toString());
651 /*if (classification != null){
652 result.addUpdatedObject(classification);
653 DeleteResult resultClassification = classService.delete(classification);
654 if (!resultClassification.isOk()){
655 result.addExceptions(resultClassification.getExceptions());
656 result.setStatus(resultClassification.getStatus());
664 @Transactional(readOnly
= false)
665 public DeleteResult
deleteTaxonNodes(Collection
<UUID
> nodeUuids
, TaxonDeletionConfigurator config
) {
666 List
<TaxonNode
> nodes
= new ArrayList
<>();
667 for(UUID nodeUuid
: nodeUuids
) {
668 nodes
.add(dao
.load(nodeUuid
));
670 return deleteTaxonNodes(nodes
, config
);
675 @Transactional(readOnly
= false)
676 public DeleteResult
deleteTaxonNode(UUID nodeUUID
, TaxonDeletionConfigurator config
) {
678 TaxonNode node
= CdmBase
.deproxy(dao
.load(nodeUUID
));
679 return deleteTaxonNode(node
, config
);
683 @Transactional(readOnly
= false)
684 public DeleteResult
deleteTaxonNode(TaxonNode node
, TaxonDeletionConfigurator config
) {
685 DeleteResult result
= new DeleteResult();
688 result
.addException(new Exception("The TaxonNode was already deleted."));
693 taxon
= HibernateProxyHelper
.deproxy(node
.getTaxon());
694 }catch(NullPointerException e
){
696 result
.addException(new Exception("The Taxon was already deleted."));
699 TaxonNode parent
= HibernateProxyHelper
.deproxy(node
.getParent(), TaxonNode
.class);
701 config
= new TaxonDeletionConfigurator();
704 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.MOVE_TO_PARENT
)){
705 Object
[] children
= node
.getChildNodes().toArray();
707 for (Object child
: children
){
708 childNode
= (TaxonNode
) child
;
709 parent
.addChildNode(childNode
, childNode
.getReference(), childNode
.getMicroReference());
712 DeleteResult tmpResult
= deleteTaxonNodes(node
.getChildNodes(), config
);
713 result
.includeResult(tmpResult
);
716 //remove node from DescriptiveDataSet
717 commonService
.getReferencingObjects(node
).stream()
718 .filter(obj
->obj
instanceof DescriptiveDataSet
)
720 ((DescriptiveDataSet
)dataSet
).removeTaxonSubtree(node
);
721 dataSetService
.saveOrUpdate((DescriptiveDataSet
) dataSet
);
725 if (config
.getTaxonNodeConfig().isDeleteTaxon() && (config
.isDeleteInAllClassifications() || taxon
.getTaxonNodes().size() == 1)){
726 result
= taxonService
.deleteTaxon(taxon
.getUuid(), config
, node
.getClassification().getUuid());
727 result
.addUpdatedObject(parent
);
732 result
.addUpdatedObject(taxon
);
735 result
.setCdmEntity(node
);
736 boolean success
= true;
738 success
= taxon
.removeTaxonNode(node
);
739 taxonService
.saveOrUpdate(taxon
);
741 dao
.saveOrUpdate(parent
);
743 result
.addUpdatedObject(parent
);
746 result
.setStatus(Status
.OK
);
748 parent
= HibernateProxyHelper
.deproxy(parent
, TaxonNode
.class);
749 int index
= parent
.getChildNodes().indexOf(node
);
751 parent
.removeChild(index
);
754 if (!dao
.delete(node
, config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
)).equals(null)){
755 result
.getUpdatedObjects().remove(node
);
756 result
.addDeletedObject(node
);
763 if (dao
.findByUuid(node
.getUuid()) != null){
765 result
.addException(new Exception("The node can not be removed from the taxon."));
773 public List
<TaxonNode
> listAllNodesForClassification(Classification classification
, Integer start
, Integer end
) {
774 return dao
.getTaxonOfAcceptedTaxaByClassification(classification
, start
, end
);
778 public int countAllNodesForClassification(Classification classification
) {
779 return dao
.countTaxonOfAcceptedTaxaByClassification(classification
);
784 public UpdateResult
moveTaxonNode(UUID taxonNodeUuid
, UUID targetNodeUuid
, int movingType
, SecReferenceHandlingEnum secHandling
, UUID secUuid
){
785 TaxonNode taxonNode
= HibernateProxyHelper
.deproxy(dao
.load(taxonNodeUuid
));
786 TaxonNode targetNode
= HibernateProxyHelper
.deproxy(dao
.load(targetNodeUuid
));
787 Reference sec
= null;
788 if (secUuid
!= null){
789 sec
= HibernateProxyHelper
.deproxy(referenceDao
.load(secUuid
));
791 UpdateResult result
= moveTaxonNode(taxonNode
, targetNode
, movingType
, secHandling
, sec
);
797 public UpdateResult
moveTaxonNode(TaxonNode taxonNode
, TaxonNode newParent
, int movingType
, SecReferenceHandlingEnum secHandling
, Reference sec
){
798 UpdateResult result
= new UpdateResult();
800 TaxonNode parentParent
= HibernateProxyHelper
.deproxy(newParent
.getParent());
801 Integer sortIndex
= -1;
802 if (movingType
== 0){
804 }else if (movingType
== 1){
805 sortIndex
= newParent
.getSortIndex();
806 newParent
= parentParent
;
807 } else if (movingType
== 2){
808 sortIndex
= newParent
.getSortIndex() +1;
809 newParent
= parentParent
;
812 result
.addException(new Exception("The moving type "+ movingType
+" is not supported."));
815 if (secHandling
.equals(SecReferenceHandlingEnum
.AlwaysSelect
) || (secHandling
.equals(SecReferenceHandlingEnum
.KeepOrSelect
) && sec
!= null)){
816 if (taxonNode
.getTaxon() != null){
817 taxonNode
.getTaxon().setSec(sec
);
819 }else if (secHandling
.equals(SecReferenceHandlingEnum
.AlwaysDelete
)){
820 if (taxonNode
.getTaxon() != null){
821 taxonNode
.getTaxon().setSec(null);
823 }else if (secHandling
.equals(SecReferenceHandlingEnum
.UseNewParentSec
)){
824 if (taxonNode
.getTaxon() != null && newParent
.getTaxon()!= null){
825 taxonNode
.getTaxon().setSec(newParent
.getTaxon().getSec());
829 taxonNode
= newParent
.addChildNode(taxonNode
, sortIndex
, taxonNode
.getReference(), taxonNode
.getMicroReference());
830 result
.addUpdatedObject(taxonNode
);
837 public UpdateResult
moveTaxonNodes(Set
<UUID
> taxonNodeUuids
, UUID newParentNodeUuid
, int movingType
, SecReferenceHandlingEnum secHandling
, UUID secUuid
, IProgressMonitor monitor
){
839 if (monitor
== null){
840 monitor
= DefaultProgressMonitor
.NewInstance();
842 UpdateResult result
= new UpdateResult();
843 List
<String
> taxonNodePropertyPath
= new ArrayList
<>();
844 taxonNodePropertyPath
.add("taxon.secSource.*");
845 taxonNodePropertyPath
.add("parent.taxon.secSource.*");
846 TaxonNode targetNode
= dao
.load(newParentNodeUuid
, taxonNodePropertyPath
);
847 List
<TaxonNode
> nodes
= dao
.list(taxonNodeUuids
, null, null, null, null);
848 Reference sec
= referenceDao
.load(secUuid
);
850 monitor
.beginTask("Move Taxonnodes", nodes
.size()*2);
851 monitor
.subTask("move taxon nodes");
852 for (TaxonNode node
: nodes
){
853 if (!monitor
.isCanceled()){
854 if (!nodes
.contains(node
.getParent())){
855 result
.includeResult(moveTaxonNode(node
, targetNode
, movingType
, secHandling
, sec
));
864 if (!monitor
.isCanceled()){
865 monitor
.subTask("saving and reindex");
866 dao
.saveOrUpdateAll(nodes
);
868 TransactionAspectSupport
.currentTransactionStatus().setRollbackOnly();
876 public Pager
<TaxonNodeAgentRelation
> pageTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
,
877 UUID agentUuid
, UUID rankUuid
, UUID relTypeUuid
, Integer pageSize
, Integer pageIndex
, List
<String
> propertyPaths
) {
879 List
<TaxonNodeAgentRelation
> records
= null;
881 long count
= dao
.countTaxonNodeAgentRelations(taxonUuid
, classificationUuid
, agentUuid
, rankUuid
, relTypeUuid
);
882 if(PagerUtils
.hasResultsInRange(count
, pageIndex
, pageSize
)) {
883 records
= dao
.listTaxonNodeAgentRelations(taxonUuid
, classificationUuid
,
884 agentUuid
, rankUuid
, relTypeUuid
, PagerUtils
.startFor(pageSize
, pageIndex
), PagerUtils
.limitFor(pageSize
), propertyPaths
);
887 Pager
<TaxonNodeAgentRelation
> pager
= new DefaultPagerImpl
<>(pageIndex
, count
, pageSize
, records
);
893 public UpdateResult
createNewTaxonNode(UUID parentNodeUuid
, CreateTaxonDTO taxonDto
,
894 NamedSource source
, String microref
,
895 TaxonNodeStatus status
, Map
<Language
,LanguageString
> statusNote
){
897 UpdateResult result
= new UpdateResult();
898 TaxonNode child
= null;
899 TaxonNode parent
= null;
901 TaxonName name
= null;
903 if (taxonDto
.getTaxonUuid() != null){
904 taxon
= (Taxon
) taxonService
.load(taxonDto
.getTaxonUuid());
906 throw new RuntimeException("Taxon for not found for id " + taxonDto
.getTaxonUuid());
909 if (taxonDto
.getNameUuid() != null){
910 name
= nameService
.load(taxonDto
.getNameUuid());
912 throw new RuntimeException("Taxon name not found for id " + taxonDto
.getTaxonUuid());
915 UpdateResult tmpResult
= nameService
.parseName(taxonDto
.getTaxonNameString(),
916 taxonDto
.getCode(), taxonDto
.getPreferredRank(), true);
917 result
.addUpdatedObjects(tmpResult
.getUpdatedObjects());
918 name
= (TaxonName
)tmpResult
.getCdmEntity();
920 Reference sec
= null;
921 if (taxonDto
.getSecUuid() != null ){
922 sec
= referenceDao
.load(taxonDto
.getSecUuid());
924 if (name
!= null && !name
.isPersited()){
925 for (HybridRelationship rel
: name
.getHybridChildRelations()){
926 if (!rel
.getHybridName().isPersited()) {
927 nameService
.save(rel
.getHybridName());
929 if (!rel
.getParentName().isPersited()) {
930 nameService
.save(rel
.getParentName());
934 taxon
= Taxon
.NewInstance(name
, sec
);
935 taxon
.setPublish(taxonDto
.isPublish());
938 parent
= dao
.load(parentNodeUuid
);
940 if (source
.isPersited()){
941 source
= (NamedSource
) sourceDao
.load(source
.getUuid());
943 if (source
.getCitation() != null){
944 source
.setCitation(referenceDao
.load(source
.getCitation().getUuid()));
946 if (source
.getNameUsedInSource() !=null){
947 source
.setNameUsedInSource(nameService
.load(source
.getNameUsedInSource().getUuid()));
951 child
= parent
.addChildTaxon(taxon
, source
);
952 child
.setStatus(status
);
954 if (statusNote
!= null){
955 child
.getStatusNote().putAll(statusNote
);
959 result
.addException(e
);
963 child
= dao
.save(child
);
965 result
.addUpdatedObject(parent
);
967 result
.setCdmEntity(child
);
974 public UpdateResult
addTaxonNodeAgentRelation(UUID taxonNodeUUID
, UUID agentUUID
, DefinedTerm relationshipType
){
975 UpdateResult result
= new UpdateResult();
976 TaxonNode node
= dao
.load(taxonNodeUUID
);
977 TeamOrPersonBase
<?
> agent
= (TeamOrPersonBase
<?
>) agentService
.load(agentUUID
);
978 node
.addAgentRelation(relationshipType
, agent
);
980 dao
.merge(node
, true);
981 }catch (Exception e
){
983 result
.addException(e
);
985 result
.setCdmEntity(node
);
990 @Transactional(readOnly
=false)
991 public UpdateResult
setSecundumForSubtree(SecundumForSubtreeConfigurator config
) {
992 UpdateResult result
= new UpdateResult();
993 IProgressMonitor monitor
= config
.getMonitor();
995 if (monitor
== null){
996 monitor
= DefaultProgressMonitor
.NewInstance();
998 monitor
.beginTask("Update secundum reference for subtree", 100);
999 monitor
.subTask("Check start conditions");
1001 if (config
.getSubtreeUuid() == null){
1003 result
.addException(new NullPointerException("No subtree given"));
1008 TaxonNode subTree
= load(config
.getSubtreeUuid());
1009 if (subTree
== null){
1011 result
.addException(new NullPointerException("Subtree does not exist"));
1017 Reference newSec
= null;
1018 if (config
.getNewSecundum() != null){
1019 newSec
= referenceDao
.load(config
.getNewSecundum().getUuid());
1020 if (newSec
== null){
1022 result
.addException(new NullPointerException("New secundum reference does not exist"));
1029 monitor
.subTask("Count records");
1031 boolean includeRelatedTaxa
= config
.isIncludeProParteSynonyms() || config
.isIncludeMisapplications();
1033 TreeIndex subTreeIndex
= TreeIndex
.NewInstance(subTree
.treeIndex());
1034 int count
= config
.isIncludeAcceptedTaxa() ? dao
.countSecundumForSubtreeAcceptedTaxa(subTreeIndex
, newSec
, config
.isOverwriteExisting(), config
.isIncludeSharedTaxa(), config
.isEmptySecundumDetail()):0;
1036 count
+= config
.isIncludeSynonyms() ? dao
.countSecundumForSubtreeSynonyms(subTreeIndex
, newSec
, config
.isOverwriteExisting(), config
.isIncludeSharedTaxa(), config
.isEmptySecundumDetail()) :0;
1038 count
+= includeRelatedTaxa ? dao
.countSecundumForSubtreeRelations(subTreeIndex
, newSec
, config
.isOverwriteExisting(), config
.isIncludeSharedTaxa(), config
.isEmptySecundumDetail()):0;
1040 if (monitor
.isCanceled()){
1044 SubProgressMonitor subMonitor
= SubProgressMonitor
.NewStarted(monitor
, 90, "Updating secundum for subtree", count
* 2); //*2 1 tick for update and 1 tick for commit
1045 //Reference ref = config.getNewSecundum();
1046 if (config
.isIncludeAcceptedTaxa()){
1047 monitor
.subTask("Update Accepted Taxa");
1048 Set
<CdmBase
> updatedTaxa
= dao
.setSecundumForSubtreeAcceptedTaxa(subTreeIndex
, newSec
,
1049 config
.isOverwriteExisting(), config
.isIncludeSharedTaxa(), config
.isEmptySecundumDetail(), subMonitor
);
1050 result
.addUpdatedObjects(updatedTaxa
);
1051 if (monitor
.isCanceled()){
1055 if (config
.isIncludeSynonyms()){
1056 monitor
.subTask("Update Synonyms");
1057 Set
<CdmBase
> updatedSynonyms
= dao
.setSecundumForSubtreeSynonyms(subTreeIndex
, newSec
,
1058 config
.isOverwriteExisting(), config
.isIncludeSharedTaxa() , config
.isEmptySecundumDetail(), subMonitor
);
1059 result
.addUpdatedObjects(updatedSynonyms
);
1060 if (monitor
.isCanceled()){
1064 if (includeRelatedTaxa
){
1065 monitor
.subTask("Update Related Taxa");
1066 Set
<UUID
> relationTypes
= getRelationTypesForSubtree(config
);
1067 Set
<CdmBase
> updatedRels
= dao
.setSecundumForSubtreeRelations(subTreeIndex
, newSec
,
1068 relationTypes
, config
.isOverwriteExisting(), config
.isIncludeSharedTaxa() , config
.isEmptySecundumDetail(), subMonitor
);
1069 result
.addUpdatedObjects(updatedRels
);
1070 if (monitor
.isCanceled()){
1074 } catch (Exception e
) {
1076 result
.addException(e
);
1083 @Transactional(readOnly
=false)
1084 public UpdateResult
setPublishForSubtree(PublishForSubtreeConfigurator config
){
1085 UpdateResult result
= new UpdateResult();
1086 IProgressMonitor monitor
= config
.getMonitor();
1087 if (monitor
== null){
1088 monitor
= DefaultProgressMonitor
.NewInstance();
1090 monitor
.beginTask("Update publish flag for subtree", 100);
1091 monitor
.subTask("Check start conditions");
1093 if (config
.getSubtreeUuid() == null){
1095 result
.addException(new NullPointerException("No subtree given"));
1101 TaxonNode subTree
= find(config
.getSubtreeUuid());
1102 if (subTree
== null){
1104 result
.addException(new NullPointerException("Subtree does not exist"));
1110 monitor
.subTask("Count records");
1111 boolean includeAcceptedTaxa
= config
.isIncludeAcceptedTaxa();
1112 boolean publish
= config
.isPublish();
1113 boolean includeSynonyms
= config
.isIncludeSynonyms();
1114 boolean includeSharedTaxa
= config
.isIncludeSharedTaxa();
1115 boolean includeHybrids
= config
.isIncludeHybrids();
1116 boolean includeRelatedTaxa
= config
.isIncludeProParteSynonyms() || config
.isIncludeMisapplications();
1118 TreeIndex subTreeIndex
= TreeIndex
.NewInstance(subTree
.treeIndex());
1119 int count
= includeAcceptedTaxa ? dao
.countPublishForSubtreeAcceptedTaxa(subTreeIndex
, publish
, includeSharedTaxa
, includeHybrids
):0;
1121 count
+= includeSynonyms ? dao
.countPublishForSubtreeSynonyms(subTreeIndex
, publish
, includeSharedTaxa
, includeHybrids
):0;
1123 count
+= includeRelatedTaxa ? dao
.countPublishForSubtreeRelatedTaxa(subTreeIndex
, publish
, includeSharedTaxa
, includeHybrids
):0;
1125 if (monitor
.isCanceled()){
1129 SubProgressMonitor subMonitor
= SubProgressMonitor
.NewStarted(monitor
, 90, "Updating secundum for subtree", count
);
1130 if (includeAcceptedTaxa
){
1131 monitor
.subTask("Update Accepted Taxa");
1132 @SuppressWarnings("rawtypes")
1133 Set
<TaxonBase
> updatedTaxa
= dao
.setPublishForSubtreeAcceptedTaxa(subTreeIndex
,
1134 publish
, includeSharedTaxa
, includeHybrids
, subMonitor
);
1135 result
.addUpdatedObjects(updatedTaxa
);
1136 if (monitor
.isCanceled()){
1140 if (includeSynonyms
){
1141 monitor
.subTask("Update Synonyms");
1142 @SuppressWarnings("rawtypes")
1143 Set
<TaxonBase
> updatedSynonyms
= dao
.setPublishForSubtreeSynonyms(subTreeIndex
,
1144 publish
, includeSharedTaxa
, includeHybrids
, subMonitor
);
1145 result
.addUpdatedObjects(updatedSynonyms
);
1146 if (monitor
.isCanceled()){
1150 if (includeRelatedTaxa
){
1151 monitor
.subTask("Update Related Taxa");
1152 Set
<UUID
> relationTypes
= getRelationTypesForSubtree(config
);
1153 if (config
.isIncludeMisapplications()){
1154 relationTypes
.addAll(TaxonRelationshipType
.misappliedNameUuids());
1156 if (config
.isIncludeProParteSynonyms()){
1157 relationTypes
.addAll(TaxonRelationshipType
.proParteOrPartialSynonymUuids());
1159 @SuppressWarnings("rawtypes")
1160 Set
<TaxonBase
> updatedTaxa
= dao
.setPublishForSubtreeRelatedTaxa(subTreeIndex
, publish
,
1161 relationTypes
, includeSharedTaxa
, includeHybrids
, subMonitor
);
1162 result
.addUpdatedObjects(updatedTaxa
);
1163 if (monitor
.isCanceled()){
1167 } catch (Exception e
) {
1169 result
.addException(e
);
1176 private Set
<UUID
> getRelationTypesForSubtree(ForSubtreeConfiguratorBase config
) {
1177 Set
<UUID
> relationTypes
= new HashSet
<>();
1178 if (config
.isIncludeMisapplications()){
1179 relationTypes
.addAll(TaxonRelationshipType
.misappliedNameUuids());
1181 if (config
.isIncludeProParteSynonyms()){
1182 relationTypes
.addAll(TaxonRelationshipType
.proParteOrPartialSynonymUuids());
1184 return relationTypes
;
1188 public long count(TaxonNodeFilter filter
){
1189 return nodeFilterDao
.count(filter
);
1193 public List
<UUID
> uuidList(TaxonNodeFilter filter
){
1194 return nodeFilterDao
.listUuids(filter
);
1198 public List
<Integer
> idList(TaxonNodeFilter filter
){
1199 return nodeFilterDao
.idList(filter
);
1203 public TaxonNodeDto
findCommonParentDto(Collection
<TaxonNodeDto
> nodes
) {
1204 TaxonNodeDto commonParent
= null;
1205 List
<String
> treePath
= null;
1206 for (TaxonNodeDto nodeDto
: nodes
) {
1207 if (nodeDto
== null){
1210 String nodeTreeIndex
= nodeDto
.getTreeIndex();
1211 nodeTreeIndex
= nodeTreeIndex
.replaceFirst("#", "");
1212 String
[] split
= nodeTreeIndex
.split("#");
1213 if(treePath
== null){
1214 treePath
= Arrays
.asList(split
);
1217 List
<String
> match
= new ArrayList
<>();
1218 for(int i
=0;i
<treePath
.size();i
++){
1219 if(i
>=split
.length
){
1220 //current tree index is shorter so break
1223 else if(split
[i
].equals(treePath
.get(i
))){
1225 match
.add(treePath
.get(i
));
1228 //first mismatch found
1233 if(treePath
.isEmpty()){
1234 //no common parent found for at least two nodes
1235 //-> they belong to a different classification
1240 if(treePath
!=null && !treePath
.isEmpty()) {
1242 int nodeId
= Integer
.parseInt(treePath
.get(treePath
.size()-1));
1243 TaxonNode taxonNode
= dao
.load(nodeId
, null);
1244 commonParent
= new TaxonNodeDto(taxonNode
);
1246 return commonParent
;
1250 public List
<TaxonDistributionDTO
> getTaxonDistributionDTO(List
<UUID
> nodeUuids
, List
<String
> propertyPaths
,
1251 Authentication authentication
, boolean openChildren
, TaxonNodeSortMode sortMode
){
1253 nodeUuids
= nodeUuids
.stream().distinct().collect(Collectors
.toList());
1254 List
<TaxonNode
> nodes
= new ArrayList
<>();
1256 List
<TaxonNode
> parentNodes
= load(nodeUuids
, propertyPaths
);
1257 if (sortMode
!= null){
1258 parentNodes
.sort(sortMode
.comparator());
1261 //TODO we could remove nodes which are children of other nodes in parentNodes list here as they are duplicates
1262 for (TaxonNode node
: parentNodes
){
1263 if (node
== null || nodes
.contains(node
)){
1267 List
<TaxonNode
> children
= new ArrayList
<>();
1268 children
.addAll(loadChildNodesOfTaxonNode(node
,
1269 propertyPaths
, true, true, sortMode
));
1270 for (TaxonNode child
: children
){
1271 if (!nodes
.contains(child
)){
1277 nodes
.addAll(nodes
);
1280 List
<TaxonDistributionDTO
> result
= new ArrayList
<>();
1281 boolean hasPermission
= false;
1282 for(TaxonNode node
: nodes
){
1283 if (authentication
!= null ) {
1284 hasPermission
= permissionEvaluator
.hasPermission(authentication
, node
, Operation
.UPDATE
);
1286 hasPermission
= true;
1288 if (node
.getTaxon() != null && hasPermission
){
1290 TaxonDistributionDTO dto
= new TaxonDistributionDTO(node
);
1292 }catch(Exception e
){
1293 logger
.error(e
.getMessage(), e
);
1301 public <S
extends TaxonNode
> Pager
<S
> page(Class
<S
> clazz
, List
<Restriction
<?
>> restrictions
, Integer pageSize
,
1302 Integer pageIndex
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1303 return page(clazz
, restrictions
, pageSize
, pageIndex
, orderHints
, propertyPaths
, INCLUDE_UNPUBLISHED
);
1307 public <S
extends TaxonNode
> Pager
<S
> page(Class
<S
> clazz
, List
<Restriction
<?
>> restrictions
, Integer pageSize
,
1308 Integer pageIndex
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, boolean includeUnpublished
) {
1311 long resultSize
= dao
.count(clazz
, restrictions
);
1312 if(AbstractPagerImpl
.hasResultsInRange(resultSize
, pageIndex
, pageSize
)){
1313 records
= dao
.list(clazz
, restrictions
, pageSize
, pageIndex
, orderHints
, propertyPaths
, includeUnpublished
);
1315 records
= new ArrayList
<>();
1317 Pager
<S
> pager
= new DefaultPagerImpl
<>(pageIndex
, resultSize
, pageSize
, records
);
1322 public List
<TaxonDistributionDTO
> getTaxonDistributionDTO(List
<UUID
> nodeUuids
,
1323 List
<String
> propertyPaths
, boolean openChildren
) {
1324 return getTaxonDistributionDTO(nodeUuids
, propertyPaths
, null, openChildren
, null);
1328 @Transactional(readOnly
= false)
1329 public UpdateResult
cloneSubtree(SubtreeCloneConfigurator config
) {
1330 UpdateResult result
= new UpdateResult();
1332 if (config
.getSubTreeUuids().isEmpty()){
1336 //TODO error handling
1337 Reference taxonSecundum
= config
.isReuseTaxa() || config
.isReuseTaxonSecundum() || config
.getTaxonSecundumUuid() == null ?
1338 null : referenceDao
.findByUuid(config
.getTaxonSecundumUuid());
1339 config
.setTaxonSecundum(taxonSecundum
);
1341 Reference parentChildReference
= config
.isReuseParentChildReference() || config
.getParentChildReferenceUuid() == null ?
1342 null : referenceDao
.findByUuid(config
.getParentChildReferenceUuid());
1343 config
.setParentChildReference(parentChildReference
);
1345 Reference taxonRelationshipReference
= config
.getRelationTypeToOldTaxon() == null ?
1346 null : referenceDao
.findByUuid(config
.getRelationshipReferenceUuid());
1347 config
.setRelationshipReference(taxonRelationshipReference
);
1349 Classification classificationClone
= Classification
.NewInstance(config
.getNewClassificationName());
1351 if (config
.isReuseClassificationReference()){
1352 TaxonNode anyNode
= dao
.findByUuid(config
.getSubTreeUuids().iterator().next());
1353 if (anyNode
!= null){
1354 Reference oldClassificationRef
= anyNode
.getClassification().getReference();
1355 classificationClone
.setReference(oldClassificationRef
);
1357 }else if (config
.getClassificationReferenceUuid() != null) {
1358 Reference classificationReference
= referenceDao
.findByUuid(config
.getClassificationReferenceUuid());
1359 classificationClone
.setReference(classificationReference
);
1362 //clone taxa and taxon nodes
1363 // List<Integer> childNodeIds = taxonNodeService.idList(taxonNodeFilter);
1364 // List<TaxonNode> childNodes = taxonNodeService.loadByIds(childNodeIds, null);
1365 List
<TaxonNode
> rootNodes
= this.find(config
.getSubTreeUuids());
1366 for (TaxonNode taxonNode
: rootNodes
) {
1367 cloneTaxonRecursive(taxonNode
, classificationClone
.getRootNode(), config
);
1369 classificationDao
.saveOrUpdate(classificationClone
);
1370 result
.setCdmEntity(classificationClone
);
1374 private void cloneTaxonRecursive(TaxonNode originalParentNode
, TaxonNode parentNodeClone
,
1375 SubtreeCloneConfigurator config
){
1377 Taxon originalTaxon
= CdmBase
.deproxy(originalParentNode
.getTaxon());
1378 TaxonNode childNodeClone
;
1379 if (originalTaxon
!= null){
1380 String microReference
= null;
1381 if (config
.isReuseTaxa()){
1382 childNodeClone
= parentNodeClone
.addChildTaxon(originalTaxon
, config
.getParentChildReference(), microReference
);
1384 Taxon cloneTaxon
= originalTaxon
.clone(config
.isIncludeSynonymsIncludingManAndProParte(),
1385 config
.isIncludeTaxonRelationshipsExcludingManAndProParte(),
1386 config
.isIncludeDescriptiveData(), config
.isIncludeMedia());
1389 if (!config
.isReuseNames()){
1390 cloneTaxon
.setName(cloneTaxon
.getName().clone());
1391 //TODO needs further handling for name relationships etc., see #9349
1392 cloneTaxon
.getSynonyms().forEach(syn
->
1393 syn
.setName(syn
.getName() == null ?
null : syn
.getName().clone()));
1396 if (!config
.isReuseTaxonSecundum()){
1397 cloneTaxon
.setSec(config
.getTaxonSecundum());
1400 //add relation between taxa
1401 if (config
.getRelationTypeToOldTaxon() != null){
1402 TaxonRelationship rel
= cloneTaxon
.addTaxonRelation(originalParentNode
.getTaxon(), config
.getRelationTypeToOldTaxon(),
1403 config
.getRelationshipReference(), microReference
);
1404 rel
.setDoubtful(config
.isRelationDoubtful());
1406 childNodeClone
= parentNodeClone
.addChildTaxon(cloneTaxon
, config
.getParentChildReference(), microReference
);
1409 //probably necessary as taxon nodes do not cascade
1410 dao
.saveOrUpdate(childNodeClone
);
1413 childNodeClone
= parentNodeClone
;
1416 if (config
.isDoRecursive()){
1417 List
<TaxonNode
> originalChildNodes
= originalParentNode
.getChildNodes();
1419 for (TaxonNode originalChildNode
: originalChildNodes
) {
1420 cloneTaxonRecursive(originalChildNode
, childNodeClone
, config
);
1426 public HomotypicGroupDto
getHomotypicGroupDto(UUID homotypicGroupUuid
, UUID nodeUuid
) {
1428 HomotypicalGroup group
= homotypicalGroupDao
.load(homotypicGroupUuid
);
1432 return new HomotypicGroupDto(group
, nodeUuid
);
1436 public TaxonNodeDto
getTaxonNodeDto(UUID nodeUuid
) {
1437 return dao
.getTaxonNodeDto(nodeUuid
);
1441 public List
<TaxonNodeDto
> getTaxonNodeDtos(List
<UUID
> nodeUuids
) {
1442 return dao
.getTaxonNodeDtos(nodeUuids
);
1446 public List
<TaxonNodeDto
> getTaxonNodeDtosFromTaxon(UUID taxonUuid
, String subTreeIndex
) {
1447 return dao
.getTaxonNodeDtosFromTaxon(taxonUuid
, subTreeIndex
);