(no commit message)
[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 List<UUID> deleteTaxonNodes(Set<ITaxonTreeNode> nodes, TaxonDeletionConfigurator config) {
246 if (config == null){
247 config = new TaxonDeletionConfigurator();
248 }
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){
254 TaxonNode taxonNode;
255 taxonNode = HibernateProxyHelper.deproxy(treeNode, TaxonNode.class);
256
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;
264 if (childHandling){
265 boolean changeDeleteTaxon = false;
266 if (!config.getTaxonNodeConfig().isDeleteTaxon()){
267 config.getTaxonNodeConfig().setDeleteTaxon(true);
268 changeDeleteTaxon = true;
269 }
270 deleteTaxonNodes(children, config);
271 if (changeDeleteTaxon){
272 config.getTaxonNodeConfig().setDeleteTaxon(false);
273 }
274
275 } else {
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());
280 }
281
282 }
283 }
284
285 classification = taxonNode.getClassification();
286
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);
293 //node is rootNode
294 if (taxon != null){
295
296 if (config.getTaxonNodeConfig().isDeleteTaxon()){
297 TaxonDeletionConfigurator configNew = new TaxonDeletionConfigurator();
298 configNew.setDeleteTaxonNodes(false);
299 taxonService.deleteTaxon(taxon, configNew, classification);
300 }
301 }
302 classification = null;
303
304 }else {
305 classification = null;
306 Taxon taxon = taxonNode.getTaxon();
307 //node is rootNode
308 if (taxon != null){
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);
314 }
315 }
316
317 }
318
319 UUID uuid = dao.delete(taxonNode);
320 logger.debug("Deleted node " +uuid.toString());
321 }else {
322 classification = (Classification) treeNode;
323
324 }
325
326 deletedUUIDs.add(treeNode.getUuid());
327
328 }
329 }
330 if (classification != null){
331 classService.delete(classification);
332 }
333 return deletedUUIDs;
334
335 }
336 /* (non-Javadoc)
337 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNode(java.util.List)
338 */
339 @Override
340 @Transactional(readOnly = false)
341 public DeleteResult deleteTaxonNode(TaxonNode node, TaxonDeletionConfigurator config) {
342 Taxon taxon = (Taxon)HibernateProxyHelper.deproxy(node.getTaxon());
343 if (config == null){
344 config = new TaxonDeletionConfigurator();
345 }
346 if (config.getTaxonNodeConfig().isDeleteTaxon()){
347 return taxonService.deleteTaxon(taxon, config, node.getClassification());
348 } else{
349 DeleteResult result = new DeleteResult();
350 boolean success = taxon.removeTaxonNode(node);
351 if (success){
352 if (!dao.delete(node).equals(null)){
353 return result;
354 } else {
355 result.setError();
356 return result;
357 }
358 }else{
359 result.setError();
360 result.addException(new Exception("The node can not be removed from the taxon."));
361 return result;
362 }
363
364 }
365
366 }
367
368 /* (non-Javadoc)
369 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#listAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification, int, int)
370 */
371 @Override
372 public List<TaxonNode> listAllNodesForClassification(Classification classification, Integer start, Integer end) {
373 return dao.getTaxonOfAcceptedTaxaByClassification(classification, start, end);
374 }
375
376 /* (non-Javadoc)
377 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#countAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification)
378 */
379 @Override
380 public int countAllNodesForClassification(Classification classification) {
381 return dao.countTaxonOfAcceptedTaxaByClassification(classification);
382 }
383
384
385
386 }