- added service method deleteDerivateHierarchy()
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TaxonNodeServiceImpl.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
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.
9 */
10
11 package eu.etaxonomy.cdm.api.service;
12
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;
18 import java.util.Set;
19 import java.util.UUID;
20
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;
25
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;
45
46 /**
47 * @author n.hoffmann
48 * @created Apr 9, 2010
49 * @version 1.0
50 */
51 @Service
52 @Transactional(readOnly = true)
53 public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITaxonNodeDao> implements ITaxonNodeService{
54 private static final Logger logger = Logger.getLogger(TaxonNodeServiceImpl.class);
55
56 @Autowired
57 private IBeanInitializer defaultBeanInitializer;
58
59 private Comparator<? super TaxonNode> taxonNodeComparator;
60
61 @Autowired
62 private ITaxonService taxonService;
63
64 @Autowired
65 private IClassificationService classService;
66
67 @Autowired
68 public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){
69 this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;
70 }
71
72 @Override
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);
79 }else{
80 childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
81 }
82 if (sorted){
83 Collections.sort(childNodes, taxonNodeComparator);
84 }
85 defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
86 return childNodes;
87 }
88
89 /* (non-Javadoc)
90 * @see eu.etaxonomy.cdm.api.service.ServiceBase#setDao(eu.etaxonomy.cdm.persistence.dao.common.ICdmEntityDao)
91 */
92 @Override
93 @Autowired
94 protected void setDao(ITaxonNodeDao dao) {
95 this.dao = dao;
96 }
97
98 /* (non-Javadoc)
99 * @see eu.etaxonomy.cdm.api.service.ITaxonService#makeTaxonSynonym(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.Taxon)
100 */
101 @Override
102 @Transactional(readOnly = false)
103 public Synonym makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode, TaxonNode newAcceptedTaxonNode, SynonymRelationshipType synonymRelationshipType, Reference citation, String citationMicroReference) {
104
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.");
109 }
110
111 if(oldTaxonNode.equals(newAcceptedTaxonNode)){
112 throw new IllegalArgumentException("Taxon can not be made synonym of its own.");
113 }
114
115
116
117 Taxon oldTaxon = (Taxon) HibernateProxyHelper.deproxy(oldTaxonNode.getTaxon());
118 Taxon newAcceptedTaxon = (Taxon) HibernateProxyHelper.deproxy(newAcceptedTaxonNode.getTaxon());
119
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();
127 }else{
128 synonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
129 }
130 }
131
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;
143
144 //the synonyms of the homotypical group of the old taxon
145 if (synonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){
146 synonymsInHomotypicalGroup = oldTaxon.getSynonymsInGroup(group);
147 }
148
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();
157 } else{
158 srt = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
159 }
160 } else {
161 srt = synRelation.getType();
162
163 }
164
165 newAcceptedTaxon.addSynonym(synRelation.getSynonym(),
166 srt,
167 synRelation.getCitation(),
168 synRelation.getCitationMicroReference());
169
170 /*if (synonymsInHomotypicalGroup.contains(synRelation.getSynonym()) && srt.equals(SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF())){
171 homotypicalGroupAcceptedTaxon.addTypifiedName(synRelation.getSynonym().getName());
172 }*/
173
174 }
175
176
177
178
179
180 // CHILD NODES
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()
184 }
185 }
186
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());
195
196 }else if(toTaxon == oldTaxon){
197 fromTaxon.addTaxonRelation(newAcceptedTaxon, taxonRelationship.getType(),
198 taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
199 taxonService.saveOrUpdate(fromTaxon);
200
201 }else{
202 logger.warn("Taxon is not part of its own Taxonrelationship");
203 }
204 // Remove old relationships
205
206 fromTaxon.removeTaxonRelation(taxonRelationship);
207 toTaxon.removeTaxonRelation(taxonRelationship);
208 taxonRelationship.setToTaxon(null);
209 taxonRelationship.setFromTaxon(null);
210 }
211
212
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);
220 }
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 DeleteResult result;
227 if (deleteMessages.isEmpty()){
228 result = taxonService.deleteTaxon(oldTaxon, conf, null);
229 }else{
230 TaxonNodeDeletionConfigurator config = new TaxonNodeDeletionConfigurator();
231 config.setDeleteTaxon(false);
232 conf.setTaxonNodeConfig(config);
233 result = deleteTaxonNode(oldTaxonNode, conf);
234 }
235
236 //oldTaxonNode.delete();
237 return synonmyRelationship.getSynonym();
238 }
239
240 /* (non-Javadoc)
241 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNodes(java.util.List)
242 */
243 @Override
244 @Transactional(readOnly = false)
245 public DeleteResult deleteTaxonNodes(Set<ITaxonTreeNode> nodes, TaxonDeletionConfigurator config) {
246 if (config == null){
247 config = new TaxonDeletionConfigurator();
248 }
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){
255 TaxonNode taxonNode;
256 taxonNode = HibernateProxyHelper.deproxy(treeNode, TaxonNode.class);
257
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;
265 if (childHandling){
266 boolean changeDeleteTaxon = false;
267 if (!config.getTaxonNodeConfig().isDeleteTaxon()){
268 config.getTaxonNodeConfig().setDeleteTaxon(true);
269 changeDeleteTaxon = true;
270 }
271 DeleteResult resultNodes = deleteTaxonNodes(children, config);
272 if (!resultNodes.isOk()){
273 result.addExceptions(resultNodes.getExceptions());
274 result.setStatus(resultNodes.getStatus());
275 }
276 if (changeDeleteTaxon){
277 config.getTaxonNodeConfig().setDeleteTaxon(false);
278 }
279
280 } else {
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());
285 }
286
287 }
288 }
289
290 classification = taxonNode.getClassification();
291
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);
298
299 //node is rootNode
300 if (taxon != null){
301
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());
310 }
311
312 }
313 }
314 classification = null;
315
316 }else {
317 classification = null;
318 Taxon taxon = taxonNode.getTaxon();
319 //node is rootNode
320 if (taxon != null){
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());
330 }
331 }
332 }
333
334 }
335
336 UUID uuid = dao.delete(taxonNode);
337 logger.debug("Deleted node " +uuid.toString());
338 }else {
339 classification = (Classification) treeNode;
340
341 }
342
343 //deletedUUIDs.add(treeNode.getUuid());
344
345 }
346 }
347 if (classification != null){
348 DeleteResult resultClassification = classService.delete(classification);
349 if (!resultClassification.isOk()){
350 result.addExceptions(resultClassification.getExceptions());
351 result.setStatus(resultClassification.getStatus());
352 }
353 }
354 return result;
355
356 }
357 /* (non-Javadoc)
358 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNode(java.util.List)
359 */
360 @Override
361 @Transactional(readOnly = false)
362 public DeleteResult deleteTaxonNode(TaxonNode node, TaxonDeletionConfigurator config) {
363 Taxon taxon = (Taxon)HibernateProxyHelper.deproxy(node.getTaxon());
364 if (config == null){
365 config = new TaxonDeletionConfigurator();
366 }
367 if (config.getTaxonNodeConfig().isDeleteTaxon()){
368 return taxonService.deleteTaxon(taxon, config, node.getClassification());
369 } else{
370 DeleteResult result = new DeleteResult();
371 boolean success = taxon.removeTaxonNode(node);
372 if (success){
373 if (!dao.delete(node).equals(null)){
374 return result;
375 } else {
376 result.setError();
377 return result;
378 }
379 }else{
380 result.setError();
381 result.addException(new Exception("The node can not be removed from the taxon."));
382 return result;
383 }
384
385 }
386
387 }
388
389 /* (non-Javadoc)
390 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#listAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification, int, int)
391 */
392 @Override
393 public List<TaxonNode> listAllNodesForClassification(Classification classification, Integer start, Integer end) {
394 return dao.getTaxonOfAcceptedTaxaByClassification(classification, start, end);
395 }
396
397 /* (non-Javadoc)
398 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#countAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification)
399 */
400 @Override
401 public int countAllNodesForClassification(Classification classification) {
402 return dao.countTaxonOfAcceptedTaxaByClassification(classification);
403 }
404
405
406
407 }