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
.HashSet
;
18 import java
.util
.Iterator
;
19 import java
.util
.List
;
20 import java
.util
.Map
.Entry
;
22 import java
.util
.UUID
;
24 import org
.apache
.log4j
.Logger
;
25 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
26 import org
.springframework
.security
.core
.Authentication
;
27 import org
.springframework
.stereotype
.Service
;
28 import org
.springframework
.transaction
.annotation
.Transactional
;
29 import org
.springframework
.transaction
.interceptor
.TransactionAspectSupport
;
31 import eu
.etaxonomy
.cdm
.api
.service
.UpdateResult
.Status
;
32 import eu
.etaxonomy
.cdm
.api
.service
.config
.NodeDeletionConfigurator
.ChildHandling
;
33 import eu
.etaxonomy
.cdm
.api
.service
.config
.PublishForSubtreeConfigurator
;
34 import eu
.etaxonomy
.cdm
.api
.service
.config
.SecundumForSubtreeConfigurator
;
35 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
36 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonNodeDeletionConfigurator
;
37 import eu
.etaxonomy
.cdm
.api
.service
.dto
.CdmEntityIdentifier
;
38 import eu
.etaxonomy
.cdm
.api
.service
.dto
.TaxonDistributionDTO
;
39 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
40 import eu
.etaxonomy
.cdm
.api
.service
.pager
.PagerUtils
;
41 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.AbstractPagerImpl
;
42 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
43 import eu
.etaxonomy
.cdm
.common
.monitor
.DefaultProgressMonitor
;
44 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
45 import eu
.etaxonomy
.cdm
.filter
.TaxonNodeFilter
;
46 import eu
.etaxonomy
.cdm
.hibernate
.HHH_9751_Util
;
47 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
48 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
49 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
50 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
51 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
52 import eu
.etaxonomy
.cdm
.model
.common
.TreeIndex
;
53 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
54 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementSource
;
55 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveDataSet
;
56 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
57 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
58 import eu
.etaxonomy
.cdm
.model
.name
.HybridRelationship
;
59 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
60 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
61 import eu
.etaxonomy
.cdm
.model
.permission
.Operation
;
62 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
63 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
64 import eu
.etaxonomy
.cdm
.model
.taxon
.HomotypicGroupTaxonComparator
;
65 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonTreeNode
;
66 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
67 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymType
;
68 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
69 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
70 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
71 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNodeAgentRelation
;
72 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
73 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
74 import eu
.etaxonomy
.cdm
.model
.term
.DefinedTerm
;
75 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.Restriction
;
76 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.IBeanInitializer
;
77 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonNodeDao
;
78 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonNodeFilterDao
;
79 import eu
.etaxonomy
.cdm
.persistence
.dto
.TaxonNodeDto
;
80 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
81 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.permission
.ICdmPermissionEvaluator
;
82 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
89 @Transactional(readOnly
= true)
90 public class TaxonNodeServiceImpl
91 extends AnnotatableServiceBase
<TaxonNode
, ITaxonNodeDao
>
92 implements ITaxonNodeService
{
93 private static final Logger logger
= Logger
.getLogger(TaxonNodeServiceImpl
.class);
96 private IBeanInitializer defaultBeanInitializer
;
99 private ITaxonService taxonService
;
102 private IReferenceService referenceService
;
105 private IDescriptiveDataSetService dataSetService
;
108 private IAgentService agentService
;
111 private INameService nameService
;
114 private IReferenceService refService
;
117 private ITaxonNodeFilterDao nodeFilterDao
;
120 IProgressMonitorService progressMonitorService
;
123 private ICdmPermissionEvaluator permissionEvaluator
;
126 public List
<TaxonNode
> loadChildNodesOfTaxonNode(TaxonNode taxonNode
,
127 List
<String
> propertyPaths
, boolean recursive
, boolean includeUnpublished
,
128 NodeSortMode sortMode
) {
130 getSession().refresh(taxonNode
);
131 List
<TaxonNode
> childNodes
;
132 if (recursive
== true){
133 childNodes
= dao
.listChildrenOf(taxonNode
, null, null, recursive
, includeUnpublished
, null);
134 }else if (includeUnpublished
){
135 childNodes
= new ArrayList
<>(taxonNode
.getChildNodes());
137 childNodes
= new ArrayList
<>();
138 for (TaxonNode node
:taxonNode
.getChildNodes()){
139 if (node
.getTaxon().isPublish()){
140 childNodes
.add(node
);
145 HHH_9751_Util
.removeAllNull(childNodes
);
147 if (sortMode
!= null){
148 Comparator
<TaxonNode
> comparator
= sortMode
.newComparator();
149 Collections
.sort(childNodes
, comparator
);
151 defaultBeanInitializer
.initializeAll(childNodes
, propertyPaths
);
156 public List
<TaxonNode
> listChildrenOf(TaxonNode node
, Integer pageSize
, Integer pageIndex
,
157 boolean recursive
, boolean includeUnpublished
, List
<String
> propertyPaths
){
158 return dao
.listChildrenOf(node
, pageSize
, pageIndex
, recursive
, includeUnpublished
, propertyPaths
);
165 public TaxonNodeDto
getParentUuidAndTitleCache(ITaxonTreeNode child
) {
166 UUID uuid
= child
.getUuid();
167 int id
= child
.getId();
168 TaxonNodeDto uuidAndTitleCache
= new TaxonNodeDto(uuid
, id
, null);
169 return getParentUuidAndTitleCache(uuidAndTitleCache
);
176 public TaxonNodeDto
getParentUuidAndTitleCache(TaxonNodeDto child
) {
177 return dao
.getParentUuidAndTitleCache(child
);
181 public List
<TaxonNodeDto
> listChildNodesAsTaxonNodeDto(TaxonNodeDto parent
) {
182 return dao
.listChildNodesAsTaxonNodeDto(parent
);
188 public List
<UuidAndTitleCache
<TaxonNode
>> getUuidAndTitleCache(Integer limit
, String pattern
, UUID classificationUuid
) {
189 return dao
.getUuidAndTitleCache(limit
, pattern
, classificationUuid
);
193 public List
<TaxonNodeDto
> listChildNodesAsTaxonNodeDto(ITaxonTreeNode parent
) {
194 TaxonNodeDto uuidAndTitleCache
= new TaxonNodeDto(parent
);
195 return listChildNodesAsTaxonNodeDto(uuidAndTitleCache
);
200 public TaxonNodeDto
taxonNodeDtoParentRank(Classification classification
, Rank rank
, TaxonName name
) {
201 return dao
.getParentTaxonNodeDtoForRank(classification
, rank
, name
);
205 public Pager
<TaxonNodeDto
> pageChildNodesDTOs(UUID taxonNodeUuid
, boolean recursive
, boolean includeUnpublished
,
206 boolean doSynonyms
, NodeSortMode sortMode
,
207 Integer pageSize
, Integer pageIndex
) {
209 TaxonNode parentNode
= dao
.load(taxonNodeUuid
);
211 List
<CdmBase
> allRecords
= new ArrayList
<>();
214 List
<TaxonNode
> childNodes
= loadChildNodesOfTaxonNode(parentNode
, null, recursive
, includeUnpublished
, sortMode
);
215 allRecords
.addAll(childNodes
);
217 //add synonyms if pager is not yet full synonyms
219 List
<Synonym
> synList
= new ArrayList
<>(parentNode
.getTaxon().getSynonyms());
220 Collections
.sort(synList
, new HomotypicGroupTaxonComparator(null));
223 allRecords
.addAll(synList
);
226 List
<TaxonNodeDto
> dtos
= new ArrayList
<>(pageSize
==null?
25:pageSize
);
227 long totalCount
= Long
.valueOf(allRecords
.size());
229 TaxonName parentName
= null;
231 for(CdmBase record
: PagerUtils
.pageList(allRecords
, pageIndex
, pageSize
)) {
232 if (record
.isInstanceOf(TaxonNode
.class)){
233 dtos
.add(new TaxonNodeDto(CdmBase
.deproxy(record
, TaxonNode
.class)));
234 }else if (record
.isInstanceOf(Synonym
.class)){
235 Synonym synonym
= CdmBase
.deproxy(record
, Synonym
.class);
236 parentName
= parentName
== null? parentNode
.getTaxon().getName(): parentName
;
237 boolean isHomotypic
= synonym
.getName().isHomotypic(parentName
);
238 dtos
.add(new TaxonNodeDto(synonym
, isHomotypic
));
241 return new DefaultPagerImpl
<>(pageIndex
, totalCount
, pageSize
, dtos
);
245 public TaxonNodeDto
parentDto(UUID taxonNodeUuid
) {
246 TaxonNode taxonNode
= dao
.load(taxonNodeUuid
);
247 if(taxonNode
.getParent() != null) {
248 return new TaxonNodeDto(taxonNode
.getParent());
254 public TaxonNodeDto
dto(UUID taxonNodeUuid
) {
255 TaxonNode taxonNode
= dao
.load(taxonNodeUuid
);
256 if(taxonNode
.getParent() != null) {
257 return new TaxonNodeDto(taxonNode
);
264 protected void setDao(ITaxonNodeDao dao
) {
269 @Transactional(readOnly
= false)
270 public DeleteResult
makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode
, TaxonNode newAcceptedTaxonNode
,
271 SynonymType synonymType
, Reference citation
, String citationMicroReference
, boolean setNameInSource
) {
273 // TODO at the moment this method only moves synonym-, concept relations and descriptions to the new accepted taxon
274 // in a future version we also want to move cdm data like annotations, marker, so., but we will need a policy for that
275 if (oldTaxonNode
== null || newAcceptedTaxonNode
== null || oldTaxonNode
.getTaxon().getName() == null){
276 throw new IllegalArgumentException("A mandatory parameter was null.");
279 if(oldTaxonNode
.equals(newAcceptedTaxonNode
)){
280 throw new IllegalArgumentException("Taxon can not be made synonym of its own.");
283 Classification classification
= oldTaxonNode
.getClassification();
284 Taxon oldTaxon
= HibernateProxyHelper
.deproxy(oldTaxonNode
.getTaxon());
285 Taxon newAcceptedTaxon
= (Taxon
)this.taxonService
.find(newAcceptedTaxonNode
.getTaxon().getUuid());
286 newAcceptedTaxon
= HibernateProxyHelper
.deproxy(newAcceptedTaxon
);
287 // Move oldTaxon to newTaxon
288 //TaxonName synonymName = oldTaxon.getName();
289 TaxonName newSynonymName
= CdmBase
.deproxy(oldTaxon
.getName());
290 HomotypicalGroup group
= CdmBase
.deproxy(newSynonymName
.getHomotypicalGroup());
291 if (synonymType
== null){
292 if (newSynonymName
.isHomotypic(newAcceptedTaxon
.getName())){
293 synonymType
= SynonymType
.HOMOTYPIC_SYNONYM_OF();
295 synonymType
= SynonymType
.HETEROTYPIC_SYNONYM_OF();
299 //set homotypic group
300 TaxonName newAcceptedTaxonName
= HibernateProxyHelper
.deproxy(newAcceptedTaxon
.getName(), TaxonName
.class);
301 newAcceptedTaxon
.setName(newAcceptedTaxonName
);
302 // Move Synonym Relations to new Taxon
303 newAcceptedTaxon
.addSynonymName(newSynonymName
, citation
, citationMicroReference
, synonymType
);
304 // Move Synonyms to new Taxon
305 // From ticket 3163 we can move taxon with accepted name having homotypic synonyms
306 List
<Synonym
> synonymsInHomotypicalGroup
= null;
308 //the synonyms of the homotypical group of the old taxon
309 if (synonymType
.equals(SynonymType
.HOMOTYPIC_SYNONYM_OF())){
310 synonymsInHomotypicalGroup
= oldTaxon
.getSynonymsInGroup(group
);
313 Set
<Synonym
> syns
= new HashSet
<>(oldTaxon
.getSynonyms());
314 for(Synonym synonym
: syns
){
316 if(synonym
.getHomotypicGroup()!= null
317 && synonym
.getHomotypicGroup().equals(newAcceptedTaxonName
.getHomotypicalGroup())) {
318 srt
= SynonymType
.HOMOTYPIC_SYNONYM_OF();
319 } else if(synonym
.getType() != null && synonym
.getType().equals(SynonymType
.HOMOTYPIC_SYNONYM_OF())) {
320 if (synonymType
.equals(SynonymType
.HOMOTYPIC_SYNONYM_OF())){
321 srt
= SynonymType
.HOMOTYPIC_SYNONYM_OF();
323 srt
= SynonymType
.HETEROTYPIC_SYNONYM_OF();
326 if (synonymsInHomotypicalGroup
!= null && synonymsInHomotypicalGroup
.contains(synonym
)){
327 srt
= SynonymType
.HOMOTYPIC_SYNONYM_OF();
329 srt
= synonym
.getType();
334 newAcceptedTaxon
.addSynonym(synonym
, srt
);
337 /*if (synonymsInHomotypicalGroup.contains(synRelation.getSynonym()) && srt.equals(SynonymType.HETEROTYPIC_SYNONYM_OF())){
338 homotypicalGroupAcceptedTaxon.addTypifiedName(synRelation.getSynonym().getName());
345 if(oldTaxonNode
.getChildNodes() != null && oldTaxonNode
.getChildNodes().size() != 0){
346 List
<TaxonNode
> childNodes
= new ArrayList
<>();
347 for (TaxonNode childNode
: oldTaxonNode
.getChildNodes()){
348 childNodes
.add(childNode
);
350 for(TaxonNode childNode
:childNodes
){
351 newAcceptedTaxonNode
.addChildNode(childNode
, childNode
.getReference(), childNode
.getMicroReference()); // childNode.getSynonymToBeUsed()
355 //Move Taxon RelationShips to new Taxon
356 for(TaxonRelationship taxonRelationship
: oldTaxon
.getTaxonRelations()){
357 Taxon fromTaxon
= HibernateProxyHelper
.deproxy(taxonRelationship
.getFromTaxon());
358 Taxon toTaxon
= HibernateProxyHelper
.deproxy(taxonRelationship
.getToTaxon());
359 if (fromTaxon
== oldTaxon
){
360 newAcceptedTaxon
.addTaxonRelation(taxonRelationship
.getToTaxon(), taxonRelationship
.getType(),
361 taxonRelationship
.getCitation(), taxonRelationship
.getCitationMicroReference());
363 }else if(toTaxon
== oldTaxon
){
364 fromTaxon
.addTaxonRelation(newAcceptedTaxon
, taxonRelationship
.getType(),
365 taxonRelationship
.getCitation(), taxonRelationship
.getCitationMicroReference());
366 taxonService
.saveOrUpdate(fromTaxon
);
369 logger
.warn("Taxon is not part of its own Taxonrelationship");
371 // Remove old relationships
373 fromTaxon
.removeTaxonRelation(taxonRelationship
);
374 toTaxon
.removeTaxonRelation(taxonRelationship
);
375 taxonRelationship
.setToTaxon(null);
376 taxonRelationship
.setFromTaxon(null);
379 //Move descriptions to new taxon
380 List
<TaxonDescription
> descriptions
= new ArrayList
<TaxonDescription
>( oldTaxon
.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())
381 for(TaxonDescription description
: descriptions
){
382 String message
= "Description copied from former accepted taxon: %s (Old title: %s)";
383 message
= String
.format(message
, oldTaxon
.getTitleCache(), description
.getTitleCache());
384 description
.setTitleCache(message
, true);
385 if (setNameInSource
) {
386 for (DescriptionElementBase element
: description
.getElements()){
387 for (DescriptionElementSource source
: element
.getSources()){
388 if (source
.getNameUsedInSource() == null){
389 source
.setNameUsedInSource(newSynonymName
);
394 //oldTaxon.removeDescription(description, false);
395 newAcceptedTaxon
.addDescription(description
);
397 oldTaxon
.clearDescriptions();
399 taxonService
.saveOrUpdate(newAcceptedTaxon
);
401 taxonService
.saveOrUpdate(oldTaxon
);
402 taxonService
.getSession().flush();
404 TaxonDeletionConfigurator conf
= new TaxonDeletionConfigurator();
405 conf
.setDeleteSynonymsIfPossible(false);
406 conf
.setDeleteNameIfPossible(false);
407 DeleteResult result
= taxonService
.isDeletable(oldTaxon
.getUuid(), conf
);
411 result
= taxonService
.deleteTaxon(oldTaxon
.getUuid(), conf
, classification
.getUuid());
414 result
.setStatus(Status
.OK
);
415 TaxonNodeDeletionConfigurator config
= new TaxonNodeDeletionConfigurator();
416 config
.setDeleteElement(false);
417 conf
.setTaxonNodeConfig(config
);
418 result
.includeResult(deleteTaxonNode(oldTaxonNode
, conf
));
421 result
.addUpdatedObject(newAcceptedTaxon
);
424 //oldTaxonNode.delete();
428 @Transactional(readOnly
= false)
429 public UpdateResult
makeTaxonNodeSynonymsOfAnotherTaxonNode( Set
<UUID
> oldTaxonNodeUuids
,
430 UUID newAcceptedTaxonNodeUUIDs
,
431 SynonymType synonymType
,
433 String citationMicroReference
,
434 boolean setNameInSource
) {
435 UpdateResult result
= new UpdateResult();
436 for (UUID nodeUuid
: oldTaxonNodeUuids
) {
437 result
.includeResult(makeTaxonNodeASynonymOfAnotherTaxonNode(nodeUuid
, newAcceptedTaxonNodeUUIDs
, synonymType
, citation
, citationMicroReference
, setNameInSource
));
443 @Transactional(readOnly
= false)
444 public UpdateResult
makeTaxonNodeASynonymOfAnotherTaxonNode(UUID oldTaxonNodeUuid
,
445 UUID newAcceptedTaxonNodeUUID
,
446 SynonymType synonymType
,
448 String citationMicroReference
,
449 boolean setNameInSource
) {
451 TaxonNode oldTaxonNode
= dao
.load(oldTaxonNodeUuid
);
452 TaxonNode oldTaxonParentNode
= oldTaxonNode
.getParent();
453 TaxonNode newTaxonNode
= dao
.load(newAcceptedTaxonNodeUUID
);
455 UpdateResult result
= makeTaxonNodeASynonymOfAnotherTaxonNode(oldTaxonNode
,
459 citationMicroReference
, setNameInSource
);
460 result
.addUpdatedCdmId(new CdmEntityIdentifier(oldTaxonParentNode
.getId(), TaxonNode
.class));
461 result
.addUpdatedCdmId(new CdmEntityIdentifier(newTaxonNode
.getId(), TaxonNode
.class));
462 result
.setCdmEntity(oldTaxonParentNode
);
467 @Transactional(readOnly
= false)
468 public DeleteResult
deleteTaxonNodes(List
<TaxonNode
> list
, TaxonDeletionConfigurator config
) {
471 config
= new TaxonDeletionConfigurator();
473 DeleteResult result
= new DeleteResult();
474 Classification classification
= null;
475 List
<TaxonNode
> taxonNodes
= new ArrayList
<>(list
);
477 for (TaxonNode treeNode
:taxonNodes
){
478 if (treeNode
!= null){
481 taxonNode
= HibernateProxyHelper
.deproxy(treeNode
, TaxonNode
.class);
482 TaxonNode parent
= taxonNode
.getParent();
483 //check whether the node has children or the children are already deleted
484 if(taxonNode
.hasChildNodes()) {
485 List
<TaxonNode
> children
= new ArrayList
<TaxonNode
> ();
486 List
<TaxonNode
> childNodesList
= taxonNode
.getChildNodes();
487 children
.addAll(childNodesList
);
488 //To avoid NPE when child is also in list of taxonNodes, remove it from the list
489 Iterator
<TaxonNode
> it
= taxonNodes
.iterator();
490 for (TaxonNode child
: children
) {
491 while (it
.hasNext()) {
492 if (it
.next().equals(child
)) {
497 int compare
= config
.getTaxonNodeConfig().getChildHandling().compareTo(ChildHandling
.DELETE
);
498 boolean childHandling
= (compare
== 0)?
true: false;
500 boolean changeDeleteTaxon
= false;
501 if (!config
.getTaxonNodeConfig().isDeleteTaxon()){
502 config
.getTaxonNodeConfig().setDeleteTaxon(true);
503 changeDeleteTaxon
= true;
505 DeleteResult resultNodes
= deleteTaxonNodes(children
, config
);
506 if (!resultNodes
.isOk()){
507 result
.addExceptions(resultNodes
.getExceptions());
508 result
.setStatus(resultNodes
.getStatus());
510 if (changeDeleteTaxon
){
511 config
.getTaxonNodeConfig().setDeleteTaxon(false);
515 //move the children to the parent
517 for (TaxonNode child
: childNodesList
){
518 parent
.addChildNode(child
, child
.getReference(), child
.getMicroReference());
524 classification
= taxonNode
.getClassification();
526 if (classification
.getRootNode().equals(taxonNode
)){
527 classification
.removeRootNode();
528 classification
= null;
529 }else if (classification
.getChildNodes().contains(taxonNode
)){
530 Taxon taxon
= taxonNode
.getTaxon();
531 classification
.deleteChildNode(taxonNode
);
536 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
537 taxonService
.saveOrUpdate(taxon
);
538 saveOrUpdate(taxonNode
);
540 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
541 configNew
.setClassificationUuid(classification
.getUuid());
542 DeleteResult resultTaxon
= taxonService
.deleteTaxon(taxon
.getUuid(), configNew
, classification
.getUuid());
543 if (!resultTaxon
.isOk()){
544 result
.addExceptions(resultTaxon
.getExceptions());
545 result
.setStatus(resultTaxon
.getStatus());
550 classification
= null;
553 //classification = null;
554 Taxon taxon
= taxonNode
.getTaxon();
555 taxon
= HibernateProxyHelper
.deproxy(taxon
, Taxon
.class);
557 taxon
.removeTaxonNode(taxonNode
);
558 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
559 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
560 saveOrUpdate(taxonNode
);
561 taxonService
.saveOrUpdate(taxon
);
562 DeleteResult resultTaxon
= taxonService
.deleteTaxon(taxon
.getUuid(), configNew
, classification
.getUuid());
564 if (!resultTaxon
.isOk()){
565 result
.addExceptions(resultTaxon
.getExceptions());
566 result
.setStatus(resultTaxon
.getStatus());
573 result
.addUpdatedObject(parent
);
574 if(result
.getCdmEntity() == null){
575 result
.setCdmEntity(taxonNode
);
577 UUID uuid
= dao
.delete(taxonNode
);
578 logger
.debug("Deleted node " +uuid
.toString());
582 /*if (classification != null){
583 result.addUpdatedObject(classification);
584 DeleteResult resultClassification = classService.delete(classification);
585 if (!resultClassification.isOk()){
586 result.addExceptions(resultClassification.getExceptions());
587 result.setStatus(resultClassification.getStatus());
595 @Transactional(readOnly
= false)
596 public DeleteResult
deleteTaxonNodes(Collection
<UUID
> nodeUuids
, TaxonDeletionConfigurator config
) {
597 List
<TaxonNode
> nodes
= new ArrayList
<>();
598 for(UUID nodeUuid
: nodeUuids
) {
599 nodes
.add(dao
.load(nodeUuid
));
601 return deleteTaxonNodes(nodes
, config
);
606 @Transactional(readOnly
= false)
607 public DeleteResult
deleteTaxonNode(UUID nodeUUID
, TaxonDeletionConfigurator config
) {
609 TaxonNode node
= HibernateProxyHelper
.deproxy(dao
.load(nodeUUID
), TaxonNode
.class);
610 return deleteTaxonNode(node
, config
);
614 @Transactional(readOnly
= false)
615 public DeleteResult
deleteTaxonNode(TaxonNode node
, TaxonDeletionConfigurator config
) {
616 DeleteResult result
= new DeleteResult();
619 result
.addException(new Exception("The TaxonNode was already deleted."));
624 taxon
= HibernateProxyHelper
.deproxy(node
.getTaxon());
625 }catch(NullPointerException e
){
627 result
.addException(new Exception("The Taxon was already deleted."));
632 TaxonNode parent
= HibernateProxyHelper
.deproxy(node
.getParent(), TaxonNode
.class);
634 config
= new TaxonDeletionConfigurator();
638 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.MOVE_TO_PARENT
)){
639 Object
[] children
= node
.getChildNodes().toArray();
641 for (Object child
: children
){
642 childNode
= (TaxonNode
) child
;
643 parent
.addChildNode(childNode
, childNode
.getReference(), childNode
.getMicroReference());
647 result
.includeResult(deleteTaxonNodes(node
.getChildNodes(), config
));
650 //remove node from DescriptiveDataSet
651 commonService
.getReferencingObjects(node
).stream()
652 .filter(obj
->obj
instanceof DescriptiveDataSet
)
654 ((DescriptiveDataSet
)dataSet
).removeTaxonSubtree(node
);
655 dataSetService
.saveOrUpdate((DescriptiveDataSet
) dataSet
);
659 if (config
.getTaxonNodeConfig().isDeleteTaxon() && (config
.isDeleteInAllClassifications() || taxon
.getTaxonNodes().size() == 1)){
660 result
= taxonService
.deleteTaxon(taxon
.getUuid(), config
, node
.getClassification().getUuid());
661 result
.addUpdatedObject(parent
);
666 result
.addUpdatedObject(taxon
);
669 result
.setCdmEntity(node
);
670 boolean success
= true;
672 success
= taxon
.removeTaxonNode(node
);
673 taxonService
.saveOrUpdate(taxon
);
675 dao
.saveOrUpdate(parent
);
677 result
.addUpdatedObject(parent
);
680 result
.setStatus(Status
.OK
);
682 parent
= HibernateProxyHelper
.deproxy(parent
, TaxonNode
.class);
683 int index
= parent
.getChildNodes().indexOf(node
);
685 parent
.removeChild(index
);
688 if (!dao
.delete(node
, config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
)).equals(null)){
689 result
.getUpdatedObjects().remove(node
);
690 result
.addDeletedObject(node
);
697 if (dao
.findByUuid(node
.getUuid()) != null){
699 result
.addException(new Exception("The node can not be removed from the taxon."));
707 public List
<TaxonNode
> listAllNodesForClassification(Classification classification
, Integer start
, Integer end
) {
708 return dao
.getTaxonOfAcceptedTaxaByClassification(classification
, start
, end
);
712 public int countAllNodesForClassification(Classification classification
) {
713 return dao
.countTaxonOfAcceptedTaxaByClassification(classification
);
718 public UpdateResult
moveTaxonNode(UUID taxonNodeUuid
, UUID targetNodeUuid
, int movingType
){
719 TaxonNode taxonNode
= HibernateProxyHelper
.deproxy(dao
.load(taxonNodeUuid
));
720 TaxonNode targetNode
= HibernateProxyHelper
.deproxy(dao
.load(targetNodeUuid
));
721 UpdateResult result
= moveTaxonNode(taxonNode
, targetNode
, movingType
);
727 public UpdateResult
moveTaxonNode(TaxonNode taxonNode
, TaxonNode newParent
, int movingType
){
728 UpdateResult result
= new UpdateResult();
730 TaxonNode parentParent
= HibernateProxyHelper
.deproxy(newParent
.getParent());
731 TaxonNode oldParent
= HibernateProxyHelper
.deproxy(taxonNode
.getParent());
732 Integer sortIndex
= -1;
733 if (movingType
== 0){
735 }else if (movingType
== 1){
736 sortIndex
= newParent
.getSortIndex();
737 newParent
= parentParent
;
738 } else if (movingType
== 2){
739 sortIndex
= newParent
.getSortIndex() +1;
740 newParent
= parentParent
;
743 result
.addException(new Exception("The moving type "+ movingType
+" is not supported."));
746 taxonNode
= newParent
.addChildNode(taxonNode
, sortIndex
, taxonNode
.getReference(), taxonNode
.getMicroReference());
747 result
.addUpdatedObject(taxonNode
);
756 public UpdateResult
moveTaxonNodes(Set
<UUID
> taxonNodeUuids
, UUID newParentNodeUuid
, int movingType
, IProgressMonitor monitor
){
758 if (monitor
== null){
759 monitor
= DefaultProgressMonitor
.NewInstance();
761 UpdateResult result
= new UpdateResult();
763 TaxonNode targetNode
= dao
.load(newParentNodeUuid
);
764 List
<TaxonNode
> nodes
= dao
.list(taxonNodeUuids
, null, null, null, null);
766 monitor
.beginTask("Move Taxonnodes", nodes
.size()*2);
767 monitor
.subTask("move taxon nodes");
768 for (TaxonNode node
: nodes
){
769 if (!monitor
.isCanceled()){
770 if (!nodes
.contains(node
.getParent())){
771 result
.includeResult(moveTaxonNode(node
,targetNode
, movingType
));
780 if (!monitor
.isCanceled()){
781 monitor
.subTask("saving and reindex");
782 dao
.saveOrUpdateAll(nodes
);
784 TransactionAspectSupport
.currentTransactionStatus().setRollbackOnly();
792 public Pager
<TaxonNodeAgentRelation
> pageTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
,
793 UUID agentUuid
, UUID rankUuid
, UUID relTypeUuid
, Integer pageSize
, Integer pageIndex
, List
<String
> propertyPaths
) {
796 List
<TaxonNodeAgentRelation
> records
= null;
798 long count
= dao
.countTaxonNodeAgentRelations(taxonUuid
, classificationUuid
, agentUuid
, rankUuid
, relTypeUuid
);
799 if(PagerUtils
.hasResultsInRange(count
, pageIndex
, pageSize
)) {
800 records
= dao
.listTaxonNodeAgentRelations(taxonUuid
, classificationUuid
,
801 agentUuid
, rankUuid
, relTypeUuid
, PagerUtils
.startFor(pageSize
, pageIndex
), PagerUtils
.limitFor(pageSize
), propertyPaths
);
804 Pager
<TaxonNodeAgentRelation
> pager
= new DefaultPagerImpl
<>(pageIndex
, count
, pageSize
, records
);
810 public UpdateResult
createNewTaxonNode(UUID parentNodeUuid
, Taxon newTaxon
, Reference ref
, String microref
){
811 UpdateResult result
= new UpdateResult();
812 if (newTaxon
.getName().getId() != 0){
813 TaxonName name
= nameService
.load(newTaxon
.getName().getUuid());
814 newTaxon
.setName(name
);
816 for (HybridRelationship rel
: newTaxon
.getName().getHybridChildRelations()){
817 if (!rel
.getHybridName().isPersited()) {
818 nameService
.save(rel
.getHybridName());
820 if (!rel
.getParentName().isPersited()) {
821 nameService
.save(rel
.getParentName());
825 UUID taxonUUID
= taxonService
.saveOrUpdate(newTaxon
);
826 newTaxon
= (Taxon
) taxonService
.load(taxonUUID
);
828 TaxonNode parent
= dao
.load(parentNodeUuid
);
829 TaxonNode child
= null;
831 child
= parent
.addChildTaxon(newTaxon
,ref
, microref
);
833 result
.addException(e
);
837 child
= dao
.save(child
);
839 result
.addUpdatedObject(parent
);
841 result
.setCdmEntity(child
);
850 public UpdateResult
saveNewTaxonNode(TaxonNode newTaxonNode
){
851 UpdateResult result
= new UpdateResult();
852 UUID parentUuid
= newTaxonNode
.getParent().getUuid();
855 if (newTaxonNode
.getTaxon().getId() != 0){
856 taxon
= (Taxon
)taxonService
.load(newTaxonNode
.getTaxon().getUuid());
857 //newTaxonNode.setTaxon(taxon);
858 }else if (newTaxonNode
.getTaxon().getName().getId() != 0){
859 TaxonName name
= nameService
.load(newTaxonNode
.getTaxon().getName().getUuid());
860 taxon
= newTaxonNode
.getTaxon();
863 for (HybridRelationship rel
: newTaxonNode
.getTaxon().getName().getHybridChildRelations()){
864 if (!rel
.getHybridName().isPersited()) {
865 nameService
.save(rel
.getHybridName());
867 if (!rel
.getParentName().isPersited()) {
868 nameService
.save(rel
.getParentName());
873 taxon
= newTaxonNode
.getTaxon();
875 taxon
.removeTaxonNode(newTaxonNode
);
877 if (taxon
.getSec() != null && taxon
.getSec().isPersited()){
878 Reference sec
= referenceService
.load(taxon
.getSec().getUuid());
881 if (taxon
.getId() == 0){
882 UUID taxonUUID
= taxonService
.saveOrUpdate(taxon
);
883 taxon
= (Taxon
) taxonService
.load(taxonUUID
);
888 TaxonNode parent
= dao
.load(parentUuid
);
889 TaxonNode child
= null;
891 child
= parent
.addChildTaxon(taxon
, newTaxonNode
.getReference(), newTaxonNode
.getMicroReference());
894 result
.addException(e
);
899 child
.setUnplaced(newTaxonNode
.isUnplaced());
900 child
.setExcluded(newTaxonNode
.isExcluded());
901 child
.setDoubtful(newTaxonNode
.isDoubtful());
902 for (TaxonNodeAgentRelation agentRel
:newTaxonNode
.getAgentRelations()){
903 child
.addAgentRelation(agentRel
.getType(), agentRel
.getAgent());
905 for (Entry
<Language
, LanguageString
> entry
: newTaxonNode
.getExcludedNote().entrySet()){
906 child
.putExcludedNote(entry
.getKey(), entry
.getValue().getText());
910 dao
.saveOrUpdate(child
);
912 result
.addUpdatedObject(child
.getParent());
914 result
.setCdmEntity(child
);
925 public UpdateResult
createNewTaxonNode(UUID parentNodeUuid
, UUID taxonUuid
, Reference ref
, String microref
){
926 UpdateResult result
= new UpdateResult();
927 TaxonNode parent
= dao
.load(parentNodeUuid
);
928 Taxon taxon
= (Taxon
) taxonService
.load(taxonUuid
);
929 TaxonNode child
= null;
931 child
= parent
.addChildTaxon(taxon
, parent
.getReference(), parent
.getMicroReference());
933 result
.addException(e
);
937 // child = dao.save(child);
939 // dao.saveOrUpdate(child);
940 result
.addUpdatedObject(parent
);
942 result
.setCdmEntity(child
);
950 public UpdateResult
addTaxonNodeAgentRelation(UUID taxonNodeUUID
, UUID agentUUID
, DefinedTerm relationshipType
){
951 UpdateResult result
= new UpdateResult();
952 TaxonNode node
= dao
.load(taxonNodeUUID
);
953 TeamOrPersonBase
<?
> agent
= (TeamOrPersonBase
<?
>) agentService
.load(agentUUID
);
954 node
.addAgentRelation(relationshipType
, agent
);
956 dao
.merge(node
, true);
957 }catch (Exception e
){
959 result
.addException(e
);
961 result
.setCdmEntity(node
);
966 @Transactional(readOnly
=false)
967 public UpdateResult
setSecundumForSubtree(SecundumForSubtreeConfigurator config
) {
968 UpdateResult result
= new UpdateResult();
969 IProgressMonitor monitor
= config
.getMonitor();
971 if (monitor
== null){
972 monitor
= DefaultProgressMonitor
.NewInstance();
974 TaxonNode subTree
= load(config
.getSubtreeUuid());
975 TreeIndex subTreeIndex
= null;
976 Reference newSec
= null;
977 if (config
.getNewSecundum() != null){
978 newSec
= refService
.load(config
.getNewSecundum().getUuid());
981 if (config
.getSubtreeUuid() == null){
983 result
.addException(new NullPointerException("No subtree given"));
988 if (subTree
== null){
990 result
.addException(new NullPointerException("Subtree does not exist"));
994 subTreeIndex
= TreeIndex
.NewInstance(subTree
.treeIndex());
995 int count
= config
.isIncludeAcceptedTaxa() ? dao
.countSecundumForSubtreeAcceptedTaxa(subTreeIndex
, newSec
, config
.isOverwriteExistingAccepted(), config
.isIncludeSharedTaxa(), config
.isEmptySecundumDetail()):0;
996 count
+= config
.isIncludeSynonyms() ? dao
.countSecundumForSubtreeSynonyms(subTreeIndex
, newSec
, config
.isOverwriteExistingSynonyms(), config
.isIncludeSharedTaxa() , config
.isEmptySecundumDetail()) :0;
997 monitor
.beginTask("Update Secundum Reference", count
);
1000 //Reference ref = config.getNewSecundum();
1001 if (config
.isIncludeAcceptedTaxa()){
1002 monitor
.subTask("Update Accepted Taxa");
1004 Set
<TaxonBase
> updatedTaxa
= dao
.setSecundumForSubtreeAcceptedTaxa(subTreeIndex
, newSec
, config
.isOverwriteExistingAccepted(), config
.isIncludeSharedTaxa(), config
.isEmptySecundumDetail(), monitor
);
1005 result
.addUpdatedObjects(updatedTaxa
);
1007 if (config
.isIncludeSynonyms()){
1008 monitor
.subTask("Update Synonyms");
1009 Set
<TaxonBase
> updatedSynonyms
= dao
.setSecundumForSubtreeSynonyms(subTreeIndex
, newSec
, config
.isOverwriteExistingSynonyms(), config
.isIncludeSharedTaxa() , config
.isEmptySecundumDetail(), monitor
);
1010 result
.addUpdatedObjects(updatedSynonyms
);
1019 @Transactional(readOnly
=false)
1020 public UpdateResult
setPublishForSubtree(PublishForSubtreeConfigurator config
){
1021 UpdateResult result
= new UpdateResult();
1022 IProgressMonitor monitor
= config
.getMonitor();
1023 if (monitor
== null){
1024 monitor
= DefaultProgressMonitor
.NewInstance();
1026 TreeIndex subTreeIndex
= null;
1028 if (config
.getSubtreeUuid() == null){
1030 result
.addException(new NullPointerException("No subtree given"));
1034 TaxonNode subTree
= find(config
.getSubtreeUuid());
1035 boolean includeAcceptedTaxa
= config
.isIncludeAcceptedTaxa();
1036 boolean publish
= config
.isPublish();
1037 boolean includeSynonyms
= config
.isIncludeSynonyms();
1038 boolean includeSharedTaxa
= config
.isIncludeSharedTaxa();
1039 boolean includeHybrids
= config
.isIncludeHybrids();
1040 boolean includeRelatedTaxa
= config
.isIncludeProParteSynonyms() || config
.isIncludeMisapplications();
1041 if (subTree
== null){
1043 result
.addException(new NullPointerException("Subtree does not exist"));
1047 subTreeIndex
= TreeIndex
.NewInstance(subTree
.treeIndex());
1048 int count
= includeAcceptedTaxa ? dao
.countPublishForSubtreeAcceptedTaxa(subTreeIndex
, publish
, includeSharedTaxa
, includeHybrids
):0;
1049 count
+= includeSynonyms ? dao
.countPublishForSubtreeSynonyms(subTreeIndex
, publish
, includeSharedTaxa
, includeHybrids
):0;
1050 count
+= includeRelatedTaxa ? dao
.countPublishForSubtreeRelatedTaxa(subTreeIndex
, publish
, includeSharedTaxa
, includeHybrids
):0;
1051 monitor
.beginTask("Update publish flag", count
);
1055 if (includeAcceptedTaxa
){
1056 monitor
.subTask("Update Accepted Taxa");
1057 Set
<TaxonBase
> updatedTaxa
= dao
.setPublishForSubtreeAcceptedTaxa(subTreeIndex
, publish
, includeSharedTaxa
, includeHybrids
, monitor
);
1058 result
.addUpdatedObjects(updatedTaxa
);
1060 if (includeSynonyms
){
1061 monitor
.subTask("Update Synonyms");
1062 Set
<TaxonBase
> updatedSynonyms
= dao
.setPublishForSubtreeSynonyms(subTreeIndex
, publish
, includeSharedTaxa
, includeHybrids
, monitor
);
1063 result
.addUpdatedObjects(updatedSynonyms
);
1065 if (includeRelatedTaxa
){
1066 monitor
.subTask("Update Related Taxa");
1067 Set
<UUID
> relationTypes
= new HashSet
<>();
1068 if (config
.isIncludeMisapplications()){
1069 relationTypes
.addAll(TaxonRelationshipType
.misappliedNameUuids());
1071 if (config
.isIncludeProParteSynonyms()){
1072 relationTypes
.addAll(TaxonRelationshipType
.proParteOrPartialSynonymUuids());
1074 Set
<TaxonBase
> updatedTaxa
= dao
.setPublishForSubtreeRelatedTaxa(subTreeIndex
, publish
,
1075 relationTypes
, includeSharedTaxa
, includeHybrids
, monitor
);
1076 result
.addUpdatedObjects(updatedTaxa
);
1086 public long count(TaxonNodeFilter filter
){
1087 return nodeFilterDao
.count(filter
);
1091 public List
<UUID
> uuidList(TaxonNodeFilter filter
){
1092 return nodeFilterDao
.listUuids(filter
);
1096 public List
<Integer
> idList(TaxonNodeFilter filter
){
1097 return nodeFilterDao
.idList(filter
);
1101 public TaxonNodeDto
findCommonParentDto(Collection
<TaxonNodeDto
> nodes
) {
1102 TaxonNodeDto commonParent
= null;
1103 List
<String
> treePath
= null;
1104 for (TaxonNodeDto nodeDto
: nodes
) {
1105 String nodeTreeIndex
= nodeDto
.getTreeIndex();
1106 nodeTreeIndex
= nodeTreeIndex
.replaceFirst("#", "");
1107 String
[] split
= nodeTreeIndex
.split("#");
1108 if(treePath
== null){
1109 treePath
= Arrays
.asList(split
);
1112 List
<String
> match
= new ArrayList
<>();
1113 for(int i
=0;i
<treePath
.size();i
++){
1114 if(i
>=split
.length
){
1115 //current tree index is shorter so break
1118 else if(split
[i
].equals(treePath
.get(i
))){
1120 match
.add(treePath
.get(i
));
1123 //first mismatch found
1128 if(treePath
.isEmpty()){
1129 //no common parent found for at least two nodes
1130 //-> they belong to a different classification
1135 if(treePath
!=null && !treePath
.isEmpty()) {
1137 int nodeId
= Integer
.parseInt(treePath
.get(treePath
.size()-1));
1138 TaxonNode taxonNode
= dao
.load(nodeId
, null);
1139 commonParent
= new TaxonNodeDto(taxonNode
);
1141 return commonParent
;
1147 public List
<TaxonDistributionDTO
> getTaxonDistributionDTOForSubtree(UUID parentNodeUuid
, List
<String
> propertyPaths
, Authentication authentication
){
1148 List
<TaxonNode
> nodes
= listChildrenOf(load(parentNodeUuid
), null, null,
1149 true, true, propertyPaths
);
1150 List
<TaxonDistributionDTO
> result
= new ArrayList
<>();
1151 boolean hasPermission
= false;
1152 //TaxonDescription instance = TaxonDescription.NewInstance();
1153 //hasPermission = permissionEvaluator.hasPermission(authentication, instance, Operation.UPDATE);
1154 for(TaxonNode node
:nodes
){
1155 if (authentication
!= null ) {
1156 hasPermission
= permissionEvaluator
.hasPermission(authentication
, node
, Operation
.UPDATE
);
1157 }else if (authentication
== null){
1158 hasPermission
= true;
1160 if (node
.getTaxon() != null && hasPermission
){
1162 TaxonDistributionDTO dto
= new TaxonDistributionDTO(node
.getTaxon());
1164 }catch(Exception e
){
1165 System
.err
.println(node
.getTaxon().getTitleCache());
1176 public <S
extends TaxonNode
> Pager
<S
> page(Class
<S
> clazz
, List
<Restriction
<?
>> restrictions
, Integer pageSize
,
1177 Integer pageIndex
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1178 return page(clazz
, restrictions
, pageSize
, pageIndex
, orderHints
, propertyPaths
, INCLUDE_UNPUBLISHED
);
1182 public <S
extends TaxonNode
> Pager
<S
> page(Class
<S
> clazz
, List
<Restriction
<?
>> restrictions
, Integer pageSize
,
1183 Integer pageIndex
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, boolean includeUnpublished
) {
1186 long resultSize
= dao
.count(clazz
, restrictions
);
1187 if(AbstractPagerImpl
.hasResultsInRange(resultSize
, pageIndex
, pageSize
)){
1188 records
= dao
.list(clazz
, restrictions
, pageSize
, pageIndex
, orderHints
, propertyPaths
, includeUnpublished
);
1190 records
= new ArrayList
<>();
1192 Pager
<S
> pager
= new DefaultPagerImpl
<>(pageIndex
, resultSize
, pageSize
, records
);
1197 public List
<TaxonDistributionDTO
> getTaxonDistributionDTOForSubtree(UUID parentNodeUuid
,
1198 List
<String
> propertyPaths
) {
1199 return getTaxonDistributionDTOForSubtree(parentNodeUuid
, propertyPaths
, null);
1203 public List
<TaxonNodeDto
> getTaxonNodeDto(Integer limit
, String pattern
, UUID classificationUuid
) {
1204 return dao
.getTaxonNodeDto(limit
, pattern
, classificationUuid
);
1208 public List
<UuidAndTitleCache
<TaxonNode
>> listChildNodesAsUuidAndTitleCache(ITaxonTreeNode parent
) {
1209 UUID uuid
= parent
.getUuid();
1210 int id
= parent
.getId();
1211 UuidAndTitleCache
<TaxonNode
> uuidAndTitleCache
= new UuidAndTitleCache
<>(uuid
, id
, null);
1212 return listChildNodesAsUuidAndTitleCache(uuidAndTitleCache
);
1216 public List
<UuidAndTitleCache
<TaxonNode
>> listChildNodesAsUuidAndTitleCache(UuidAndTitleCache
<TaxonNode
> parent
) {
1217 return dao
.listChildNodesAsUuidAndTitleCache(parent
);