3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
11 package eu
.etaxonomy
.cdm
.api
.service
;
13 import java
.util
.ArrayList
;
14 import java
.util
.Collection
;
15 import java
.util
.Collections
;
16 import java
.util
.Comparator
;
17 import java
.util
.HashSet
;
18 import java
.util
.List
;
20 import java
.util
.UUID
;
22 import org
.apache
.log4j
.Logger
;
23 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
24 import org
.springframework
.stereotype
.Service
;
25 import org
.springframework
.transaction
.annotation
.Transactional
;
27 import eu
.etaxonomy
.cdm
.api
.service
.UpdateResult
.Status
;
28 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
29 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonNodeDeletionConfigurator
;
30 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonNodeDeletionConfigurator
.ChildHandling
;
31 import eu
.etaxonomy
.cdm
.api
.service
.dto
.CdmEntityIdentifier
;
32 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
33 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
34 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
35 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
36 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
37 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
38 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
39 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
40 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
41 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
42 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNaturalComparator
;
43 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
44 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNodeByNameComparator
;
45 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNodeByRankAndNameComparator
;
46 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
47 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.IBeanInitializer
;
48 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonNodeDao
;
52 * @created Apr 9, 2010
56 @Transactional(readOnly
= true)
57 public class TaxonNodeServiceImpl
extends AnnotatableServiceBase
<TaxonNode
, ITaxonNodeDao
> implements ITaxonNodeService
{
58 private static final Logger logger
= Logger
.getLogger(TaxonNodeServiceImpl
.class);
61 private IBeanInitializer defaultBeanInitializer
;
63 private final Comparator
<?
super TaxonNode
> taxonNodeComparator
= new TaxonNodeByNameComparator();
66 private ITaxonService taxonService
;
69 private IClassificationService classService
;
74 public List
<TaxonNode
> loadChildNodesOfTaxonNode(TaxonNode taxonNode
,
75 List
<String
> propertyPaths
, boolean recursive
, NodeSortMode sortMode
) {
76 taxonNode
= dao
.load(taxonNode
.getUuid());
77 List
<TaxonNode
> childNodes
;
78 if (recursive
== true){
79 childNodes
= dao
.listChildrenOf(taxonNode
, null, null, null, recursive
);
81 childNodes
= new ArrayList
<TaxonNode
>(taxonNode
.getChildNodes());
83 if (sortMode
!= null){
84 Comparator
<TaxonNode
> comparator
= null;
85 if (sortMode
.equals(NodeSortMode
.NaturalOrder
)){
86 comparator
= new TaxonNaturalComparator();
87 } else if (sortMode
.equals(NodeSortMode
.AlphabeticalOrder
)){
88 comparator
= new TaxonNodeByNameComparator();
89 } else if (sortMode
.equals(NodeSortMode
.RankAndAlphabeticalOrder
)){
90 comparator
= new TaxonNodeByRankAndNameComparator();
92 Collections
.sort(childNodes
, comparator
);
94 defaultBeanInitializer
.initializeAll(childNodes
, propertyPaths
);
100 protected void setDao(ITaxonNodeDao dao
) {
105 @Transactional(readOnly
= false)
106 public DeleteResult
makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode
, TaxonNode newAcceptedTaxonNode
, SynonymRelationshipType synonymRelationshipType
, Reference citation
, String citationMicroReference
) {
109 // TODO at the moment this method only moves synonym-, concept relations and descriptions to the new accepted taxon
110 // in a future version we also want to move cdm data like annotations, marker, so., but we will need a policy for that
111 if (oldTaxonNode
== null || newAcceptedTaxonNode
== null || oldTaxonNode
.getTaxon().getName() == null){
112 throw new IllegalArgumentException("A mandatory parameter was null.");
115 if(oldTaxonNode
.equals(newAcceptedTaxonNode
)){
116 throw new IllegalArgumentException("Taxon can not be made synonym of its own.");
121 Classification classification
= oldTaxonNode
.getClassification();
122 Taxon oldTaxon
= HibernateProxyHelper
.deproxy(oldTaxonNode
.getTaxon(), Taxon
.class);
123 Taxon newAcceptedTaxon
= HibernateProxyHelper
.deproxy(this.taxonService
.load(newAcceptedTaxonNode
.getTaxon().getUuid()), Taxon
.class);
125 // Move oldTaxon to newTaxon
126 //TaxonNameBase<?,?> synonymName = oldTaxon.getName();
127 TaxonNameBase
<?
,?
> synonymName
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(oldTaxon
.getName());
128 HomotypicalGroup group
= synonymName
.getHomotypicalGroup();
129 group
= HibernateProxyHelper
.deproxy(group
, HomotypicalGroup
.class);
130 if (synonymRelationshipType
== null){
131 if (synonymName
.isHomotypic(newAcceptedTaxon
.getName())){
132 synonymRelationshipType
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
134 synonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
138 //set homotypic group
139 HomotypicalGroup newAcceptedTaxonHomotypicalgroup
= newAcceptedTaxon
.getHomotypicGroup();
140 newAcceptedTaxonHomotypicalgroup
= HibernateProxyHelper
.deproxy(newAcceptedTaxonHomotypicalgroup
, HomotypicalGroup
.class);
141 TaxonNameBase newAcceptedTaxonName
= HibernateProxyHelper
.deproxy(newAcceptedTaxon
.getName(), TaxonNameBase
.class);
142 // Move Synonym Relations to new Taxon
143 SynonymRelationship synonmyRelationship
= newAcceptedTaxon
.addSynonymName(synonymName
,
144 synonymRelationshipType
, citation
, citationMicroReference
);
145 HomotypicalGroup homotypicalGroupAcceptedTaxon
= synonmyRelationship
.getSynonym().getHomotypicGroup();
146 // Move Synonym Relations to new Taxon
147 // From ticket 3163 we can move taxon with accepted name having homotypic synonyms
148 List
<Synonym
> synonymsInHomotypicalGroup
= null;
150 //the synonyms of the homotypical group of the old taxon
151 if (synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
152 synonymsInHomotypicalGroup
= oldTaxon
.getSynonymsInGroup(group
);
155 for(SynonymRelationship synRelation
: oldTaxon
.getSynonymRelations()){
156 SynonymRelationshipType srt
;
157 if(synRelation
.getSynonym().getName().getHomotypicalGroup()!= null
158 && synRelation
.getSynonym().getName().getHomotypicalGroup().equals(newAcceptedTaxonName
.getHomotypicalGroup())) {
159 srt
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
160 } else if(synRelation
.getType() != null && synRelation
.getType().equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())) {
161 if (synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
162 srt
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
164 srt
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
167 srt
= synRelation
.getType();
171 newAcceptedTaxon
.addSynonym(synRelation
.getSynonym(),
173 synRelation
.getCitation(),
174 synRelation
.getCitationMicroReference());
176 /*if (synonymsInHomotypicalGroup.contains(synRelation.getSynonym()) && srt.equals(SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF())){
177 homotypicalGroupAcceptedTaxon.addTypifiedName(synRelation.getSynonym().getName());
187 if(oldTaxonNode
.getChildNodes() != null && oldTaxonNode
.getChildNodes().size() != 0){
188 List
<TaxonNode
> childNodes
= new ArrayList
<TaxonNode
>();
189 for (TaxonNode childNode
: oldTaxonNode
.getChildNodes()){
190 childNodes
.add(childNode
);
192 for(TaxonNode childNode
:childNodes
){
193 newAcceptedTaxonNode
.addChildNode(childNode
, childNode
.getReference(), childNode
.getMicroReference()); // childNode.getSynonymToBeUsed()
197 //Move Taxon RelationShips to new Taxon
198 Set
<TaxonRelationship
> obsoleteTaxonRelationships
= new HashSet
<TaxonRelationship
>();
199 for(TaxonRelationship taxonRelationship
: oldTaxon
.getTaxonRelations()){
200 Taxon fromTaxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxonRelationship
.getFromTaxon());
201 Taxon toTaxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxonRelationship
.getToTaxon());
202 if (fromTaxon
== oldTaxon
){
203 newAcceptedTaxon
.addTaxonRelation(taxonRelationship
.getToTaxon(), taxonRelationship
.getType(),
204 taxonRelationship
.getCitation(), taxonRelationship
.getCitationMicroReference());
206 }else if(toTaxon
== oldTaxon
){
207 fromTaxon
.addTaxonRelation(newAcceptedTaxon
, taxonRelationship
.getType(),
208 taxonRelationship
.getCitation(), taxonRelationship
.getCitationMicroReference());
209 taxonService
.saveOrUpdate(fromTaxon
);
212 logger
.warn("Taxon is not part of its own Taxonrelationship");
214 // Remove old relationships
216 fromTaxon
.removeTaxonRelation(taxonRelationship
);
217 toTaxon
.removeTaxonRelation(taxonRelationship
);
218 taxonRelationship
.setToTaxon(null);
219 taxonRelationship
.setFromTaxon(null);
223 //Move descriptions to new taxon
224 List
<TaxonDescription
> descriptions
= new ArrayList
<TaxonDescription
>( oldTaxon
.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())
225 for(TaxonDescription description
: descriptions
){
226 String message
= "Description copied from former accepted taxon: %s (Old title: %s)";
227 message
= String
.format(message
, oldTaxon
.getTitleCache(), description
.getTitleCache());
228 description
.setTitleCache(message
, true);
229 //oldTaxon.removeDescription(description, false);
230 newAcceptedTaxon
.addDescription(description
);
232 oldTaxon
.clearDescriptions();
234 taxonService
.update(newAcceptedTaxon
);
236 taxonService
.update(oldTaxon
);
238 TaxonDeletionConfigurator conf
= new TaxonDeletionConfigurator();
239 conf
.setDeleteSynonymsIfPossible(false);
240 conf
.setDeleteNameIfPossible(false);
241 DeleteResult result
= taxonService
.isDeletable(oldTaxon
, conf
);
242 conf
.setDeleteNameIfPossible(false);
245 result
= taxonService
.deleteTaxon(oldTaxon
.getUuid(), conf
, classification
.getUuid());
247 result
.setStatus(Status
.OK
);
248 TaxonNodeDeletionConfigurator config
= new TaxonNodeDeletionConfigurator();
249 config
.setDeleteTaxon(false);
250 conf
.setTaxonNodeConfig(config
);
251 result
.includeResult(deleteTaxonNode(oldTaxonNode
, conf
));
253 result
.addUpdatedObject(newAcceptedTaxon
);
254 //result.addUpdatedObject(oldTaxon);
256 //oldTaxonNode.delete();
263 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#makeTaxonNodeASynonymOfAnotherTaxonNode(java.util.UUID, java.util.UUID, java.util.UUID, java.util.UUID, java.lang.String)
266 @Transactional(readOnly
= false)
267 public UpdateResult
makeTaxonNodeASynonymOfAnotherTaxonNode(UUID oldTaxonNodeUuid
,
268 UUID newAcceptedTaxonNodeUUID
,
269 SynonymRelationshipType synonymRelationshipType
,
271 String citationMicroReference
) {
273 TaxonNode oldTaxonNode
= dao
.load(oldTaxonNodeUuid
);
274 TaxonNode oldTaxonParentNode
= oldTaxonNode
.getParent();
275 TaxonNode newTaxonNode
= dao
.load(newAcceptedTaxonNodeUUID
);
277 UpdateResult result
= makeTaxonNodeASynonymOfAnotherTaxonNode(oldTaxonNode
,
279 synonymRelationshipType
,
281 citationMicroReference
);
282 result
.addUpdatedCdmId(new CdmEntityIdentifier(oldTaxonParentNode
.getId(), TaxonNode
.class));
283 result
.addUpdatedCdmId(new CdmEntityIdentifier(newTaxonNode
.getId(), TaxonNode
.class));
284 result
.setCdmEntity(oldTaxonParentNode
);
289 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNodes(java.util.List)
292 @Transactional(readOnly
= false)
293 public DeleteResult
deleteTaxonNodes(List
<TaxonNode
> list
, TaxonDeletionConfigurator config
) {
296 config
= new TaxonDeletionConfigurator();
298 DeleteResult result
= new DeleteResult();
299 List
<UUID
> deletedUUIDs
= new ArrayList
<UUID
>();
300 Classification classification
= null;
301 List
<TaxonNode
> taxonNodes
= new ArrayList
<TaxonNode
>(list
);
302 for (TaxonNode treeNode
:taxonNodes
){
303 if (treeNode
!= null){
306 taxonNode
= HibernateProxyHelper
.deproxy(treeNode
, TaxonNode
.class);
307 TaxonNode parent
= taxonNode
.getParent();
308 //check whether the node has children or the children are already deleted
309 if(taxonNode
.hasChildNodes()) {
310 List
<TaxonNode
> children
= new ArrayList
<TaxonNode
> ();
311 List
<TaxonNode
> childNodesList
= taxonNode
.getChildNodes();
312 children
.addAll(childNodesList
);
313 int compare
= config
.getTaxonNodeConfig().getChildHandling().compareTo(ChildHandling
.DELETE
);
314 boolean childHandling
= (compare
== 0)?
true: false;
316 boolean changeDeleteTaxon
= false;
317 if (!config
.getTaxonNodeConfig().isDeleteTaxon()){
318 config
.getTaxonNodeConfig().setDeleteTaxon(true);
319 changeDeleteTaxon
= true;
321 DeleteResult resultNodes
= deleteTaxonNodes(children
, config
);
322 if (!resultNodes
.isOk()){
323 result
.addExceptions(resultNodes
.getExceptions());
324 result
.setStatus(resultNodes
.getStatus());
326 if (changeDeleteTaxon
){
327 config
.getTaxonNodeConfig().setDeleteTaxon(false);
331 //move the children to the parent
333 for (TaxonNode child
: childNodesList
){
334 parent
.addChildNode(child
, child
.getReference(), child
.getMicroReference());
340 classification
= taxonNode
.getClassification();
342 if (classification
.getRootNode().equals(taxonNode
)){
343 classification
.removeRootNode();
344 classification
= null;
345 }else if (classification
.getChildNodes().contains(taxonNode
)){
346 Taxon taxon
= taxonNode
.getTaxon();
347 classification
.deleteChildNode(taxonNode
);
352 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
353 taxonService
.saveOrUpdate(taxon
);
354 saveOrUpdate(taxonNode
);
356 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
357 DeleteResult resultTaxon
= taxonService
.deleteTaxon(taxon
.getUuid(), configNew
, classification
.getUuid());
358 if (!resultTaxon
.isOk()){
359 result
.addExceptions(resultTaxon
.getExceptions());
360 result
.setStatus(resultTaxon
.getStatus());
365 classification
= null;
368 classification
= null;
369 Taxon taxon
= taxonNode
.getTaxon();
370 taxon
= HibernateProxyHelper
.deproxy(taxon
, Taxon
.class);
372 taxon
.removeTaxonNode(taxonNode
);
373 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
374 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
375 saveOrUpdate(taxonNode
);
376 taxonService
.saveOrUpdate(taxon
);
377 DeleteResult resultTaxon
= taxonService
.deleteTaxon(taxon
.getUuid(), configNew
, null);
379 if (!resultTaxon
.isOk()){
380 result
.addExceptions(resultTaxon
.getExceptions());
381 result
.setStatus(resultTaxon
.getStatus());
388 result
.addUpdatedObject(parent
);
389 if(result
.getCdmEntity() == null){
390 result
.setCdmEntity(taxonNode
);
392 UUID uuid
= dao
.delete(taxonNode
);
393 logger
.debug("Deleted node " +uuid
.toString());
397 /*if (classification != null){
398 result.addUpdatedObject(classification);
399 DeleteResult resultClassification = classService.delete(classification);
400 if (!resultClassification.isOk()){
401 result.addExceptions(resultClassification.getExceptions());
402 result.setStatus(resultClassification.getStatus());
411 @Transactional(readOnly
= false)
412 public DeleteResult
deleteTaxonNodes(Collection
<UUID
> nodeUuids
, TaxonDeletionConfigurator config
) {
413 List
<TaxonNode
> nodes
= new ArrayList
<TaxonNode
>();
414 for(UUID nodeUuid
: nodeUuids
) {
415 nodes
.add(dao
.load(nodeUuid
));
417 return deleteTaxonNodes(nodes
, config
);
423 @Transactional(readOnly
= false)
424 public DeleteResult
deleteTaxonNode(UUID nodeUUID
, TaxonDeletionConfigurator config
) {
425 TaxonNode node
= HibernateProxyHelper
.deproxy(dao
.load(nodeUUID
), TaxonNode
.class);
426 return deleteTaxonNode(node
, config
);
430 @Transactional(readOnly
= false)
431 public DeleteResult
deleteTaxonNode(TaxonNode node
, TaxonDeletionConfigurator config
) {
433 Taxon taxon
= (Taxon
)HibernateProxyHelper
.deproxy(node
.getTaxon());
434 TaxonNode parent
= HibernateProxyHelper
.deproxy(node
.getParent(), TaxonNode
.class);
436 config
= new TaxonDeletionConfigurator();
438 DeleteResult result
= new DeleteResult();
440 if (config
.getTaxonNodeConfig().getChildHandling().equals(TaxonNodeDeletionConfigurator
.ChildHandling
.MOVE_TO_PARENT
)){
441 Object
[] children
= node
.getChildNodes().toArray();
443 for (Object child
: children
){
444 childNode
= (TaxonNode
) child
;
445 parent
.addChildNode(childNode
, childNode
.getReference(), childNode
.getMicroReference());
448 deleteTaxonNodes(node
.getChildNodes(), config
);
451 if (config
.getTaxonNodeConfig().isDeleteTaxon() && (config
.isDeleteInAllClassifications() || taxon
.getTaxonNodes().size() == 1)){
452 result
= taxonService
.deleteTaxon(taxon
.getUuid(), config
, node
.getClassification().getUuid());
453 result
.addUpdatedObject(parent
);
458 result
.addUpdatedObject(taxon
);
461 result
.setCdmEntity(node
);
463 boolean success
= true;
465 success
= taxon
.removeTaxonNode(node
);
468 taxonService
.saveOrUpdate(taxon
);
469 result
.addUpdatedObject(parent
);
472 result
.setStatus(Status
.OK
);
473 parent
= HibernateProxyHelper
.deproxy(parent
, TaxonNode
.class);
474 int index
= parent
.getChildNodes().indexOf(node
);
476 parent
.removeChild(index
);
478 if (!dao
.delete(node
, config
.getTaxonNodeConfig().getChildHandling().equals(TaxonNodeDeletionConfigurator
.ChildHandling
.DELETE
)).equals(null)){
485 if (dao
.findByUuid(node
.getUuid()) != null){
487 result
.addException(new Exception("The node can not be removed from the taxon."));
498 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#listAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification, int, int)
501 public List
<TaxonNode
> listAllNodesForClassification(Classification classification
, Integer start
, Integer end
) {
502 return dao
.getTaxonOfAcceptedTaxaByClassification(classification
, start
, end
);
506 public int countAllNodesForClassification(Classification classification
) {
507 return dao
.countTaxonOfAcceptedTaxaByClassification(classification
);
512 public UpdateResult
moveTaxonNode(UUID taxonNodeUuid
, UUID targetNodeUuid
, boolean isParent
){
513 TaxonNode taxonNode
= dao
.load(taxonNodeUuid
);
514 TaxonNode targetNode
= dao
.load(targetNodeUuid
);
515 return moveTaxonNode(taxonNode
, targetNode
, isParent
);
520 public UpdateResult
moveTaxonNode(TaxonNode taxonNode
, TaxonNode newParent
, boolean isParent
){
521 UpdateResult result
= new UpdateResult();
526 sortIndex
= newParent
.getChildNodes().size();
528 sortIndex
= newParent
.getSortIndex() +1;
529 newParent
= newParent
.getParent();
531 result
.addUpdatedObject(newParent
);
532 result
.addUpdatedObject(taxonNode
.getParent());
533 result
.setCdmEntity(taxonNode
);
534 newParent
.addChildNode(taxonNode
, sortIndex
, taxonNode
.getReference(), taxonNode
.getMicroReference());
535 dao
.saveOrUpdate(newParent
);
544 public UpdateResult
moveTaxonNodes(Set
<UUID
> taxonNodeUuids
, UUID newParentNodeUuid
, boolean isParent
){
545 UpdateResult result
= new UpdateResult();
546 TaxonNode targetNode
= dao
.load(newParentNodeUuid
);
547 for (UUID taxonNodeUuid
: taxonNodeUuids
){
548 TaxonNode taxonNode
= dao
.load(taxonNodeUuid
);
549 result
.includeResult(moveTaxonNode(taxonNode
,targetNode
, isParent
));