further fixes for subareas #4220
[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.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;
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 TaxonNode getTaxonNodeByUuid(UUID uuid) {
75 return dao.findByUuid(uuid);
76 }
77
78 @Override
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);
85 }else{
86 childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
87 }
88 Collections.sort(childNodes, taxonNodeComparator);
89 defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
90 return childNodes;
91 }
92
93 /* (non-Javadoc)
94 * @see eu.etaxonomy.cdm.api.service.ServiceBase#setDao(eu.etaxonomy.cdm.persistence.dao.common.ICdmEntityDao)
95 */
96 @Override
97 @Autowired
98 protected void setDao(ITaxonNodeDao dao) {
99 this.dao = dao;
100 }
101
102 /* (non-Javadoc)
103 * @see eu.etaxonomy.cdm.api.service.ITaxonService#makeTaxonSynonym(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.Taxon)
104 */
105 @Override
106 @Transactional(readOnly = false)
107 public Synonym makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode, TaxonNode newAcceptedTaxonNode, SynonymRelationshipType synonymRelationshipType, Reference citation, String citationMicroReference) {
108
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.");
113 }
114
115 if(oldTaxonNode.equals(newAcceptedTaxonNode)){
116 throw new IllegalArgumentException("Taxon can not be made synonym of its own.");
117 }
118
119
120
121 Taxon oldTaxon = (Taxon) HibernateProxyHelper.deproxy(oldTaxonNode.getTaxon());
122 Taxon newAcceptedTaxon = (Taxon) HibernateProxyHelper.deproxy(newAcceptedTaxonNode.getTaxon());
123
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 newAcceptedTaxon.addDescription(description);
224 }
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);
233 }else{
234 TaxonNodeDeletionConfigurator config = new TaxonNodeDeletionConfigurator();
235 config.setDeleteTaxon(false);
236 conf.setTaxonNodeConfig(config);
237 deleteTaxonNode(oldTaxonNode, conf);
238 }
239
240 //oldTaxonNode.delete();
241 return synonmyRelationship.getSynonym();
242 }
243
244 /* (non-Javadoc)
245 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNodes(java.util.List)
246 */
247 @Override
248 @Transactional(readOnly = false)
249 public List<UUID> deleteTaxonNodes(Set<ITaxonTreeNode> nodes, TaxonDeletionConfigurator config) {
250 if (config == null){
251 config = new TaxonDeletionConfigurator();
252 }
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){
258 TaxonNode taxonNode;
259 taxonNode = HibernateProxyHelper.deproxy(treeNode, TaxonNode.class);
260
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;
268 if (childHandling){
269 boolean changeDeleteTaxon = false;
270 if (!config.getTaxonNodeConfig().isDeleteTaxon()){
271 config.getTaxonNodeConfig().setDeleteTaxon(true);
272 changeDeleteTaxon = true;
273 }
274 deleteTaxonNodes(children, config);
275 if (changeDeleteTaxon){
276 config.getTaxonNodeConfig().setDeleteTaxon(false);
277 }
278
279 } else {
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());
284 }
285
286 }
287 }
288
289 classification = taxonNode.getClassification();
290
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);
297 //node is rootNode
298 if (taxon != null){
299
300 if (config.getTaxonNodeConfig().isDeleteTaxon()){
301 TaxonDeletionConfigurator configNew = new TaxonDeletionConfigurator();
302 configNew.setDeleteTaxonNodes(false);
303 taxonService.deleteTaxon(taxon, configNew, classification);
304 }
305 }
306 classification = null;
307
308 }else {
309 classification = null;
310 Taxon taxon = taxonNode.getTaxon();
311 //node is rootNode
312 if (taxon != null){
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);
318 }
319 }
320
321 }
322
323 UUID uuid = dao.delete(taxonNode);
324 logger.debug("Deleted node " +uuid.toString());
325 }else {
326 classification = (Classification) treeNode;
327
328 }
329
330 deletedUUIDs.add(treeNode.getUuid());
331
332 }
333 }
334 if (classification != null){
335 classService.delete(classification);
336 }
337 return deletedUUIDs;
338
339 }
340 /* (non-Javadoc)
341 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNode(java.util.List)
342 */
343 @Override
344 @Transactional(readOnly = false)
345 public String deleteTaxonNode(TaxonNode node, TaxonDeletionConfigurator config) {
346 Taxon taxon = (Taxon)HibernateProxyHelper.deproxy(node.getTaxon());
347 if (config == null){
348 config = new TaxonDeletionConfigurator();
349 }
350 if (config.getTaxonNodeConfig().isDeleteTaxon()){
351 return taxonService.deleteTaxon(taxon, config, node.getClassification());
352 } else{
353 taxon.removeTaxonNode(node);
354 dao.delete(node);
355 return node.getUuid().toString();
356 }
357
358 }
359
360 /* (non-Javadoc)
361 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#listAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification, int, int)
362 */
363 @Override
364 public List<TaxonNode> listAllNodesForClassification(Classification classification, Integer start, Integer end) {
365 return dao.getTaxonOfAcceptedTaxaByClassification(classification, start, end);
366 }
367
368 /* (non-Javadoc)
369 * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#countAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification)
370 */
371 @Override
372 public int countAllNodesForClassification(Classification classification) {
373 return dao.countTaxonOfAcceptedTaxaByClassification(classification);
374 }
375
376
377
378 }