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
.Collections
;
15 import java
.util
.Comparator
;
16 import java
.util
.HashSet
;
17 import java
.util
.List
;
19 import java
.util
.UUID
;
21 import org
.apache
.log4j
.Logger
;
22 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
23 import org
.springframework
.stereotype
.Service
;
24 import org
.springframework
.transaction
.annotation
.Transactional
;
26 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
27 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonNodeDeletionConfigurator
;
28 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonNodeDeletionConfigurator
.ChildHandling
;
29 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
30 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
31 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
32 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
33 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
34 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
35 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonNodeComparator
;
36 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonTreeNode
;
37 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
38 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
39 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
40 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
41 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
42 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
43 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.IBeanInitializer
;
44 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonNodeDao
;
48 * @created Apr 9, 2010
52 @Transactional(readOnly
= true)
53 public class TaxonNodeServiceImpl
extends AnnotatableServiceBase
<TaxonNode
, ITaxonNodeDao
> implements ITaxonNodeService
{
54 private static final Logger logger
= Logger
.getLogger(TaxonNodeServiceImpl
.class);
57 private IBeanInitializer defaultBeanInitializer
;
59 private Comparator
<?
super TaxonNode
> taxonNodeComparator
;
62 private ITaxonService taxonService
;
65 private IClassificationService classService
;
68 public void setTaxonNodeComparator(ITaxonNodeComparator
<?
super TaxonNode
> taxonNodeComparator
){
69 this.taxonNodeComparator
= (Comparator
<?
super TaxonNode
>) taxonNodeComparator
;
73 public List
<TaxonNode
> loadChildNodesOfTaxonNode(TaxonNode taxonNode
,
74 List
<String
> propertyPaths
, boolean recursive
, boolean sorted
) {
75 taxonNode
= dao
.load(taxonNode
.getUuid());
76 List
<TaxonNode
> childNodes
;
77 if (recursive
== true){
78 childNodes
= dao
.listChildrenOf(taxonNode
, null, null, null, recursive
);
80 childNodes
= new ArrayList
<TaxonNode
>(taxonNode
.getChildNodes());
83 Collections
.sort(childNodes
, taxonNodeComparator
);
85 defaultBeanInitializer
.initializeAll(childNodes
, propertyPaths
);
90 * @see eu.etaxonomy.cdm.api.service.ServiceBase#setDao(eu.etaxonomy.cdm.persistence.dao.common.ICdmEntityDao)
94 protected void setDao(ITaxonNodeDao dao
) {
99 * @see eu.etaxonomy.cdm.api.service.ITaxonService#makeTaxonSynonym(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.Taxon)
102 @Transactional(readOnly
= false)
103 public Synonym
makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode
, TaxonNode newAcceptedTaxonNode
, SynonymRelationshipType synonymRelationshipType
, Reference citation
, String citationMicroReference
) {
105 // TODO at the moment this method only moves synonym-, concept relations and descriptions to the new accepted taxon
106 // in a future version we also want to move cdm data like annotations, marker, so., but we will need a policy for that
107 if (oldTaxonNode
== null || newAcceptedTaxonNode
== null || oldTaxonNode
.getTaxon().getName() == null){
108 throw new IllegalArgumentException("A mandatory parameter was null.");
111 if(oldTaxonNode
.equals(newAcceptedTaxonNode
)){
112 throw new IllegalArgumentException("Taxon can not be made synonym of its own.");
117 Taxon oldTaxon
= (Taxon
) HibernateProxyHelper
.deproxy(oldTaxonNode
.getTaxon());
118 Taxon newAcceptedTaxon
= (Taxon
) HibernateProxyHelper
.deproxy(newAcceptedTaxonNode
.getTaxon());
120 // Move oldTaxon to newTaxon
121 //TaxonNameBase<?,?> synonymName = oldTaxon.getName();
122 TaxonNameBase
<?
,?
> synonymName
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(oldTaxon
.getName());
123 HomotypicalGroup group
= synonymName
.getHomotypicalGroup();
124 if (synonymRelationshipType
== null){
125 if (synonymName
.isHomotypic(newAcceptedTaxon
.getName())){
126 synonymRelationshipType
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
128 synonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
132 //set homotypic group
133 HomotypicalGroup newAcceptedTaxonHomotypicalgroup
= newAcceptedTaxon
.getHomotypicGroup();
134 HibernateProxyHelper
.deproxy(newAcceptedTaxonHomotypicalgroup
);
135 HibernateProxyHelper
.deproxy(newAcceptedTaxon
.getName());
136 // Move Synonym Relations to new Taxon
137 SynonymRelationship synonmyRelationship
= newAcceptedTaxon
.addSynonymName(synonymName
,
138 synonymRelationshipType
, citation
, citationMicroReference
);
139 HomotypicalGroup homotypicalGroupAcceptedTaxon
= synonmyRelationship
.getSynonym().getHomotypicGroup();
140 // Move Synonym Relations to new Taxon
141 // From ticket 3163 we can move taxon with accepted name having homotypic synonyms
142 List
<Synonym
> synonymsInHomotypicalGroup
= null;
144 //the synonyms of the homotypical group of the old taxon
145 if (synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
146 synonymsInHomotypicalGroup
= oldTaxon
.getSynonymsInGroup(group
);
149 for(SynonymRelationship synRelation
: oldTaxon
.getSynonymRelations()){
150 SynonymRelationshipType srt
;
151 if(synRelation
.getSynonym().getName().getHomotypicalGroup()!= null
152 && synRelation
.getSynonym().getName().getHomotypicalGroup().equals(newAcceptedTaxon
.getName().getHomotypicalGroup())) {
153 srt
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
154 } else if(synRelation
.getType() != null && synRelation
.getType().equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())) {
155 if (synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
156 srt
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
158 srt
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
161 srt
= synRelation
.getType();
165 newAcceptedTaxon
.addSynonym(synRelation
.getSynonym(),
167 synRelation
.getCitation(),
168 synRelation
.getCitationMicroReference());
170 /*if (synonymsInHomotypicalGroup.contains(synRelation.getSynonym()) && srt.equals(SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF())){
171 homotypicalGroupAcceptedTaxon.addTypifiedName(synRelation.getSynonym().getName());
181 if(oldTaxonNode
.getChildNodes() != null && oldTaxonNode
.getChildNodes().size() != 0){
182 for(TaxonNode childNode
: oldTaxonNode
.getChildNodes()){
183 newAcceptedTaxonNode
.addChildNode(childNode
, childNode
.getReference(), childNode
.getMicroReference()); // childNode.getSynonymToBeUsed()
187 //Move Taxon RelationShips to new Taxon
188 Set
<TaxonRelationship
> obsoleteTaxonRelationships
= new HashSet
<TaxonRelationship
>();
189 for(TaxonRelationship taxonRelationship
: oldTaxon
.getTaxonRelations()){
190 Taxon fromTaxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxonRelationship
.getFromTaxon());
191 Taxon toTaxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxonRelationship
.getToTaxon());
192 if (fromTaxon
== oldTaxon
){
193 newAcceptedTaxon
.addTaxonRelation(taxonRelationship
.getToTaxon(), taxonRelationship
.getType(),
194 taxonRelationship
.getCitation(), taxonRelationship
.getCitationMicroReference());
196 }else if(toTaxon
== oldTaxon
){
197 fromTaxon
.addTaxonRelation(newAcceptedTaxon
, taxonRelationship
.getType(),
198 taxonRelationship
.getCitation(), taxonRelationship
.getCitationMicroReference());
199 taxonService
.saveOrUpdate(fromTaxon
);
202 logger
.warn("Taxon is not part of its own Taxonrelationship");
204 // Remove old relationships
206 fromTaxon
.removeTaxonRelation(taxonRelationship
);
207 toTaxon
.removeTaxonRelation(taxonRelationship
);
208 taxonRelationship
.setToTaxon(null);
209 taxonRelationship
.setFromTaxon(null);
213 //Move descriptions to new taxon
214 List
<TaxonDescription
> descriptions
= new ArrayList
<TaxonDescription
>( oldTaxon
.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())
215 for(TaxonDescription description
: descriptions
){
216 String message
= "Description copied from former accepted taxon: %s (Old title: %s)";
217 message
= String
.format(message
, oldTaxon
.getTitleCache(), description
.getTitleCache());
218 description
.setTitleCache(message
, true);
219 newAcceptedTaxon
.addDescription(description
);
221 taxonService
.update(newAcceptedTaxon
);
222 TaxonDeletionConfigurator conf
= new TaxonDeletionConfigurator();
223 conf
.setDeleteSynonymsIfPossible(false);
224 List
<String
> deleteMessages
= taxonService
.isDeletable(oldTaxon
, conf
);
225 // conf.setDeleteNameIfPossible(false);
227 if (deleteMessages
.isEmpty()){
228 result
= taxonService
.deleteTaxon(oldTaxon
, conf
, null);
230 TaxonNodeDeletionConfigurator config
= new TaxonNodeDeletionConfigurator();
231 config
.setDeleteTaxon(false);
232 conf
.setTaxonNodeConfig(config
);
233 result
= deleteTaxonNode(oldTaxonNode
, conf
);
236 //oldTaxonNode.delete();
237 return synonmyRelationship
.getSynonym();
241 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNodes(java.util.List)
244 @Transactional(readOnly
= false)
245 public DeleteResult
deleteTaxonNodes(Set
<ITaxonTreeNode
> nodes
, TaxonDeletionConfigurator config
) {
247 config
= new TaxonDeletionConfigurator();
249 DeleteResult result
= new DeleteResult();
250 List
<UUID
> deletedUUIDs
= new ArrayList
<UUID
>();
251 Classification classification
= null;
252 for (ITaxonTreeNode treeNode
:nodes
){
253 if (treeNode
!= null){
254 if (treeNode
instanceof TaxonNode
){
256 taxonNode
= HibernateProxyHelper
.deproxy(treeNode
, TaxonNode
.class);
258 //check whether the node has children or the children are already deleted
259 if(taxonNode
.hasChildNodes()){
260 Set
<ITaxonTreeNode
> children
= new HashSet
<ITaxonTreeNode
> ();
261 List
<TaxonNode
> childNodesList
= taxonNode
.getChildNodes();
262 children
.addAll(childNodesList
);
263 int compare
= config
.getTaxonNodeConfig().getChildHandling().compareTo(ChildHandling
.DELETE
);
264 boolean childHandling
= (compare
== 0)?
true: false;
266 boolean changeDeleteTaxon
= false;
267 if (!config
.getTaxonNodeConfig().isDeleteTaxon()){
268 config
.getTaxonNodeConfig().setDeleteTaxon(true);
269 changeDeleteTaxon
= true;
271 DeleteResult resultNodes
= deleteTaxonNodes(children
, config
);
272 if (!resultNodes
.isOk()){
273 result
.addExceptions(resultNodes
.getExceptions());
274 result
.setStatus(resultNodes
.getStatus());
276 if (changeDeleteTaxon
){
277 config
.getTaxonNodeConfig().setDeleteTaxon(false);
281 //move the children to the parent
282 TaxonNode parent
= taxonNode
.getParent();
283 for (TaxonNode child
: childNodesList
){
284 parent
.addChildNode(child
, child
.getReference(), child
.getMicroReference());
290 classification
= taxonNode
.getClassification();
292 if (classification
.getRootNode().equals(taxonNode
)){
293 classification
.removeRootNode();
294 classification
= null;
295 }else if (classification
.getChildNodes().contains(taxonNode
)){
296 Taxon taxon
= taxonNode
.getTaxon();
297 classification
.deleteChildNode(taxonNode
);
302 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
303 taxonService
.saveOrUpdate(taxon
);
304 saveOrUpdate(taxonNode
);
305 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
306 DeleteResult resultTaxon
= taxonService
.deleteTaxon(taxon
, configNew
, classification
);
307 if (!resultTaxon
.isOk()){
308 result
.addExceptions(resultTaxon
.getExceptions());
309 result
.setStatus(resultTaxon
.getStatus());
314 classification
= null;
317 classification
= null;
318 Taxon taxon
= taxonNode
.getTaxon();
321 taxonNode
.getTaxon().removeTaxonNode(taxonNode
);
322 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
323 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
324 saveOrUpdate(taxonNode
);
325 taxonService
.saveOrUpdate(taxon
);
326 DeleteResult resultTaxon
= taxonService
.deleteTaxon(taxon
, configNew
, classification
);
327 if (!resultTaxon
.isOk()){
328 result
.addExceptions(resultTaxon
.getExceptions());
329 result
.setStatus(resultTaxon
.getStatus());
336 UUID uuid
= dao
.delete(taxonNode
);
337 logger
.debug("Deleted node " +uuid
.toString());
339 classification
= (Classification
) treeNode
;
343 //deletedUUIDs.add(treeNode.getUuid());
347 if (classification
!= null){
348 DeleteResult resultClassification
= classService
.delete(classification
);
349 if (!resultClassification
.isOk()){
350 result
.addExceptions(resultClassification
.getExceptions());
351 result
.setStatus(resultClassification
.getStatus());
358 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNode(java.util.List)
361 @Transactional(readOnly
= false)
362 public DeleteResult
deleteTaxonNode(TaxonNode node
, TaxonDeletionConfigurator config
) {
363 Taxon taxon
= (Taxon
)HibernateProxyHelper
.deproxy(node
.getTaxon());
365 config
= new TaxonDeletionConfigurator();
367 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
368 return taxonService
.deleteTaxon(taxon
, config
, node
.getClassification());
370 DeleteResult result
= new DeleteResult();
371 boolean success
= taxon
.removeTaxonNode(node
);
373 if (!dao
.delete(node
).equals(null)){
381 result
.addException(new Exception("The node can not be removed from the taxon."));
390 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#listAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification, int, int)
393 public List
<TaxonNode
> listAllNodesForClassification(Classification classification
, Integer start
, Integer end
) {
394 return dao
.getTaxonOfAcceptedTaxaByClassification(classification
, start
, end
);
398 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#countAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification)
401 public int countAllNodesForClassification(Classification classification
) {
402 return dao
.countTaxonOfAcceptedTaxaByClassification(classification
);