Project

General

Profile

Download (23.3 KB) Statistics
| Branch: | Tag: | Revision:
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.HashMap;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.TreeMap;
21
import java.util.UUID;
22

    
23
import org.apache.commons.collections.CollectionUtils;
24
import org.apache.log4j.Logger;
25
import org.springframework.beans.factory.annotation.Autowired;
26
import org.springframework.stereotype.Service;
27
import org.springframework.transaction.annotation.Transactional;
28

    
29
import eu.etaxonomy.cdm.api.service.config.CreateHierarchyForClassificationConfigurator;
30
import eu.etaxonomy.cdm.api.service.pager.Pager;
31
import eu.etaxonomy.cdm.api.service.pager.PagerUtils;
32
import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
33
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
34
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
35
import eu.etaxonomy.cdm.model.common.CdmBase;
36
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
37
import eu.etaxonomy.cdm.model.description.TaxonDescription;
38
import eu.etaxonomy.cdm.model.media.Media;
39
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
40
import eu.etaxonomy.cdm.model.media.MediaUtils;
41
import eu.etaxonomy.cdm.model.name.NonViralName;
42
import eu.etaxonomy.cdm.model.name.Rank;
43
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
44
import eu.etaxonomy.cdm.model.taxon.Classification;
45
import eu.etaxonomy.cdm.model.taxon.ITaxonNodeComparator;
46
import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
47
import eu.etaxonomy.cdm.model.taxon.Taxon;
48
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
49
import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
50
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
51
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
52
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
53
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
54
import eu.etaxonomy.cdm.persistence.query.OrderHint;
55
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
56
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
57

    
58
/**
59
 * @author n.hoffmann
60
 * @created Sep 21, 2009
61
 */
62
@Service
63
@Transactional(readOnly = true)
64
public class ClassificationServiceImpl extends IdentifiableServiceBase<Classification, IClassificationDao>
65
    implements IClassificationService {
66
    private static final Logger logger = Logger.getLogger(ClassificationServiceImpl.class);
67

    
68
    @Autowired
69
    private ITaxonNodeDao taxonNodeDao;
70

    
71
    @Autowired
72
    private ITaxonDao taxonDao;
73

    
74
    @Autowired
75
    private IBeanInitializer defaultBeanInitializer;
76

    
77
    @Override
78
    @Autowired
79
    protected void setDao(IClassificationDao dao) {
80
        this.dao = dao;
81
    }
82

    
83
    private Comparator<? super TaxonNode> taxonNodeComparator;
84

    
85
    @Autowired
86
    public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){
87
        this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;
88
    }
89

    
90
    @Override
91
    public TaxonNode loadTaxonNodeByTaxon(Taxon taxon, UUID classificationUuid, List<String> propertyPaths){
92
        Classification tree = dao.load(classificationUuid);
93
        TaxonNode node = tree.getNode(taxon);
94

    
95
        return loadTaxonNode(node.getUuid(), propertyPaths);
96
    }
97

    
98
    @Override
99
    @Deprecated // use loadTaxonNode(UUID, List<String>) instead
100
    public TaxonNode loadTaxonNode(TaxonNode taxonNode, List<String> propertyPaths){
101
        return taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
102
    }
103

    
104
    public TaxonNode loadTaxonNode(UUID taxonNodeUuid, List<String> propertyPaths){
105
        return taxonNodeDao.load(taxonNodeUuid, propertyPaths);
106
    }
107

    
108
    @Override
109
    public List<TaxonNode> listRankSpecificRootNodes(Classification classification, Rank rank, Integer pageSize,
110
            Integer pageIndex, List<String> propertyPaths) {
111
        return pageRankSpecificRootNodes(classification, rank, pageSize, pageIndex, propertyPaths).getRecords();
112
    }
113

    
114
    @Override
115
    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, Rank rank, Integer pageSize,
116
            Integer pageIndex, List<String> propertyPaths) {
117
        long[] numberOfResults = dao.countRankSpecificRootNodes(classification, rank);
118
        long totalNumberOfResults = numberOfResults[0] + (numberOfResults.length > 1 ? numberOfResults[1] : 0);
119

    
120
        List<TaxonNode> results = new ArrayList<TaxonNode>();
121

    
122
        if (AbstractPagerImpl.hasResultsInRange(totalNumberOfResults, pageIndex, pageSize)) { // no point checking again
123
            Integer limit = PagerUtils.limitFor(pageSize);
124
            Integer start = PagerUtils.startFor(pageSize, pageIndex);
125

    
126
            Integer remainingLimit = limit;
127
            int[] queryIndexes = rank == null ? new int[]{0} : new int[]{0,1};
128

    
129
            for(int queryIndex: queryIndexes) {
130
                if(start != null && start > numberOfResults[queryIndex]) {
131
                    // start in next query with new start value
132
                    start = start - (int)numberOfResults[queryIndex];
133
                    continue;
134
                }
135

    
136
                List<TaxonNode> perQueryResults = dao.listRankSpecificRootNodes(classification, rank, remainingLimit, start, propertyPaths, queryIndex);
137
                results.addAll(perQueryResults);
138
                if(remainingLimit != null ){
139
                    remainingLimit = remainingLimit - results.size();
140
                    if(remainingLimit <= 0) {
141
                        // no need to run further queries if first query returned enough items!
142
                        break;
143
                    }
144
                    // start at with fist item of next query to fetch the remaining items
145
                    start = 0;
146
                }
147
            }
148
        }
149
//        long start_t = System.currentTimeMillis();
150
        Collections.sort(results, taxonNodeComparator); // TODO is ordering during the hibernate query in the dao possible?
151
//        System.err.println("service.pageRankSpecificRootNodes() - Collections.sort(results,  taxonNodeComparator) " + (System.currentTimeMillis() - start_t));
152
        return new DefaultPagerImpl<TaxonNode>(pageIndex, (int) totalNumberOfResults, pageSize, results);
153

    
154
    }
155

    
156
    /**
157
     * @implements {@link IClassificationService#loadTreeBranch(TaxonNode, Rank, List)
158
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#loadTreeBranchTo(eu.etaxonomy.cdm.model.taxon.TaxonNode, eu.etaxonomy.cdm.model.name.Rank, java.util.List)
159
     * FIXME Candidate for harmonization
160
     * move to classification service
161
     */
162
    @Override
163
    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, Rank baseRank, List<String> propertyPaths){
164

    
165
        TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
166
        List<TaxonNode> pathToRoot = new ArrayList<TaxonNode>();
167
        pathToRoot.add(thisNode);
168

    
169
        while(!thisNode.isTopmostNode()){
170
            //TODO why do we need to deproxy here?
171
            //     without this thisNode.getParent() will return NULL in
172
            //     some cases (environment dependend?) even if the parent exits
173
            TaxonNode parentNode = CdmBase.deproxy(thisNode, TaxonNode.class).getParent();
174

    
175
            if(parentNode == null){
176
                throw new NullPointerException("taxonNode " + thisNode + " must have a parent since it is not top most");
177
            }
178
            if(parentNode.getTaxon() == null){
179
                throw new NullPointerException("The taxon associated with taxonNode " + parentNode + " is NULL");
180
            }
181
            if(parentNode.getTaxon().getName() == null){
182
                throw new NullPointerException("The name of the taxon associated with taxonNode " + parentNode + " is NULL");
183
            }
184

    
185
            Rank parentNodeRank = parentNode.getTaxon().getName() == null ? null : parentNode.getTaxon().getName().getRank();
186
            // stop if the next parent is higher than the baseRank
187
            if(baseRank != null && parentNodeRank != null && baseRank.isLower(parentNodeRank)){
188
                break;
189
            }
190

    
191
            pathToRoot.add(parentNode);
192
            thisNode = parentNode;
193
        }
194

    
195
        // initialize and invert order of nodes in list
196
        defaultBeanInitializer.initializeAll(pathToRoot, propertyPaths);
197
        Collections.reverse(pathToRoot);
198

    
199
        return pathToRoot;
200
    }
201

    
202
    @Override
203
    public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification, Rank baseRank, List<String> propertyPaths){
204
        Classification tree = dao.load(classification.getUuid());
205
        taxon = (Taxon) taxonDao.load(taxon.getUuid());
206
        TaxonNode node = tree.getNode(taxon);
207
        if(node == null){
208
            logger.warn("The specified taxon is not found in the given tree.");
209
            return null;
210
        }
211
        return loadTreeBranch(node, baseRank, propertyPaths);
212
    }
213

    
214

    
215
    @Override
216
    public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
217
            List<String> propertyPaths) {
218
        taxonNode = taxonNodeDao.load(taxonNode.getUuid());
219
        List<TaxonNode> childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
220
        defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
221
        Collections.sort(childNodes, taxonNodeComparator);
222
        return childNodes;
223
    }
224

    
225
    @Override
226
    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
227
            Integer pageIndex, List<String> propertyPaths){
228

    
229
        Classification classification = dao.load(classificationUuid);
230
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
231

    
232
        List<TaxonNode> results = dao.listChildrenOf(taxon, classification, pageSize, pageIndex, propertyPaths);
233
        Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
234
        return results;
235
    }
236

    
237
    @Override
238
    public TaxonNode getTaxonNodeByUuid(UUID uuid) {
239
        return taxonNodeDao.findByUuid(uuid);
240
    }
241

    
242
    @Override
243
    public ITaxonTreeNode getTreeNodeByUuid(UUID uuid){
244
        ITaxonTreeNode treeNode = taxonNodeDao.findByUuid(uuid);
245
        if(treeNode == null){
246
            treeNode = dao.findByUuid(uuid);
247
        }
248

    
249
        return treeNode;
250
    }
251

    
252
    @Override
253
    public List<Classification> listClassifications(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
254
        return dao.list(limit, start, orderHints, propertyPaths);
255
    }
256

    
257
    @Override
258
    public UUID removeTaxonNode(TaxonNode taxonNode) {
259
        return taxonNodeDao.delete(taxonNode);
260
    }
261
    @Override
262
    public UUID removeTreeNode(ITaxonTreeNode treeNode) {
263
        if(treeNode instanceof Classification){
264
            return dao.delete((Classification) treeNode);
265
        }else if(treeNode instanceof TaxonNode){
266
            return taxonNodeDao.delete((TaxonNode)treeNode);
267
        }
268
        return null;
269
    }
270
    @Override
271
    public UUID saveTaxonNode(TaxonNode taxonNode) {
272
        return taxonNodeDao.save(taxonNode).getUuid();
273
    }
274

    
275
    @Override
276
    public Map<UUID, TaxonNode> saveTaxonNodeAll(
277
            Collection<TaxonNode> taxonNodeCollection) {
278
        return taxonNodeDao.saveAll(taxonNodeCollection);
279
    }
280

    
281
    @Override
282
    public UUID saveTreeNode(ITaxonTreeNode treeNode) {
283
        if(treeNode instanceof Classification){
284
            return dao.save((Classification) treeNode).getUuid();
285
        }else if(treeNode instanceof TaxonNode){
286
            return taxonNodeDao.save((TaxonNode)treeNode).getUuid();
287
        }
288
        return null;
289
    }
290

    
291
    @Override
292
    public List<TaxonNode> getAllNodes(){
293
        return taxonNodeDao.list(null,null);
294
    }
295

    
296
    @Override
297
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, List<UUID> excludeTaxa, Integer limit, String pattern) {
298
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), excludeTaxa, limit, pattern);
299
    }
300

    
301
    @Override
302
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, List<UUID> excludeTaxa, Integer limit, String pattern) {
303
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, excludeTaxa, limit, pattern);
304
    }
305

    
306
    @Override
307
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, List<UUID> excludeTaxa) {
308
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), excludeTaxa);
309
    }
310

    
311
    @Override
312
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, List<UUID> excludeTaxa) {
313
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, excludeTaxa);
314
    }
315

    
316
    @Override
317
    public List<UuidAndTitleCache<Classification>> getUuidAndTitleCache(Integer limit, String pattern) {
318
        return dao.getUuidAndTitleCache(limit, pattern);
319
    }
320

    
321
    @Override
322
    public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(
323
            TaxonNode taxonNode, List<String> propertyPaths, int size,
324
            int height, int widthOrDuration, String[] mimeTypes) {
325

    
326
        TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<UUID, List<MediaRepresentation>>();
327
        List<Media> taxonMedia = new ArrayList<Media>();
328
        List<MediaRepresentation> mediaRepresentations = new ArrayList<MediaRepresentation>();
329

    
330
        //add all media of the children to the result map
331
        if (taxonNode != null){
332

    
333
            List<TaxonNode> nodes = new ArrayList<TaxonNode>();
334

    
335
            nodes.add(loadTaxonNode(taxonNode, propertyPaths));
336
            nodes.addAll(loadChildNodesOfTaxonNode(taxonNode, propertyPaths));
337

    
338
            if (nodes != null){
339
                for(TaxonNode node : nodes){
340
                    Taxon taxon = node.getTaxon();
341
                    for (TaxonDescription taxonDescription: taxon.getDescriptions()){
342
                        for (DescriptionElementBase descriptionElement: taxonDescription.getElements()){
343
                            for(Media media : descriptionElement.getMedia()){
344
                                taxonMedia.add(media);
345

    
346
                                //find the best matching representation
347
                                mediaRepresentations.add(MediaUtils.findBestMatchingRepresentation(media,null, size, height, widthOrDuration, mimeTypes));
348

    
349
                            }
350
                        }
351
                    }
352
                    result.put(taxon.getUuid(), mediaRepresentations);
353

    
354
                }
355
            }
356

    
357
        }
358

    
359
        return result;
360

    
361
    }
362

    
363
    @Override
364
    public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(Taxon taxon, Classification taxTree, List<String> propertyPaths, int size, int height, int widthOrDuration, String[] mimeTypes){
365
        TaxonNode node = taxTree.getNode(taxon);
366

    
367
        return getAllMediaForChildNodes(node, propertyPaths, size, height, widthOrDuration, mimeTypes);
368
    }
369

    
370
    @Override
371
    @Transactional(readOnly = false)
372
    public void updateTitleCache(Class<? extends Classification> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Classification> cacheStrategy, IProgressMonitor monitor) {
373
        if (clazz == null){
374
            clazz = Classification.class;
375
        }
376
        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
377
    }
378

    
379
    /**
380
     *
381
     * @param allNodesOfClassification
382
     * @return null - if  allNodesOfClassification is empty <br>
383
     */
384

    
385
    private Map<String, List<TaxonNode>> getSortedGenusList(Collection<TaxonNode> allNodesOfClassification){
386

    
387
    	if(allNodesOfClassification == null || allNodesOfClassification.isEmpty()){
388
    		return null;
389
    	}
390
    	Map<String, List<TaxonNode>> sortedGenusMap = new HashMap<String, List<TaxonNode>>();
391
    	for(TaxonNode node:allNodesOfClassification){
392
    		final TaxonNode tn = node;
393
    		Taxon taxon = node.getTaxon();
394
    		NonViralName name = CdmBase.deproxy(taxon.getName(), NonViralName.class);
395
    		String genusOrUninomial = name.getGenusOrUninomial();
396
    		//if rank unknown split string and take first word
397
    		if(genusOrUninomial == null){
398
    			String titleCache = taxon.getTitleCache();
399
    			String[] split = titleCache.split("\\s+");
400
    			for(String s:split){
401
    				genusOrUninomial = s;
402
    				break;
403
    			}
404
    		}
405
    		//if node has children
406

    
407
    		//retrieve list from map if not create List
408
    		if(sortedGenusMap.containsKey(genusOrUninomial)){
409
    			List<TaxonNode> list = sortedGenusMap.get(genusOrUninomial);
410
    			list.add(node);
411
    			sortedGenusMap.put(genusOrUninomial, list);
412
    		}else{
413
    			//create List for genus
414
    			List<TaxonNode> list = new ArrayList<TaxonNode>();
415
    			list.add(node);
416
    			sortedGenusMap.put(genusOrUninomial, list);
417
    		}
418
    	}
419
    	return sortedGenusMap;
420
    }
421

    
422
    /**
423
     *
424
     * creates new Classification and parent TaxonNodes at genus level
425
     *
426
     *
427
     * @param map GenusMap which holds a name (Genus) and all the same Taxa as a list
428
     * @param classification you want to improve the hierarchy (will not be modified)
429
     * @param configurator to change certain settings, if null then standard settings will be taken
430
     * @return new classification with parentNodes for each entry in the map
431
     */
432
    @SuppressWarnings({ "rawtypes", "unchecked" })
433
	@Transactional(readOnly = false)
434
	@Override
435
    public UpdateResult createHierarchyInClassification(Classification classification, CreateHierarchyForClassificationConfigurator configurator){
436
        UpdateResult result = new UpdateResult();
437
    	classification = dao.findByUuid(classification.getUuid());
438
    	Map<String, List<TaxonNode>> map = getSortedGenusList(classification.getAllNodes());
439

    
440
    	final String APPENDIX = "repaired";
441
    	String titleCache = org.apache.commons.lang.StringUtils.isBlank(classification.getTitleCache()) ? " " : classification.getTitleCache() ;
442
    	//TODO classification clone???
443
    	Classification newClassification = Classification.NewInstance(titleCache +" "+ APPENDIX);
444
    	newClassification.setReference(classification.getReference());
445

    
446
    	for(Map.Entry<String, List<TaxonNode>> entry:map.entrySet()){
447
    		String genus = entry.getKey();
448
    		List<TaxonNode> listOfTaxonNodes = entry.getValue();
449
    		TaxonNode parentNode = null;
450
    		//Search for genus in list
451
    		for(TaxonNode tNode:listOfTaxonNodes){
452
    			//take that taxonNode as parent and remove from list with all it possible children
453
    			//FIXME NPE for name
454
    			TaxonNameBase name = tNode.getTaxon().getName();
455
				NonViralName nonViralName = CdmBase.deproxy(name, NonViralName.class);
456
    			if(nonViralName.getNameCache().equalsIgnoreCase(genus)){
457
    				TaxonNode clone = (TaxonNode) tNode.clone();
458
    				if(!tNode.hasChildNodes()){
459
    					//FIXME remove classification
460
//    					parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
461
    					parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
462
    					//remove taxonNode from list because just added to classification
463
    					result.addUpdatedObject(tNode);
464
    					listOfTaxonNodes.remove(tNode);
465
    				}else{
466
    					//get all childNodes
467
    					//save prior Hierarchy and remove them from the list
468
    					List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tNode, clone, result);
469
//    					parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
470
      					//FIXME remove classification
471
    					parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
472
    					//remove taxonNode from list because just added to classification
473
    					result.addUpdatedObject(tNode);
474
    					listOfTaxonNodes.remove(tNode);
475
    					if(copyAllChildrenToTaxonNode != null){
476
    						listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
477
    					}
478
    				}
479
    				break;
480
    			}
481
    		}
482
    		if(parentNode == null){
483
    			//if no match found in list, create parentNode
484
    			NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
485
    			NonViralName nonViralName = parser.parseFullName(genus);
486
    			TaxonNameBase taxonNameBase = nonViralName;
487
    			//TODO Sec via configurator
488
    			Taxon taxon = Taxon.NewInstance(taxonNameBase, null);
489
    			parentNode = newClassification.addChildTaxon(taxon, 0, null, null);
490
    			result.addUpdatedObject(parentNode);
491
    		}
492
    		//iterate over the rest of the list
493
    		for(TaxonNode tn : listOfTaxonNodes){
494
    			//if TaxonNode has a parent and this is not the classification then skip it
495
    			//and add to new classification via the parentNode as children of it
496
    			//this should assures to keep the already existing hierarchy
497
    			//FIXME: Assert is not rootnode --> entrypoint is not classification in future but rather rootNode
498

    
499
    			if(!tn.isTopmostNode()){
500
    				continue; //skip to next taxonNode
501
    			}
502

    
503
    			TaxonNode clone = (TaxonNode) tn.clone();
504
    			//FIXME: citation from node
505
    			//TODO: addchildNode without citation and references
506
//    			TaxonNode taxonNode = parentNode.addChildNode(clone, classification.getCitation(), classification.getMicroReference());
507
    			TaxonNode taxonNode = parentNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
508
    			result.addUnChangedObject(clone);
509
    			if(tn.hasChildNodes()){
510
    				//save hierarchy in new classification
511
    				List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tn, taxonNode, result);
512
    				if(copyAllChildrenToTaxonNode != null){
513
    					listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
514
    				}
515
    			}
516
    		}
517
    	}
518
    	dao.saveOrUpdate(newClassification);
519
    	result.setCdmEntity(newClassification);
520
    	return result;
521
    }
522

    
523
    /**
524
     *
525
     * recursive method to get all childnodes of taxonNode in classification.
526
     *
527
     * @param classification just for References and Citation, can be null
528
     * @param copyFromNode TaxonNode with Children
529
     * @param copyToNode TaxonNode which will receive the children
530
     * @return List of ChildNode which has been added. If node has no children returns null
531
     */
532
   private List<TaxonNode> copyAllChildrenToTaxonNode(TaxonNode copyFromNode, TaxonNode copyToNode, UpdateResult result) {
533
		List<TaxonNode> childNodes;
534
		if(!copyFromNode.hasChildNodes()){
535
			return null;
536
		}else{
537
			childNodes = copyFromNode.getChildNodes();
538
		}
539
		for(TaxonNode childNode:childNodes){
540
			TaxonNode clone = (TaxonNode) childNode.clone();
541
			result.addUnChangedObject(clone);
542
			if(childNode.hasChildNodes()){
543
				copyAllChildrenToTaxonNode(childNode, clone, result);
544
			}
545
			//FIXME: citation from node instead of classification
546
//			copyToNode.addChildNode(clone,classification.getCitation(), classification.getMicroReference());
547
			copyToNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
548
		}
549
		return childNodes;
550
	}
551

    
552
}
(7-7/97)