Project

General

Profile

Download (29.5 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.Arrays;
15
import java.util.Collection;
16
import java.util.Collections;
17
import java.util.Comparator;
18
import java.util.HashMap;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.TreeMap;
22
import java.util.UUID;
23

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

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

    
64
/**
65
 * @author n.hoffmann
66
 * @created Sep 21, 2009
67
 */
68
@Service
69
@Transactional(readOnly = true)
70
public class ClassificationServiceImpl extends IdentifiableServiceBase<Classification, IClassificationDao>
71
    implements IClassificationService {
72
    private static final Logger logger = Logger.getLogger(ClassificationServiceImpl.class);
73

    
74
    @Autowired
75
    private ITaxonNodeDao taxonNodeDao;
76

    
77
    @Autowired
78
    private ITaxonDao taxonDao;
79

    
80
    @Autowired
81
    private IBeanInitializer defaultBeanInitializer;
82

    
83
    @Override
84
    @Autowired
85
    protected void setDao(IClassificationDao dao) {
86
        this.dao = dao;
87
    }
88

    
89
    private Comparator<? super TaxonNode> taxonNodeComparator;
90

    
91
    @Autowired
92
    public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){
93
        this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;
94
    }
95

    
96
    @Override
97
    public TaxonNode loadTaxonNodeByTaxon(Taxon taxon, UUID classificationUuid, List<String> propertyPaths){
98
        Classification tree = dao.load(classificationUuid);
99
        TaxonNode node = tree.getNode(taxon);
100

    
101
        return loadTaxonNode(node.getUuid(), propertyPaths);
102
    }
103

    
104
    @Override
105
    @Deprecated // use loadTaxonNode(UUID, List<String>) instead
106
    public TaxonNode loadTaxonNode(TaxonNode taxonNode, List<String> propertyPaths){
107
        return taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
108
    }
109

    
110
    public TaxonNode loadTaxonNode(UUID taxonNodeUuid, List<String> propertyPaths){
111
        return taxonNodeDao.load(taxonNodeUuid, propertyPaths);
112
    }
113

    
114
    @Override
115
    public List<TaxonNode> listRankSpecificRootNodes(Classification classification, Rank rank, Integer pageSize,
116
            Integer pageIndex, List<String> propertyPaths) {
117
        return pageRankSpecificRootNodes(classification, rank, pageSize, pageIndex, propertyPaths).getRecords();
118
    }
119

    
120
    @Override
121
    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, Rank rank, Integer pageSize,
122
            Integer pageIndex, List<String> propertyPaths) {
123
        long[] numberOfResults = dao.countRankSpecificRootNodes(classification, rank);
124
        long totalNumberOfResults = numberOfResults[0] + (numberOfResults.length > 1 ? numberOfResults[1] : 0);
125

    
126
        List<TaxonNode> results = new ArrayList<TaxonNode>();
127

    
128
        if (AbstractPagerImpl.hasResultsInRange(totalNumberOfResults, pageIndex, pageSize)) { // no point checking again
129
            Integer limit = PagerUtils.limitFor(pageSize);
130
            Integer start = PagerUtils.startFor(pageSize, pageIndex);
131

    
132
            Integer remainingLimit = limit;
133
            int[] queryIndexes = rank == null ? new int[]{0} : new int[]{0,1};
134

    
135
            for(int queryIndex: queryIndexes) {
136
                if(start != null && start > numberOfResults[queryIndex]) {
137
                    // start in next query with new start value
138
                    start = start - (int)numberOfResults[queryIndex];
139
                    continue;
140
                }
141

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

    
160
    }
161

    
162
    /**
163
     * @implements {@link IClassificationService#loadTreeBranch(TaxonNode, Rank, List)
164
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#loadTreeBranchTo(eu.etaxonomy.cdm.model.taxon.TaxonNode, eu.etaxonomy.cdm.model.name.Rank, java.util.List)
165
     * FIXME Candidate for harmonization
166
     * move to classification service
167
     */
168
    @Override
169
    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, Rank baseRank, List<String> propertyPaths){
170

    
171
        TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
172
        List<TaxonNode> pathToRoot = new ArrayList<TaxonNode>();
173
        pathToRoot.add(thisNode);
174

    
175
        while(!thisNode.isTopmostNode()){
176
            //TODO why do we need to deproxy here?
177
            //     without this thisNode.getParent() will return NULL in
178
            //     some cases (environment dependend?) even if the parent exits
179
            TaxonNode parentNode = CdmBase.deproxy(thisNode, TaxonNode.class).getParent();
180

    
181
            if(parentNode == null){
182
                throw new NullPointerException("taxonNode " + thisNode + " must have a parent since it is not top most");
183
            }
184
            if(parentNode.getTaxon() == null){
185
                throw new NullPointerException("The taxon associated with taxonNode " + parentNode + " is NULL");
186
            }
187
            if(parentNode.getTaxon().getName() == null){
188
                throw new NullPointerException("The name of the taxon associated with taxonNode " + parentNode + " is NULL");
189
            }
190

    
191
            Rank parentNodeRank = parentNode.getTaxon().getName() == null ? null : parentNode.getTaxon().getName().getRank();
192
            // stop if the next parent is higher than the baseRank
193
            if(baseRank != null && parentNodeRank != null && baseRank.isLower(parentNodeRank)){
194
                break;
195
            }
196

    
197
            pathToRoot.add(parentNode);
198
            thisNode = parentNode;
199
        }
200

    
201
        // initialize and invert order of nodes in list
202
        defaultBeanInitializer.initializeAll(pathToRoot, propertyPaths);
203
        Collections.reverse(pathToRoot);
204

    
205
        return pathToRoot;
206
    }
207

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

    
220

    
221
    @Override
222
    public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
223
            List<String> propertyPaths) {
224
        taxonNode = taxonNodeDao.load(taxonNode.getUuid());
225
        List<TaxonNode> childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
226
        defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
227
        Collections.sort(childNodes, taxonNodeComparator);
228
        return childNodes;
229
    }
230

    
231
    @Override
232
    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
233
            Integer pageIndex, List<String> propertyPaths){
234

    
235
        Classification classification = dao.load(classificationUuid);
236
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
237

    
238
        List<TaxonNode> results = dao.listChildrenOf(taxon, classification, pageSize, pageIndex, propertyPaths);
239
        Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
240
        return results;
241
    }
242

    
243
    @Override
244
    public Pager<TaxonNode> pageSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
245
            Integer pageIndex, List<String> propertyPaths){
246

    
247
        Classification classification = dao.load(classificationUuid);
248
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
249

    
250
        long numberOfResults = dao.countSiblingsOf(taxon, classification);
251

    
252
        List<TaxonNode> results;
253
        if(PagerUtils.hasResultsInRange(numberOfResults, pageIndex, pageSize)) {
254
            results = dao.listSiblingsOf(taxon, classification, pageSize, pageIndex, propertyPaths);
255
            Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
256
        } else {
257
            results = new ArrayList<>();
258
        }
259

    
260
        return new DefaultPagerImpl<TaxonNode>(pageIndex, numberOfResults, pageSize, results);
261
    }
262

    
263
    @Override
264
    public List<TaxonNode> listSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
265
            Integer pageIndex, List<String> propertyPaths){
266

    
267
        Pager<TaxonNode> pager = pageSiblingsOfTaxon(taxonUuid, classificationUuid, pageSize, pageIndex, propertyPaths);
268
        return pager.getRecords();
269
    }
270

    
271
    @Override
272
    public TaxonNode getTaxonNodeByUuid(UUID uuid) {
273
        return taxonNodeDao.findByUuid(uuid);
274
    }
275

    
276
    @Override
277
    public ITaxonTreeNode getTreeNodeByUuid(UUID uuid){
278
        ITaxonTreeNode treeNode = taxonNodeDao.findByUuid(uuid);
279
        if(treeNode == null){
280
            treeNode = dao.findByUuid(uuid);
281
        }
282

    
283
        return treeNode;
284
    }
285

    
286
    @Override
287
    public TaxonNode getRootNode(UUID classificationUuid){
288
        return dao.getRootNode(classificationUuid);
289
    }
290

    
291
    @Override
292
    public List<Classification> listClassifications(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
293
        return dao.list(limit, start, orderHints, propertyPaths);
294
    }
295

    
296
    @Override
297
    public UUID removeTaxonNode(TaxonNode taxonNode) {
298
        return taxonNodeDao.delete(taxonNode);
299
    }
300
    @Override
301
    public UUID removeTreeNode(ITaxonTreeNode treeNode) {
302
        if(treeNode instanceof Classification){
303
            return dao.delete((Classification) treeNode);
304
        }else if(treeNode instanceof TaxonNode){
305
            return taxonNodeDao.delete((TaxonNode)treeNode);
306
        }
307
        return null;
308
    }
309
    @Override
310
    public UUID saveTaxonNode(TaxonNode taxonNode) {
311
        return taxonNodeDao.save(taxonNode).getUuid();
312
    }
313

    
314
    @Override
315
    public Map<UUID, TaxonNode> saveTaxonNodeAll(
316
            Collection<TaxonNode> taxonNodeCollection) {
317
        return taxonNodeDao.saveAll(taxonNodeCollection);
318
    }
319

    
320
    @Override
321
    public UUID saveTreeNode(ITaxonTreeNode treeNode) {
322
        if(treeNode instanceof Classification){
323
            return dao.save((Classification) treeNode).getUuid();
324
        }else if(treeNode instanceof TaxonNode){
325
            return taxonNodeDao.save((TaxonNode)treeNode).getUuid();
326
        }
327
        return null;
328
    }
329

    
330
    @Override
331
    public List<TaxonNode> getAllNodes(){
332
        return taxonNodeDao.list(null,null);
333
    }
334

    
335
    @Override
336
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, Integer limit, String pattern) {
337
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid),  limit, pattern);
338
    }
339

    
340
    @Override
341
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification,  Integer limit, String pattern) {
342
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification,  limit, pattern);
343
    }
344

    
345
    @Override
346
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid ) {
347
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), null, null);
348
    }
349

    
350
    @Override
351
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification ) {
352
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, null, null);
353
    }
354

    
355
    @Override
356
    public List<UuidAndTitleCache<Classification>> getUuidAndTitleCache(Integer limit, String pattern) {
357
        return dao.getUuidAndTitleCache(limit, pattern);
358
    }
359

    
360
    @Override
361
    public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(
362
            TaxonNode taxonNode, List<String> propertyPaths, int size,
363
            int height, int widthOrDuration, String[] mimeTypes) {
364

    
365
        TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<UUID, List<MediaRepresentation>>();
366
        List<Media> taxonMedia = new ArrayList<Media>();
367
        List<MediaRepresentation> mediaRepresentations = new ArrayList<MediaRepresentation>();
368

    
369
        //add all media of the children to the result map
370
        if (taxonNode != null){
371

    
372
            List<TaxonNode> nodes = new ArrayList<TaxonNode>();
373

    
374
            nodes.add(loadTaxonNode(taxonNode, propertyPaths));
375
            nodes.addAll(loadChildNodesOfTaxonNode(taxonNode, propertyPaths));
376

    
377
            if (nodes != null){
378
                for(TaxonNode node : nodes){
379
                    Taxon taxon = node.getTaxon();
380
                    for (TaxonDescription taxonDescription: taxon.getDescriptions()){
381
                        for (DescriptionElementBase descriptionElement: taxonDescription.getElements()){
382
                            for(Media media : descriptionElement.getMedia()){
383
                                taxonMedia.add(media);
384

    
385
                                //find the best matching representation
386
                                mediaRepresentations.add(MediaUtils.findBestMatchingRepresentation(media,null, size, height, widthOrDuration, mimeTypes));
387

    
388
                            }
389
                        }
390
                    }
391
                    result.put(taxon.getUuid(), mediaRepresentations);
392

    
393
                }
394
            }
395

    
396
        }
397

    
398
        return result;
399

    
400
    }
401

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

    
406
        return getAllMediaForChildNodes(node, propertyPaths, size, height, widthOrDuration, mimeTypes);
407
    }
408

    
409
    @Override
410
    @Transactional(readOnly = false)
411
    public void updateTitleCache(Class<? extends Classification> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Classification> cacheStrategy, IProgressMonitor monitor) {
412
        if (clazz == null){
413
            clazz = Classification.class;
414
        }
415
        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
416
    }
417

    
418
    /**
419
     *
420
     * @param allNodesOfClassification
421
     * @return null - if  allNodesOfClassification is empty <br>
422
     */
423

    
424
    private Map<String, List<TaxonNode>> getSortedGenusList(Collection<TaxonNode> allNodesOfClassification){
425

    
426
    	if(allNodesOfClassification == null || allNodesOfClassification.isEmpty()){
427
    		return null;
428
    	}
429
    	Map<String, List<TaxonNode>> sortedGenusMap = new HashMap<String, List<TaxonNode>>();
430
    	for(TaxonNode node:allNodesOfClassification){
431
    		final TaxonNode tn = node;
432
    		Taxon taxon = node.getTaxon();
433
    		NonViralName name = CdmBase.deproxy(taxon.getName(), NonViralName.class);
434
    		String genusOrUninomial = name.getGenusOrUninomial();
435
    		//if rank unknown split string and take first word
436
    		if(genusOrUninomial == null){
437
    			String titleCache = taxon.getTitleCache();
438
    			String[] split = titleCache.split("\\s+");
439
    			for(String s:split){
440
    				genusOrUninomial = s;
441
    				break;
442
    			}
443
    		}
444
    		//if node has children
445

    
446
    		//retrieve list from map if not create List
447
    		if(sortedGenusMap.containsKey(genusOrUninomial)){
448
    			List<TaxonNode> list = sortedGenusMap.get(genusOrUninomial);
449
    			list.add(node);
450
    			sortedGenusMap.put(genusOrUninomial, list);
451
    		}else{
452
    			//create List for genus
453
    			List<TaxonNode> list = new ArrayList<TaxonNode>();
454
    			list.add(node);
455
    			sortedGenusMap.put(genusOrUninomial, list);
456
    		}
457
    	}
458
    	return sortedGenusMap;
459
    }
460

    
461
    /**
462
     *
463
     * creates new Classification and parent TaxonNodes at genus level
464
     *
465
     *
466
     * @param map GenusMap which holds a name (Genus) and all the same Taxa as a list
467
     * @param classification you want to improve the hierarchy (will not be modified)
468
     * @param configurator to change certain settings, if null then standard settings will be taken
469
     * @return new classification with parentNodes for each entry in the map
470
     */
471
    @SuppressWarnings({ "rawtypes", "unchecked" })
472
	@Transactional(readOnly = false)
473
	@Override
474
    public UpdateResult createHierarchyInClassification(Classification classification, CreateHierarchyForClassificationConfigurator configurator){
475
        UpdateResult result = new UpdateResult();
476
    	classification = dao.findByUuid(classification.getUuid());
477
    	Map<String, List<TaxonNode>> map = getSortedGenusList(classification.getAllNodes());
478

    
479
    	final String APPENDIX = "repaired";
480
    	String titleCache = org.apache.commons.lang.StringUtils.isBlank(classification.getTitleCache()) ? " " : classification.getTitleCache() ;
481
    	//TODO classification clone???
482
    	Classification newClassification = Classification.NewInstance(titleCache +" "+ APPENDIX);
483
    	newClassification.setReference(classification.getReference());
484

    
485
    	for(Map.Entry<String, List<TaxonNode>> entry:map.entrySet()){
486
    		String genus = entry.getKey();
487
    		List<TaxonNode> listOfTaxonNodes = entry.getValue();
488
    		TaxonNode parentNode = null;
489
    		//Search for genus in list
490
    		for(TaxonNode tNode:listOfTaxonNodes){
491
    			//take that taxonNode as parent and remove from list with all it possible children
492
    			//FIXME NPE for name
493
    			TaxonNameBase name = tNode.getTaxon().getName();
494
				NonViralName nonViralName = CdmBase.deproxy(name, NonViralName.class);
495
    			if(nonViralName.getNameCache().equalsIgnoreCase(genus)){
496
    				TaxonNode clone = (TaxonNode) tNode.clone();
497
    				if(!tNode.hasChildNodes()){
498
    					//FIXME remove classification
499
//    					parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
500
    					parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
501
    					//remove taxonNode from list because just added to classification
502
    					result.addUpdatedObject(tNode);
503
    					listOfTaxonNodes.remove(tNode);
504
    				}else{
505
    					//get all childNodes
506
    					//save prior Hierarchy and remove them from the list
507
    					List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tNode, clone, result);
508
//    					parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
509
      					//FIXME remove classification
510
    					parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
511
    					//remove taxonNode from list because just added to classification
512
    					result.addUpdatedObject(tNode);
513
    					listOfTaxonNodes.remove(tNode);
514
    					if(copyAllChildrenToTaxonNode != null){
515
    						listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
516
    					}
517
    				}
518
    				break;
519
    			}
520
    		}
521
    		if(parentNode == null){
522
    			//if no match found in list, create parentNode
523
    			NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
524
    			NonViralName nonViralName = parser.parseFullName(genus);
525
    			TaxonNameBase taxonNameBase = nonViralName;
526
    			//TODO Sec via configurator
527
    			Taxon taxon = Taxon.NewInstance(taxonNameBase, null);
528
    			parentNode = newClassification.addChildTaxon(taxon, 0, null, null);
529
    			result.addUpdatedObject(parentNode);
530
    		}
531
    		//iterate over the rest of the list
532
    		for(TaxonNode tn : listOfTaxonNodes){
533
    			//if TaxonNode has a parent and this is not the classification then skip it
534
    			//and add to new classification via the parentNode as children of it
535
    			//this should assures to keep the already existing hierarchy
536
    			//FIXME: Assert is not rootnode --> entrypoint is not classification in future but rather rootNode
537

    
538
    			if(!tn.isTopmostNode()){
539
    				continue; //skip to next taxonNode
540
    			}
541

    
542
    			TaxonNode clone = (TaxonNode) tn.clone();
543
    			//FIXME: citation from node
544
    			//TODO: addchildNode without citation and references
545
//    			TaxonNode taxonNode = parentNode.addChildNode(clone, classification.getCitation(), classification.getMicroReference());
546
    			TaxonNode taxonNode = parentNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
547
    			result.addUnChangedObject(clone);
548
    			if(tn.hasChildNodes()){
549
    				//save hierarchy in new classification
550
    				List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tn, taxonNode, result);
551
    				if(copyAllChildrenToTaxonNode != null){
552
    					listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
553
    				}
554
    			}
555
    		}
556
    	}
557
    	dao.saveOrUpdate(newClassification);
558
    	result.setCdmEntity(newClassification);
559
    	return result;
560
    }
561

    
562
    /**
563
     *
564
     * recursive method to get all childnodes of taxonNode in classification.
565
     *
566
     * @param classification just for References and Citation, can be null
567
     * @param copyFromNode TaxonNode with Children
568
     * @param copyToNode TaxonNode which will receive the children
569
     * @return List of ChildNode which has been added. If node has no children returns null
570
     */
571
   private List<TaxonNode> copyAllChildrenToTaxonNode(TaxonNode copyFromNode, TaxonNode copyToNode, UpdateResult result) {
572
		List<TaxonNode> childNodes;
573
		if(!copyFromNode.hasChildNodes()){
574
			return null;
575
		}else{
576
			childNodes = copyFromNode.getChildNodes();
577
		}
578
		for(TaxonNode childNode:childNodes){
579
			TaxonNode clone = (TaxonNode) childNode.clone();
580
			result.addUnChangedObject(clone);
581
			if(childNode.hasChildNodes()){
582
				copyAllChildrenToTaxonNode(childNode, clone, result);
583
			}
584
			//FIXME: citation from node instead of classification
585
//			copyToNode.addChildNode(clone,classification.getCitation(), classification.getMicroReference());
586
			copyToNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
587
		}
588
		return childNodes;
589
	}
590

    
591
    /**
592
     * {@inheritDoc}
593
     */
594
    @Override
595
    public ClassificationLookupDTO classificationLookup(Classification classification) {
596
        return dao.classificationLookup(classification);
597
    }
598

    
599

    
600
    @Override
601
    public DeleteResult delete(UUID classificationUuid, TaxonDeletionConfigurator config){
602
        DeleteResult result = new DeleteResult();
603
        Classification classification = dao.findByUuid(classificationUuid);
604
        if (classification == null){
605
            result.addException(new IllegalArgumentException("The classification does not exist in database."));
606
            result.setAbort();
607
            return result;
608
        }
609
        if (!classification.hasChildNodes()){
610
            dao.delete(classification);
611
        }
612
        if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE) ){
613
            TaxonNode root = classification.getRootNode();
614
            taxonNodeDao.delete(root, true);
615
            dao.delete(classification);
616
        }
617

    
618

    
619
        return result;
620
    }
621

    
622
    @Override
623
    public List<GroupedTaxonDTO> groupTaxaByHigherTaxon(List<UUID> originalTaxonUuids, UUID classificationUuid, Rank minRank, Rank maxRank){
624
        List<GroupedTaxonDTO> result = new ArrayList<>();
625

    
626
        //get treeindex for each taxonUUID
627
        Map<UUID, String> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
628

    
629
        //build treeindex list (or tree)
630
        List<String> treeIndexClosure = new ArrayList<>();
631
        for (String treeIndex : taxonIdTreeIndexMap.values()){
632
            String[] splits = treeIndex.substring(1).split(ITreeNode.separator);
633
            String currentIndex = ITreeNode.separator;
634
            for (String split : splits){
635
                if (split.equals("")){
636
                    continue;
637
                }
638
                currentIndex += split + ITreeNode.separator;
639
                if (!treeIndexClosure.contains(currentIndex) && !split.startsWith(ITreeNode.treePrefix)){
640
                    treeIndexClosure.add(currentIndex);
641
                }
642
            }
643
        }
644

    
645
        //get rank sortindex for all parent taxa with sortindex <= minRank and sortIndex >= maxRank (if available)
646
        Integer minRankOrderIndex = minRank == null ? null : minRank.getOrderIndex();
647
        Integer maxRankOrderIndex = maxRank == null ? null : maxRank.getOrderIndex();
648
        Map<String, Integer> treeIndexSortIndexMapTmp = taxonNodeDao.rankOrderIndexForTreeIndex(treeIndexClosure, minRankOrderIndex, maxRankOrderIndex);
649

    
650
        //remove all treeindex with "exists child in above map(and child.sortindex > xxx)
651
        List<String> treeIndexList = new ArrayList<>(treeIndexSortIndexMapTmp.keySet());
652
        Collections.sort(treeIndexList, new TreeIndexComparator());
653
        Map<String, Integer> treeIndexSortIndexMap = new HashMap<>();
654
        String lastTreeIndex = null;
655
        for (String treeIndex : treeIndexList){
656
            if (lastTreeIndex != null && treeIndex.startsWith(lastTreeIndex)){
657
                treeIndexSortIndexMap.remove(lastTreeIndex);
658
            }
659
            treeIndexSortIndexMap.put(treeIndex, treeIndexSortIndexMapTmp.get(treeIndex));
660
            lastTreeIndex = treeIndex;
661
        }
662

    
663
        //get taxonID for treeIndexes
664
        Map<String, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(treeIndexSortIndexMap.keySet());
665

    
666
        //fill result list
667
        for (UUID originalTaxonUuid : originalTaxonUuids){
668
            GroupedTaxonDTO item = new GroupedTaxonDTO();
669
            result.add(item);
670
            item.setTaxonUuid(originalTaxonUuid);
671
            String groupIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
672
            while (groupIndex != null){
673
                if (treeIndexTaxonIdMap.get(groupIndex) != null){
674
                    UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupIndex);
675
                    item.setGroupTaxonUuid(uuidAndLabel.getUuid());
676
                    item.setGroupTaxonName(uuidAndLabel.getTitleCache());
677
                    break;
678
                }else{
679
                    int index = groupIndex.substring(0, groupIndex.length()-1).lastIndexOf(ITreeNode.separator);
680
                    groupIndex = index<0 ? null : groupIndex.substring(0, index+1);
681
                }
682
            }
683
        }
684

    
685
        return result;
686
    }
687

    
688
    /**
689
     * {@inheritDoc}
690
     */
691
    @Override
692
    public UUID getTaxonNodeUuidByTaxonUuid(UUID classificationUuid, UUID taxonUuid) {
693
        Map<UUID, UUID> map = dao.getTaxonNodeUuidByTaxonUuid(classificationUuid, Arrays.asList(taxonUuid));
694
        UUID taxonNodeUuid = map.get(taxonUuid);
695
        return taxonNodeUuid;
696
    }
697

    
698
}
(7-7/98)