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
.config
.TaxonDeletionConfigurator
;
28 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonNodeDeletionConfigurator
;
29 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonNodeDeletionConfigurator
.ChildHandling
;
30 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
31 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
32 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
33 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
34 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
35 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
36 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonNodeComparator
;
37 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonTreeNode
;
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
.TaxonNode
;
43 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
44 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.IBeanInitializer
;
45 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonNodeDao
;
49 * @created Apr 9, 2010
53 @Transactional(readOnly
= true)
54 public class TaxonNodeServiceImpl
extends AnnotatableServiceBase
<TaxonNode
, ITaxonNodeDao
> implements ITaxonNodeService
{
55 private static final Logger logger
= Logger
.getLogger(TaxonNodeServiceImpl
.class);
58 private IBeanInitializer defaultBeanInitializer
;
60 private Comparator
<?
super TaxonNode
> taxonNodeComparator
;
63 private ITaxonService taxonService
;
66 private IClassificationService classService
;
69 public void setTaxonNodeComparator(ITaxonNodeComparator
<?
super TaxonNode
> taxonNodeComparator
){
70 this.taxonNodeComparator
= (Comparator
<?
super TaxonNode
>) taxonNodeComparator
;
74 public List
<TaxonNode
> loadChildNodesOfTaxonNode(TaxonNode taxonNode
,
75 List
<String
> propertyPaths
, boolean recursive
, boolean sorted
) {
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());
84 Collections
.sort(childNodes
, taxonNodeComparator
);
86 defaultBeanInitializer
.initializeAll(childNodes
, propertyPaths
);
91 * @see eu.etaxonomy.cdm.api.service.ServiceBase#setDao(eu.etaxonomy.cdm.persistence.dao.common.ICdmEntityDao)
95 protected void setDao(ITaxonNodeDao dao
) {
100 * @see eu.etaxonomy.cdm.api.service.ITaxonService#makeTaxonSynonym(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.Taxon)
103 @Transactional(readOnly
= false)
104 public Synonym
makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode
,
105 TaxonNode newAcceptedTaxonNode
,
106 SynonymRelationshipType synonymRelationshipType
,
108 String citationMicroReference
) {
110 // TODO at the moment this method only moves synonym-, concept relations and descriptions to the new accepted taxon
111 // in a future version we also want to move cdm data like annotations, marker, so., but we will need a policy for that
112 if (oldTaxonNode
== null || newAcceptedTaxonNode
== null || oldTaxonNode
.getTaxon().getName() == null){
113 throw new IllegalArgumentException("A mandatory parameter was null.");
116 if(oldTaxonNode
.equals(newAcceptedTaxonNode
)){
117 throw new IllegalArgumentException("Taxon can not be made synonym of its own.");
122 Taxon oldTaxon
= (Taxon
) HibernateProxyHelper
.deproxy(oldTaxonNode
.getTaxon());
123 Taxon newAcceptedTaxon
= (Taxon
)this.taxonService
.load(newAcceptedTaxonNode
.getTaxon().getUuid());
124 // Move oldTaxon to newTaxon
125 //TaxonNameBase<?,?> synonymName = oldTaxon.getName();
126 TaxonNameBase
<?
,?
> synonymName
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(oldTaxon
.getName());
127 HomotypicalGroup group
= synonymName
.getHomotypicalGroup();
128 if (synonymRelationshipType
== null){
129 if (synonymName
.isHomotypic(newAcceptedTaxon
.getName())){
130 synonymRelationshipType
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
132 synonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
136 //set homotypic group
137 HomotypicalGroup newAcceptedTaxonHomotypicalgroup
= newAcceptedTaxon
.getHomotypicGroup();
138 HibernateProxyHelper
.deproxy(newAcceptedTaxonHomotypicalgroup
);
139 HibernateProxyHelper
.deproxy(newAcceptedTaxon
.getName());
140 // Move Synonym Relations to new Taxon
141 SynonymRelationship synonmyRelationship
= newAcceptedTaxon
.addSynonymName(synonymName
,
142 synonymRelationshipType
, citation
, citationMicroReference
);
143 HomotypicalGroup homotypicalGroupAcceptedTaxon
= synonmyRelationship
.getSynonym().getHomotypicGroup();
144 // Move Synonym Relations to new Taxon
145 // From ticket 3163 we can move taxon with accepted name having homotypic synonyms
146 List
<Synonym
> synonymsInHomotypicalGroup
= null;
148 //the synonyms of the homotypical group of the old taxon
149 if (synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
150 synonymsInHomotypicalGroup
= oldTaxon
.getSynonymsInGroup(group
);
153 for(SynonymRelationship synRelation
: oldTaxon
.getSynonymRelations()){
154 SynonymRelationshipType srt
;
155 if(synRelation
.getSynonym().getName().getHomotypicalGroup()!= null
156 && synRelation
.getSynonym().getName().getHomotypicalGroup().equals(newAcceptedTaxon
.getName().getHomotypicalGroup())) {
157 srt
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
158 } else if(synRelation
.getType() != null && synRelation
.getType().equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())) {
159 if (synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
160 srt
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
162 srt
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
165 srt
= synRelation
.getType();
169 newAcceptedTaxon
.addSynonym(synRelation
.getSynonym(),
171 synRelation
.getCitation(),
172 synRelation
.getCitationMicroReference());
174 /*if (synonymsInHomotypicalGroup.contains(synRelation.getSynonym()) && srt.equals(SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF())){
175 homotypicalGroupAcceptedTaxon.addTypifiedName(synRelation.getSynonym().getName());
185 if(oldTaxonNode
.getChildNodes() != null && oldTaxonNode
.getChildNodes().size() != 0){
186 for(TaxonNode childNode
: oldTaxonNode
.getChildNodes()){
187 newAcceptedTaxonNode
.addChildNode(childNode
, childNode
.getReference(), childNode
.getMicroReference()); // childNode.getSynonymToBeUsed()
191 //Move Taxon RelationShips to new Taxon
192 Set
<TaxonRelationship
> obsoleteTaxonRelationships
= new HashSet
<TaxonRelationship
>();
193 for(TaxonRelationship taxonRelationship
: oldTaxon
.getTaxonRelations()){
194 Taxon fromTaxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxonRelationship
.getFromTaxon());
195 Taxon toTaxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxonRelationship
.getToTaxon());
196 if (fromTaxon
== oldTaxon
){
197 newAcceptedTaxon
.addTaxonRelation(taxonRelationship
.getToTaxon(), taxonRelationship
.getType(),
198 taxonRelationship
.getCitation(), taxonRelationship
.getCitationMicroReference());
200 }else if(toTaxon
== oldTaxon
){
201 fromTaxon
.addTaxonRelation(newAcceptedTaxon
, taxonRelationship
.getType(),
202 taxonRelationship
.getCitation(), taxonRelationship
.getCitationMicroReference());
203 taxonService
.saveOrUpdate(fromTaxon
);
206 logger
.warn("Taxon is not part of its own Taxonrelationship");
208 // Remove old relationships
210 fromTaxon
.removeTaxonRelation(taxonRelationship
);
211 toTaxon
.removeTaxonRelation(taxonRelationship
);
212 taxonRelationship
.setToTaxon(null);
213 taxonRelationship
.setFromTaxon(null);
217 //Move descriptions to new taxon
218 List
<TaxonDescription
> descriptions
= new ArrayList
<TaxonDescription
>( oldTaxon
.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())
219 for(TaxonDescription description
: descriptions
){
220 String message
= "Description copied from former accepted taxon: %s (Old title: %s)";
221 message
= String
.format(message
, oldTaxon
.getTitleCache(), description
.getTitleCache());
222 description
.setTitleCache(message
, true);
223 //oldTaxon.removeDescription(description, false);
224 newAcceptedTaxon
.addDescription(description
);
226 oldTaxon
.clearDescriptions();
228 taxonService
.update(newAcceptedTaxon
);
229 TaxonDeletionConfigurator conf
= new TaxonDeletionConfigurator();
230 conf
.setDeleteSynonymsIfPossible(false);
231 DeleteResult result
= taxonService
.isDeletable(oldTaxon
, conf
);
232 // conf.setDeleteNameIfPossible(false);
235 result
= taxonService
.deleteTaxon(oldTaxon
, conf
, null);
237 TaxonNodeDeletionConfigurator config
= new TaxonNodeDeletionConfigurator();
238 config
.setDeleteTaxon(false);
239 conf
.setTaxonNodeConfig(config
);
240 result
= deleteTaxonNode(oldTaxonNode
, conf
);
243 //oldTaxonNode.delete();
244 return synonmyRelationship
.getSynonym();
249 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#makeTaxonNodeASynonymOfAnotherTaxonNode(java.util.UUID, java.util.UUID, java.util.UUID, java.util.UUID, java.lang.String)
252 @Transactional(readOnly
= false)
253 public UpdateResult
makeTaxonNodeASynonymOfAnotherTaxonNode(UUID oldTaxonNodeUuid
,
254 UUID newAcceptedTaxonNodeUUID
,
255 SynonymRelationshipType synonymRelationshipType
,
257 String citationMicroReference
) {
259 UpdateResult result
= new UpdateResult();
260 TaxonNode oldTaxonNode
= dao
.load(oldTaxonNodeUuid
);
261 TaxonNode oldTaxonParentNode
= oldTaxonNode
.getParent();
262 result
.addUpdatedObject(oldTaxonParentNode
);
263 TaxonNode newTaxonNode
= dao
.load(newAcceptedTaxonNodeUUID
);
264 result
.addUpdatedObject(newTaxonNode
);
265 makeTaxonNodeASynonymOfAnotherTaxonNode(oldTaxonNode
,
267 synonymRelationshipType
,
269 citationMicroReference
);
274 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNodes(java.util.List)
277 @Transactional(readOnly
= false)
278 public DeleteResult
deleteTaxonNodes(Set
<ITaxonTreeNode
> nodes
, TaxonDeletionConfigurator config
) {
281 config
= new TaxonDeletionConfigurator();
283 DeleteResult result
= new DeleteResult();
284 List
<UUID
> deletedUUIDs
= new ArrayList
<UUID
>();
285 Classification classification
= null;
286 for (ITaxonTreeNode treeNode
:nodes
){
287 if (treeNode
!= null){
288 if (treeNode
instanceof TaxonNode
){
290 taxonNode
= HibernateProxyHelper
.deproxy(treeNode
, TaxonNode
.class);
292 //check whether the node has children or the children are already deleted
293 if(taxonNode
.hasChildNodes()) {
294 Set
<ITaxonTreeNode
> children
= new HashSet
<ITaxonTreeNode
> ();
295 List
<TaxonNode
> childNodesList
= taxonNode
.getChildNodes();
296 children
.addAll(childNodesList
);
297 int compare
= config
.getTaxonNodeConfig().getChildHandling().compareTo(ChildHandling
.DELETE
);
298 boolean childHandling
= (compare
== 0)?
true: false;
300 boolean changeDeleteTaxon
= false;
301 if (!config
.getTaxonNodeConfig().isDeleteTaxon()){
302 config
.getTaxonNodeConfig().setDeleteTaxon(true);
303 changeDeleteTaxon
= true;
305 DeleteResult resultNodes
= deleteTaxonNodes(children
, config
);
306 if (!resultNodes
.isOk()){
307 result
.addExceptions(resultNodes
.getExceptions());
308 result
.setStatus(resultNodes
.getStatus());
310 if (changeDeleteTaxon
){
311 config
.getTaxonNodeConfig().setDeleteTaxon(false);
315 //move the children to the parent
316 TaxonNode parent
= taxonNode
.getParent();
317 for (TaxonNode child
: childNodesList
){
318 parent
.addChildNode(child
, child
.getReference(), child
.getMicroReference());
324 classification
= taxonNode
.getClassification();
326 if (classification
.getRootNode().equals(taxonNode
)){
327 classification
.removeRootNode();
328 classification
= null;
329 }else if (classification
.getChildNodes().contains(taxonNode
)){
330 Taxon taxon
= taxonNode
.getTaxon();
331 classification
.deleteChildNode(taxonNode
);
336 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
337 taxonService
.saveOrUpdate(taxon
);
338 saveOrUpdate(taxonNode
);
339 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
340 DeleteResult resultTaxon
= taxonService
.deleteTaxon(taxon
, configNew
, classification
);
341 if (!resultTaxon
.isOk()){
342 result
.addExceptions(resultTaxon
.getExceptions());
343 result
.setStatus(resultTaxon
.getStatus());
348 classification
= null;
351 classification
= null;
352 Taxon taxon
= taxonNode
.getTaxon();
355 taxonNode
.getTaxon().removeTaxonNode(taxonNode
);
356 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
357 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
358 saveOrUpdate(taxonNode
);
359 taxonService
.saveOrUpdate(taxon
);
360 DeleteResult resultTaxon
= taxonService
.deleteTaxon(taxon
, configNew
, classification
);
361 if (!resultTaxon
.isOk()){
362 result
.addExceptions(resultTaxon
.getExceptions());
363 result
.setStatus(resultTaxon
.getStatus());
370 result
.addUpdatedObject(taxonNode
.getParent());
371 UUID uuid
= dao
.delete(taxonNode
);
372 logger
.debug("Deleted node " +uuid
.toString());
374 classification
= (Classification
) treeNode
;
378 //deletedUUIDs.add(treeNode.getUuid());
382 if (classification
!= null){
383 result
.addUpdatedObject(classification
);
384 DeleteResult resultClassification
= classService
.delete(classification
);
385 if (!resultClassification
.isOk()){
386 result
.addExceptions(resultClassification
.getExceptions());
387 result
.setStatus(resultClassification
.getStatus());
395 @Transactional(readOnly
= false)
396 public DeleteResult
deleteTaxonNodes(Collection
<UUID
> nodeUuids
, TaxonDeletionConfigurator config
) {
397 Set
<ITaxonTreeNode
> nodes
= new HashSet
<ITaxonTreeNode
>();
398 for(UUID nodeUuid
: nodeUuids
) {
399 nodes
.add(dao
.load(nodeUuid
));
401 return deleteTaxonNodes(nodes
, config
);
405 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNode(java.util.List)
408 @Transactional(readOnly
= false)
409 public DeleteResult
deleteTaxonNode(TaxonNode node
, TaxonDeletionConfigurator config
) {
411 Taxon taxon
= (Taxon
)HibernateProxyHelper
.deproxy(node
.getTaxon());
412 TaxonNode parent
= node
.getParent();
414 config
= new TaxonDeletionConfigurator();
417 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
418 result
= taxonService
.deleteTaxon(taxon
, config
, node
.getClassification());
419 result
.addUpdatedObject(parent
);
423 result
= new DeleteResult();
424 boolean success
= taxon
.removeTaxonNode(node
);
426 result
.addUpdatedObject(parent
);
427 if (!dao
.delete(node
).equals(null)){
435 result
.addException(new Exception("The node can not be removed from the taxon."));
443 @Transactional(readOnly
= false)
444 public DeleteResult
deleteTaxonNode(UUID nodeUuid
, TaxonDeletionConfigurator config
) {
445 DeleteResult dr
= deleteTaxonNode(dao
.load(nodeUuid
), config
);
452 public UpdateResult
moveTaxonNode(TaxonNode taxonNode
, TaxonNode newParent
){
453 UpdateResult result
= new UpdateResult();
454 result
.addUpdatedObject(taxonNode
.getParent());
455 result
.addUpdatedObject(newParent
);
457 Reference reference
= taxonNode
.getReference();
458 String microReference
= taxonNode
.getMicroReference();
459 newParent
.addChildNode(taxonNode
, reference
, microReference
);
460 dao
.saveOrUpdate(taxonNode
);
466 @Transactional(readOnly
= false)
467 public UpdateResult
moveTaxonNode(UUID taxonNodeUuid
, UUID newParentTaxonNodeUuid
) {
468 return moveTaxonNode(dao
.load(taxonNodeUuid
), dao
.load(newParentTaxonNodeUuid
));
473 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#listAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification, int, int)
476 public List
<TaxonNode
> listAllNodesForClassification(Classification classification
, Integer start
, Integer end
) {
477 return dao
.getTaxonOfAcceptedTaxaByClassification(classification
, start
, end
);
481 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#countAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification)
484 public int countAllNodesForClassification(Classification classification
) {
485 return dao
.countTaxonOfAcceptedTaxaByClassification(classification
);