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
.api
.service
.exception
.DataChangeNoRollbackException
;
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 TaxonNode
getTaxonNodeByUuid(UUID uuid
) {
75 return dao
.findByUuid(uuid
);
79 public List
<TaxonNode
> loadChildNodesOfTaxonNode(TaxonNode taxonNode
,
80 List
<String
> propertyPaths
, boolean recursive
) {
81 taxonNode
= dao
.load(taxonNode
.getUuid());
82 List
<TaxonNode
> childNodes
;
83 if (recursive
== true){
84 childNodes
= dao
.listChildrenOf(taxonNode
, null, null, null, recursive
);
86 childNodes
= new ArrayList
<TaxonNode
>(taxonNode
.getChildNodes());
88 Collections
.sort(childNodes
, taxonNodeComparator
);
89 defaultBeanInitializer
.initializeAll(childNodes
, propertyPaths
);
94 * @see eu.etaxonomy.cdm.api.service.ServiceBase#setDao(eu.etaxonomy.cdm.persistence.dao.common.ICdmEntityDao)
98 protected void setDao(ITaxonNodeDao dao
) {
103 * @see eu.etaxonomy.cdm.api.service.ITaxonService#makeTaxonSynonym(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.Taxon)
106 @Transactional(readOnly
= false)
107 public Synonym
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 Taxon oldTaxon
= (Taxon
) HibernateProxyHelper
.deproxy(oldTaxonNode
.getTaxon());
122 Taxon newAcceptedTaxon
= (Taxon
) HibernateProxyHelper
.deproxy(newAcceptedTaxonNode
.getTaxon());
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 newAcceptedTaxon
.addDescription(description
);
225 taxonService
.update(newAcceptedTaxon
);
226 TaxonDeletionConfigurator conf
= new TaxonDeletionConfigurator();
227 conf
.setDeleteSynonymsIfPossible(false);
228 List
<String
> deleteMessages
= taxonService
.isDeletable(oldTaxon
, conf
);
229 // conf.setDeleteNameIfPossible(false);
230 if (deleteMessages
.isEmpty()){
231 String uuidString
= taxonService
.deleteTaxon(oldTaxon
, conf
, null);
232 logger
.debug(uuidString
);
234 TaxonNodeDeletionConfigurator config
= new TaxonNodeDeletionConfigurator();
235 config
.setDeleteTaxon(false);
236 conf
.setTaxonNodeConfig(config
);
237 deleteTaxonNode(oldTaxonNode
, conf
);
240 //oldTaxonNode.delete();
241 return synonmyRelationship
.getSynonym();
245 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNodes(java.util.List)
248 @Transactional(readOnly
= false)
249 public List
<UUID
> deleteTaxonNodes(Set
<ITaxonTreeNode
> nodes
, TaxonDeletionConfigurator config
) {
251 config
= new TaxonDeletionConfigurator();
253 List
<UUID
> deletedUUIDs
= new ArrayList
<UUID
>();
254 Classification classification
= null;
255 for (ITaxonTreeNode treeNode
:nodes
){
256 if (treeNode
!= null){
257 if (treeNode
instanceof TaxonNode
){
259 taxonNode
= HibernateProxyHelper
.deproxy(treeNode
, TaxonNode
.class);
261 //check whether the node has children or the children are already deleted
262 if(taxonNode
.hasChildNodes()){
263 Set
<ITaxonTreeNode
> children
= new HashSet
<ITaxonTreeNode
> ();
264 List
<TaxonNode
> childNodesList
= taxonNode
.getChildNodes();
265 children
.addAll(childNodesList
);
266 int compare
= config
.getTaxonNodeConfig().getChildHandling().compareTo(ChildHandling
.DELETE
);
267 boolean childHandling
= (compare
== 0)?
true: false;
269 boolean changeDeleteTaxon
= false;
270 if (!config
.getTaxonNodeConfig().isDeleteTaxon()){
271 config
.getTaxonNodeConfig().setDeleteTaxon(true);
272 changeDeleteTaxon
= true;
274 deleteTaxonNodes(children
, config
);
275 if (changeDeleteTaxon
){
276 config
.getTaxonNodeConfig().setDeleteTaxon(false);
280 //move the children to the parent
281 TaxonNode parent
= taxonNode
.getParent();
282 for (TaxonNode child
: childNodesList
){
283 parent
.addChildNode(child
, child
.getReference(), child
.getMicroReference());
289 classification
= taxonNode
.getClassification();
291 if (classification
.getRootNode().equals(taxonNode
)){
292 classification
.removeRootNode();
293 classification
= null;
294 }else if (classification
.getChildNodes().contains(taxonNode
)){
295 Taxon taxon
= taxonNode
.getTaxon();
296 classification
.deleteChildNode(taxonNode
);
300 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
301 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
302 configNew
.setDeleteTaxonNodes(false);
303 taxonService
.deleteTaxon(taxon
, configNew
, classification
);
306 classification
= null;
309 classification
= null;
310 Taxon taxon
= taxonNode
.getTaxon();
313 taxonNode
.getTaxon().removeTaxonNode(taxonNode
);
314 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
315 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
316 configNew
.setDeleteTaxonNodes(false);
317 taxonService
.deleteTaxon(taxon
, configNew
, classification
);
323 UUID uuid
= dao
.delete(taxonNode
);
324 logger
.debug("Deleted node " +uuid
.toString());
326 classification
= (Classification
) treeNode
;
330 deletedUUIDs
.add(treeNode
.getUuid());
334 if (classification
!= null){
335 classService
.delete(classification
);
341 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNode(java.util.List)
344 @Transactional(readOnly
= false)
345 public String
deleteTaxonNode(TaxonNode node
, TaxonDeletionConfigurator config
) {
346 Taxon taxon
= (Taxon
)HibernateProxyHelper
.deproxy(node
.getTaxon());
348 config
= new TaxonDeletionConfigurator();
350 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
351 return taxonService
.deleteTaxon(taxon
, config
, node
.getClassification());
353 taxon
.removeTaxonNode(node
);
355 return node
.getUuid().toString();
361 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#listAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification, int, int)
364 public List
<TaxonNode
> listAllNodesForClassification(Classification classification
, Integer start
, Integer end
) {
365 return dao
.getTaxonOfAcceptedTaxaByClassification(classification
, start
, end
);
369 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#countAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification)
372 public int countAllNodesForClassification(Classification classification
) {
373 return dao
.countTaxonOfAcceptedTaxaByClassification(classification
);