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);
226 if (deleteMessages
.isEmpty()){
227 String uuidString
= taxonService
.deleteTaxon(oldTaxon
, conf
, null);
228 logger
.debug(uuidString
);
230 TaxonNodeDeletionConfigurator config
= new TaxonNodeDeletionConfigurator();
231 config
.setDeleteTaxon(false);
232 conf
.setTaxonNodeConfig(config
);
233 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 List
<UUID
> deleteTaxonNodes(Set
<ITaxonTreeNode
> nodes
, TaxonDeletionConfigurator config
) {
247 config
= new TaxonDeletionConfigurator();
249 List
<UUID
> deletedUUIDs
= new ArrayList
<UUID
>();
250 Classification classification
= null;
251 for (ITaxonTreeNode treeNode
:nodes
){
252 if (treeNode
!= null){
253 if (treeNode
instanceof TaxonNode
){
255 taxonNode
= HibernateProxyHelper
.deproxy(treeNode
, TaxonNode
.class);
257 //check whether the node has children or the children are already deleted
258 if(taxonNode
.hasChildNodes()){
259 Set
<ITaxonTreeNode
> children
= new HashSet
<ITaxonTreeNode
> ();
260 List
<TaxonNode
> childNodesList
= taxonNode
.getChildNodes();
261 children
.addAll(childNodesList
);
262 int compare
= config
.getTaxonNodeConfig().getChildHandling().compareTo(ChildHandling
.DELETE
);
263 boolean childHandling
= (compare
== 0)?
true: false;
265 boolean changeDeleteTaxon
= false;
266 if (!config
.getTaxonNodeConfig().isDeleteTaxon()){
267 config
.getTaxonNodeConfig().setDeleteTaxon(true);
268 changeDeleteTaxon
= true;
270 deleteTaxonNodes(children
, config
);
271 if (changeDeleteTaxon
){
272 config
.getTaxonNodeConfig().setDeleteTaxon(false);
276 //move the children to the parent
277 TaxonNode parent
= taxonNode
.getParent();
278 for (TaxonNode child
: childNodesList
){
279 parent
.addChildNode(child
, child
.getReference(), child
.getMicroReference());
285 classification
= taxonNode
.getClassification();
287 if (classification
.getRootNode().equals(taxonNode
)){
288 classification
.removeRootNode();
289 classification
= null;
290 }else if (classification
.getChildNodes().contains(taxonNode
)){
291 Taxon taxon
= taxonNode
.getTaxon();
292 classification
.deleteChildNode(taxonNode
);
296 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
297 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
298 configNew
.setDeleteTaxonNodes(false);
299 taxonService
.deleteTaxon(taxon
, configNew
, classification
);
302 classification
= null;
305 classification
= null;
306 Taxon taxon
= taxonNode
.getTaxon();
309 taxonNode
.getTaxon().removeTaxonNode(taxonNode
);
310 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
311 TaxonDeletionConfigurator configNew
= new TaxonDeletionConfigurator();
312 configNew
.setDeleteTaxonNodes(false);
313 taxonService
.deleteTaxon(taxon
, configNew
, classification
);
319 UUID uuid
= dao
.delete(taxonNode
);
320 logger
.debug("Deleted node " +uuid
.toString());
322 classification
= (Classification
) treeNode
;
326 deletedUUIDs
.add(treeNode
.getUuid());
330 if (classification
!= null){
331 classService
.delete(classification
);
337 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNode(java.util.List)
340 @Transactional(readOnly
= false)
341 public String
deleteTaxonNode(TaxonNode node
, TaxonDeletionConfigurator config
) {
342 Taxon taxon
= (Taxon
)HibernateProxyHelper
.deproxy(node
.getTaxon());
344 config
= new TaxonDeletionConfigurator();
346 if (config
.getTaxonNodeConfig().isDeleteTaxon()){
347 return taxonService
.deleteTaxon(taxon
, config
, node
.getClassification());
349 taxon
.removeTaxonNode(node
);
351 return node
.getUuid().toString();
357 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#listAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification, int, int)
360 public List
<TaxonNode
> listAllNodesForClassification(Classification classification
, Integer start
, Integer end
) {
361 return dao
.getTaxonOfAcceptedTaxaByClassification(classification
, start
, end
);
365 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#countAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification)
368 public int countAllNodesForClassification(Classification classification
) {
369 return dao
.countTaxonOfAcceptedTaxaByClassification(classification
);