CdmCacherTest : ignoring for now since we have reverted to using termmaps on server...
[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.Collection;
15 import java.util.Collections;
16 import java.util.Comparator;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Set;
20 import java.util.UUID;
21
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;
26
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;
46
47 /**
48 * @author n.hoffmann
49 * @created Apr 9, 2010
50 * @version 1.0
51 */
52 @Service
53 @Transactional(readOnly = true)
54 public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITaxonNodeDao> implements ITaxonNodeService{
55 private static final Logger logger = Logger.getLogger(TaxonNodeServiceImpl.class);
56
57 @Autowired
58 private IBeanInitializer defaultBeanInitializer;
59
60 private Comparator<? super TaxonNode> taxonNodeComparator;
61
62 @Autowired
63 private ITaxonService taxonService;
64
65 @Autowired
66 private IClassificationService classService;
67
68 @Autowired
69 public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){
70 this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;
71 }
72
73 @Override
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);
80 }else{
81 childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
82 }
83 if (sorted){
84 Collections.sort(childNodes, taxonNodeComparator);
85 }
86 defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
87 return childNodes;
88 }
89
90 /* (non-Javadoc)
91 * @see eu.etaxonomy.cdm.api.service.ServiceBase#setDao(eu.etaxonomy.cdm.persistence.dao.common.ICdmEntityDao)
92 */
93 @Override
94 @Autowired
95 protected void setDao(ITaxonNodeDao dao) {
96 this.dao = dao;
97 }
98
99 /* (non-Javadoc)
100 * @see eu.etaxonomy.cdm.api.service.ITaxonService#makeTaxonSynonym(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.Taxon)
101 */
102 @Override
103 @Transactional(readOnly = false)
104 public Synonym makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode,
105 TaxonNode newAcceptedTaxonNode,
106 SynonymRelationshipType synonymRelationshipType,
107 Reference citation,
108 String citationMicroReference) {
109
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.");
114 }
115
116 if(oldTaxonNode.equals(newAcceptedTaxonNode)){
117 throw new IllegalArgumentException("Taxon can not be made synonym of its own.");
118 }
119
120
121
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();
131 }else{
132 synonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
133 }
134 }
135
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;
147
148 //the synonyms of the homotypical group of the old taxon
149 if (synonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){
150 synonymsInHomotypicalGroup = oldTaxon.getSynonymsInGroup(group);
151 }
152
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();
161 } else{
162 srt = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
163 }
164 } else {
165 srt = synRelation.getType();
166
167 }
168
169 newAcceptedTaxon.addSynonym(synRelation.getSynonym(),
170 srt,
171 synRelation.getCitation(),
172 synRelation.getCitationMicroReference());
173
174 /*if (synonymsInHomotypicalGroup.contains(synRelation.getSynonym()) && srt.equals(SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF())){
175 homotypicalGroupAcceptedTaxon.addTypifiedName(synRelation.getSynonym().getName());
176 }*/
177
178 }
179
180
181
182
183
184 // CHILD NODES
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()
188 }
189 }
190
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());
199
200 }else if(toTaxon == oldTaxon){
201 fromTaxon.addTaxonRelation(newAcceptedTaxon, taxonRelationship.getType(),
202 taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
203 taxonService.saveOrUpdate(fromTaxon);
204
205 }else{
206 logger.warn("Taxon is not part of its own Taxonrelationship");
207 }
208 // Remove old relationships
209
210 fromTaxon.removeTaxonRelation(taxonRelationship);
211 toTaxon.removeTaxonRelation(taxonRelationship);
212 taxonRelationship.setToTaxon(null);
213 taxonRelationship.setFromTaxon(null);
214 }
215
216
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);
225 }
226 oldTaxon.clearDescriptions();
227
228 taxonService.update(newAcceptedTaxon);
229 TaxonDeletionConfigurator conf = new TaxonDeletionConfigurator();
230 conf.setDeleteSynonymsIfPossible(false);
231 DeleteResult result = taxonService.isDeletable(oldTaxon, conf);
232 // conf.setDeleteNameIfPossible(false);
233
234 if (result.isOk()){
235 result = taxonService.deleteTaxon(oldTaxon, conf, null);
236 }else{
237 TaxonNodeDeletionConfigurator config = new TaxonNodeDeletionConfigurator();
238 config.setDeleteTaxon(false);
239 conf.setTaxonNodeConfig(config);
240 result = deleteTaxonNode(oldTaxonNode, conf);
241 }
242
243 //oldTaxonNode.delete();
244 return synonmyRelationship.getSynonym();
245 }
246
247
248 /* (non-Javadoc)
249 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#makeTaxonNodeASynonymOfAnotherTaxonNode(java.util.UUID, java.util.UUID, java.util.UUID, java.util.UUID, java.lang.String)
250 */
251 @Override
252 @Transactional(readOnly = false)
253 public UpdateResult makeTaxonNodeASynonymOfAnotherTaxonNode(UUID oldTaxonNodeUuid,
254 UUID newAcceptedTaxonNodeUUID,
255 SynonymRelationshipType synonymRelationshipType,
256 Reference citation,
257 String citationMicroReference) {
258
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,
266 newTaxonNode,
267 synonymRelationshipType,
268 citation,
269 citationMicroReference);
270 return result;
271 }
272
273 /* (non-Javadoc)
274 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNodes(java.util.List)
275 */
276 @Override
277 @Transactional(readOnly = false)
278 public DeleteResult deleteTaxonNodes(Set<ITaxonTreeNode> nodes, TaxonDeletionConfigurator config) {
279
280 if (config == null){
281 config = new TaxonDeletionConfigurator();
282 }
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){
289 TaxonNode taxonNode;
290 taxonNode = HibernateProxyHelper.deproxy(treeNode, TaxonNode.class);
291
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;
299 if (childHandling){
300 boolean changeDeleteTaxon = false;
301 if (!config.getTaxonNodeConfig().isDeleteTaxon()){
302 config.getTaxonNodeConfig().setDeleteTaxon(true);
303 changeDeleteTaxon = true;
304 }
305 DeleteResult resultNodes = deleteTaxonNodes(children, config);
306 if (!resultNodes.isOk()){
307 result.addExceptions(resultNodes.getExceptions());
308 result.setStatus(resultNodes.getStatus());
309 }
310 if (changeDeleteTaxon){
311 config.getTaxonNodeConfig().setDeleteTaxon(false);
312 }
313
314 } else {
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());
319 }
320
321 }
322 }
323
324 classification = taxonNode.getClassification();
325
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);
332
333 //node is rootNode
334 if (taxon != null){
335
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());
344 }
345
346 }
347 }
348 classification = null;
349
350 } else {
351 classification = null;
352 Taxon taxon = taxonNode.getTaxon();
353 //node is rootNode
354 if (taxon != null){
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());
364 }
365 }
366 }
367
368 }
369
370 result.addUpdatedObject(taxonNode.getParent());
371 UUID uuid = dao.delete(taxonNode);
372 logger.debug("Deleted node " +uuid.toString());
373 }else {
374 classification = (Classification) treeNode;
375
376 }
377
378 //deletedUUIDs.add(treeNode.getUuid());
379
380 }
381 }
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());
388 }
389 }
390 return result;
391
392 }
393
394 @Override
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));
400 }
401 return deleteTaxonNodes(nodes, config);
402 }
403
404 /* (non-Javadoc)
405 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNode(java.util.List)
406 */
407 @Override
408 @Transactional(readOnly = false)
409 public DeleteResult deleteTaxonNode(TaxonNode node, TaxonDeletionConfigurator config) {
410
411 Taxon taxon = (Taxon)HibernateProxyHelper.deproxy(node.getTaxon());
412 TaxonNode parent = node.getParent();
413 if (config == null){
414 config = new TaxonDeletionConfigurator();
415 }
416 DeleteResult result;
417 if (config.getTaxonNodeConfig().isDeleteTaxon()){
418 result = taxonService.deleteTaxon(taxon, config, node.getClassification());
419 result.addUpdatedObject(parent);
420
421 return result;
422 } else{
423 result = new DeleteResult();
424 boolean success = taxon.removeTaxonNode(node);
425 if (success) {
426 result.addUpdatedObject(parent);
427 if (!dao.delete(node).equals(null)){
428 return result;
429 } else {
430 result.setError();
431 return result;
432 }
433 } else {
434 result.setError();
435 result.addException(new Exception("The node can not be removed from the taxon."));
436 return result;
437 }
438 }
439 }
440
441
442 @Override
443 @Transactional(readOnly = false)
444 public DeleteResult deleteTaxonNode(UUID nodeUuid, TaxonDeletionConfigurator config) {
445 DeleteResult dr = deleteTaxonNode(dao.load(nodeUuid), config);
446 return dr;
447 }
448
449
450 @Override
451 @Transactional
452 public UpdateResult moveTaxonNode(TaxonNode taxonNode, TaxonNode newParent){
453 UpdateResult result = new UpdateResult();
454 result.addUpdatedObject(taxonNode.getParent());
455 result.addUpdatedObject(newParent);
456
457 Reference reference = taxonNode.getReference();
458 String microReference = taxonNode.getMicroReference();
459 newParent.addChildNode(taxonNode, reference, microReference);
460 dao.saveOrUpdate(taxonNode);
461
462 return result;
463 }
464
465 @Override
466 @Transactional(readOnly = false)
467 public UpdateResult moveTaxonNode(UUID taxonNodeUuid, UUID newParentTaxonNodeUuid) {
468 return moveTaxonNode(dao.load(taxonNodeUuid), dao.load(newParentTaxonNodeUuid));
469
470 }
471
472 /* (non-Javadoc)
473 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#listAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification, int, int)
474 */
475 @Override
476 public List<TaxonNode> listAllNodesForClassification(Classification classification, Integer start, Integer end) {
477 return dao.getTaxonOfAcceptedTaxaByClassification(classification, start, end);
478 }
479
480 /* (non-Javadoc)
481 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#countAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification)
482 */
483 @Override
484 public int countAllNodesForClassification(Classification classification) {
485 return dao.countTaxonOfAcceptedTaxaByClassification(classification);
486 }
487
488
489
490
491
492 }