Project

General

Profile

Download (32.1 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.EntityDTO;
34
import eu.etaxonomy.cdm.api.service.dto.GroupedTaxonDTO;
35
import eu.etaxonomy.cdm.api.service.dto.TaxonInContextDTO;
36
import eu.etaxonomy.cdm.api.service.pager.Pager;
37
import eu.etaxonomy.cdm.api.service.pager.PagerUtils;
38
import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
39
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
40
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
41
import eu.etaxonomy.cdm.model.common.CdmBase;
42
import eu.etaxonomy.cdm.model.common.ITreeNode;
43
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
44
import eu.etaxonomy.cdm.model.description.TaxonDescription;
45
import eu.etaxonomy.cdm.model.media.Media;
46
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
47
import eu.etaxonomy.cdm.model.media.MediaUtils;
48
import eu.etaxonomy.cdm.model.name.NonViralName;
49
import eu.etaxonomy.cdm.model.name.Rank;
50
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
51
import eu.etaxonomy.cdm.model.taxon.Classification;
52
import eu.etaxonomy.cdm.model.taxon.ITaxonNodeComparator;
53
import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
54
import eu.etaxonomy.cdm.model.taxon.Synonym;
55
import eu.etaxonomy.cdm.model.taxon.Taxon;
56
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
57
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
58
import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
59
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
60
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
61
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
62
import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
63
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
64
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto.TaxonStatus;
65
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
66
import eu.etaxonomy.cdm.persistence.query.OrderHint;
67
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
68
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
69

    
70
/**
71
 * @author n.hoffmann
72
 * @created Sep 21, 2009
73
 */
74
@Service
75
@Transactional(readOnly = true)
76
public class ClassificationServiceImpl extends IdentifiableServiceBase<Classification, IClassificationDao>
77
    implements IClassificationService {
78
    private static final Logger logger = Logger.getLogger(ClassificationServiceImpl.class);
79

    
80
    @Autowired
81
    private ITaxonNodeDao taxonNodeDao;
82

    
83
    @Autowired
84
    private ITaxonDao taxonDao;
85

    
86
    @Autowired
87
    private ITaxonNodeService taxonNodeService;
88

    
89
    @Autowired
90
    private IBeanInitializer defaultBeanInitializer;
91

    
92
    @Override
93
    @Autowired
94
    protected void setDao(IClassificationDao dao) {
95
        this.dao = dao;
96
    }
97

    
98
    private Comparator<? super TaxonNode> taxonNodeComparator;
99

    
100
    @Autowired
101
    public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){
102
        this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;
103
    }
104

    
105
    @Override
106
    public TaxonNode loadTaxonNodeByTaxon(Taxon taxon, UUID classificationUuid, List<String> propertyPaths){
107
        Classification tree = dao.load(classificationUuid);
108
        TaxonNode node = tree.getNode(taxon);
109

    
110
        return loadTaxonNode(node.getUuid(), propertyPaths);
111
    }
112

    
113
    @Override
114
    @Deprecated // use loadTaxonNode(UUID, List<String>) instead
115
    public TaxonNode loadTaxonNode(TaxonNode taxonNode, List<String> propertyPaths){
116
        return taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
117
    }
118

    
119
    public TaxonNode loadTaxonNode(UUID taxonNodeUuid, List<String> propertyPaths){
120
        return taxonNodeDao.load(taxonNodeUuid, propertyPaths);
121
    }
122

    
123
    @Override
124
    public List<TaxonNode> listRankSpecificRootNodes(Classification classification, Rank rank, Integer pageSize,
125
            Integer pageIndex, List<String> propertyPaths) {
126
        return pageRankSpecificRootNodes(classification, rank, pageSize, pageIndex, propertyPaths).getRecords();
127
    }
128

    
129
    @Override
130
    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, Rank rank, Integer pageSize,
131
            Integer pageIndex, List<String> propertyPaths) {
132
        long[] numberOfResults = dao.countRankSpecificRootNodes(classification, rank);
133
        long totalNumberOfResults = numberOfResults[0] + (numberOfResults.length > 1 ? numberOfResults[1] : 0);
134

    
135
        List<TaxonNode> results = new ArrayList<TaxonNode>();
136

    
137
        if (AbstractPagerImpl.hasResultsInRange(totalNumberOfResults, pageIndex, pageSize)) { // no point checking again
138
            Integer limit = PagerUtils.limitFor(pageSize);
139
            Integer start = PagerUtils.startFor(pageSize, pageIndex);
140

    
141
            Integer remainingLimit = limit;
142
            int[] queryIndexes = rank == null ? new int[]{0} : new int[]{0,1};
143

    
144
            for(int queryIndex: queryIndexes) {
145
                if(start != null && start > numberOfResults[queryIndex]) {
146
                    // start in next query with new start value
147
                    start = start - (int)numberOfResults[queryIndex];
148
                    continue;
149
                }
150

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

    
169
    }
170

    
171
    /**
172
     * @implements {@link IClassificationService#loadTreeBranch(TaxonNode, Rank, List)
173
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#loadTreeBranchTo(eu.etaxonomy.cdm.model.taxon.TaxonNode, eu.etaxonomy.cdm.model.name.Rank, java.util.List)
174
     * FIXME Candidate for harmonization
175
     * move to classification service
176
     */
177
    @Override
178
    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, Rank baseRank, List<String> propertyPaths){
179

    
180
        TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
181
        List<TaxonNode> pathToRoot = new ArrayList<TaxonNode>();
182
        pathToRoot.add(thisNode);
183

    
184
        while(!thisNode.isTopmostNode()){
185
            //TODO why do we need to deproxy here?
186
            //     without this thisNode.getParent() will return NULL in
187
            //     some cases (environment dependend?) even if the parent exits
188
            TaxonNode parentNode = CdmBase.deproxy(thisNode, TaxonNode.class).getParent();
189

    
190
            if(parentNode == null){
191
                throw new NullPointerException("taxonNode " + thisNode + " must have a parent since it is not top most");
192
            }
193
            if(parentNode.getTaxon() == null){
194
                throw new NullPointerException("The taxon associated with taxonNode " + parentNode + " is NULL");
195
            }
196
            if(parentNode.getTaxon().getName() == null){
197
                throw new NullPointerException("The name of the taxon associated with taxonNode " + parentNode + " is NULL");
198
            }
199

    
200
            Rank parentNodeRank = parentNode.getTaxon().getName() == null ? null : parentNode.getTaxon().getName().getRank();
201
            // stop if the next parent is higher than the baseRank
202
            if(baseRank != null && parentNodeRank != null && baseRank.isLower(parentNodeRank)){
203
                break;
204
            }
205

    
206
            pathToRoot.add(parentNode);
207
            thisNode = parentNode;
208
        }
209

    
210
        // initialize and invert order of nodes in list
211
        defaultBeanInitializer.initializeAll(pathToRoot, propertyPaths);
212
        Collections.reverse(pathToRoot);
213

    
214
        return pathToRoot;
215
    }
216

    
217
    @Override
218
    public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification, Rank baseRank, List<String> propertyPaths){
219
        Classification tree = dao.load(classification.getUuid());
220
        taxon = (Taxon) taxonDao.load(taxon.getUuid());
221
        TaxonNode node = tree.getNode(taxon);
222
        if(node == null){
223
            logger.warn("The specified taxon is not found in the given tree.");
224
            return null;
225
        }
226
        return loadTreeBranch(node, baseRank, propertyPaths);
227
    }
228

    
229

    
230
    @Override
231
    public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
232
            List<String> propertyPaths) {
233
        taxonNode = taxonNodeDao.load(taxonNode.getUuid());
234
        List<TaxonNode> childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
235
        defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
236
        Collections.sort(childNodes, taxonNodeComparator);
237
        return childNodes;
238
    }
239

    
240
    @Override
241
    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
242
            Integer pageIndex, List<String> propertyPaths){
243

    
244
        Classification classification = dao.load(classificationUuid);
245
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
246

    
247
        List<TaxonNode> results = dao.listChildrenOf(taxon, classification, pageSize, pageIndex, propertyPaths);
248
        Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
249
        return results;
250
    }
251

    
252
    @Override
253
    public Pager<TaxonNode> pageSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
254
            Integer pageIndex, List<String> propertyPaths){
255

    
256
        Classification classification = dao.load(classificationUuid);
257
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
258

    
259
        long numberOfResults = dao.countSiblingsOf(taxon, classification);
260

    
261
        List<TaxonNode> results;
262
        if(PagerUtils.hasResultsInRange(numberOfResults, pageIndex, pageSize)) {
263
            results = dao.listSiblingsOf(taxon, classification, pageSize, pageIndex, propertyPaths);
264
            Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
265
        } else {
266
            results = new ArrayList<>();
267
        }
268

    
269
        return new DefaultPagerImpl<TaxonNode>(pageIndex, numberOfResults, pageSize, results);
270
    }
271

    
272
    @Override
273
    public List<TaxonNode> listSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
274
            Integer pageIndex, List<String> propertyPaths){
275

    
276
        Pager<TaxonNode> pager = pageSiblingsOfTaxon(taxonUuid, classificationUuid, pageSize, pageIndex, propertyPaths);
277
        return pager.getRecords();
278
    }
279

    
280
    @Override
281
    public TaxonNode getTaxonNodeByUuid(UUID uuid) {
282
        return taxonNodeDao.findByUuid(uuid);
283
    }
284

    
285
    @Override
286
    public ITaxonTreeNode getTreeNodeByUuid(UUID uuid){
287
        ITaxonTreeNode treeNode = taxonNodeDao.findByUuid(uuid);
288
        if(treeNode == null){
289
            treeNode = dao.findByUuid(uuid);
290
        }
291

    
292
        return treeNode;
293
    }
294

    
295
    @Override
296
    public TaxonNode getRootNode(UUID classificationUuid){
297
        return dao.getRootNode(classificationUuid);
298
    }
299

    
300
    @Override
301
    public List<Classification> listClassifications(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
302
        return dao.list(limit, start, orderHints, propertyPaths);
303
    }
304

    
305
    @Override
306
    public UUID removeTaxonNode(TaxonNode taxonNode) {
307
        return taxonNodeDao.delete(taxonNode);
308
    }
309
    @Override
310
    public UUID removeTreeNode(ITaxonTreeNode treeNode) {
311
        if(treeNode instanceof Classification){
312
            return dao.delete((Classification) treeNode);
313
        }else if(treeNode instanceof TaxonNode){
314
            return taxonNodeDao.delete((TaxonNode)treeNode);
315
        }
316
        return null;
317
    }
318
    @Override
319
    public UUID saveTaxonNode(TaxonNode taxonNode) {
320
        return taxonNodeDao.save(taxonNode).getUuid();
321
    }
322

    
323
    @Override
324
    public Map<UUID, TaxonNode> saveTaxonNodeAll(
325
            Collection<TaxonNode> taxonNodeCollection) {
326
        return taxonNodeDao.saveAll(taxonNodeCollection);
327
    }
328

    
329
    @Override
330
    public UUID saveTreeNode(ITaxonTreeNode treeNode) {
331
        if(treeNode instanceof Classification){
332
            return dao.save((Classification) treeNode).getUuid();
333
        }else if(treeNode instanceof TaxonNode){
334
            return taxonNodeDao.save((TaxonNode)treeNode).getUuid();
335
        }
336
        return null;
337
    }
338

    
339
    @Override
340
    public List<TaxonNode> getAllNodes(){
341
        return taxonNodeDao.list(null,null);
342
    }
343

    
344
    @Override
345
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, Integer limit, String pattern) {
346
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid),  limit, pattern);
347
    }
348

    
349
    @Override
350
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification,  Integer limit, String pattern) {
351
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification,  limit, pattern);
352
    }
353

    
354
    @Override
355
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid ) {
356
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), null, null);
357
    }
358

    
359
    @Override
360
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification ) {
361
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, null, null);
362
    }
363

    
364
    @Override
365
    public List<UuidAndTitleCache<Classification>> getUuidAndTitleCache(Integer limit, String pattern) {
366
        return dao.getUuidAndTitleCache(limit, pattern);
367
    }
368

    
369
    @Override
370
    public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(
371
            TaxonNode taxonNode, List<String> propertyPaths, int size,
372
            int height, int widthOrDuration, String[] mimeTypes) {
373

    
374
        TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<UUID, List<MediaRepresentation>>();
375
        List<Media> taxonMedia = new ArrayList<Media>();
376
        List<MediaRepresentation> mediaRepresentations = new ArrayList<MediaRepresentation>();
377

    
378
        //add all media of the children to the result map
379
        if (taxonNode != null){
380

    
381
            List<TaxonNode> nodes = new ArrayList<TaxonNode>();
382

    
383
            nodes.add(loadTaxonNode(taxonNode, propertyPaths));
384
            nodes.addAll(loadChildNodesOfTaxonNode(taxonNode, propertyPaths));
385

    
386
            if (nodes != null){
387
                for(TaxonNode node : nodes){
388
                    Taxon taxon = node.getTaxon();
389
                    for (TaxonDescription taxonDescription: taxon.getDescriptions()){
390
                        for (DescriptionElementBase descriptionElement: taxonDescription.getElements()){
391
                            for(Media media : descriptionElement.getMedia()){
392
                                taxonMedia.add(media);
393

    
394
                                //find the best matching representation
395
                                mediaRepresentations.add(MediaUtils.findBestMatchingRepresentation(media,null, size, height, widthOrDuration, mimeTypes));
396

    
397
                            }
398
                        }
399
                    }
400
                    result.put(taxon.getUuid(), mediaRepresentations);
401

    
402
                }
403
            }
404

    
405
        }
406

    
407
        return result;
408

    
409
    }
410

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

    
415
        return getAllMediaForChildNodes(node, propertyPaths, size, height, widthOrDuration, mimeTypes);
416
    }
417

    
418
    @Override
419
    @Transactional(readOnly = false)
420
    public void updateTitleCache(Class<? extends Classification> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Classification> cacheStrategy, IProgressMonitor monitor) {
421
        if (clazz == null){
422
            clazz = Classification.class;
423
        }
424
        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
425
    }
426

    
427
    /**
428
     *
429
     * @param allNodesOfClassification
430
     * @return null - if  allNodesOfClassification is empty <br>
431
     */
432

    
433
    private Map<String, List<TaxonNode>> getSortedGenusList(Collection<TaxonNode> allNodesOfClassification){
434

    
435
    	if(allNodesOfClassification == null || allNodesOfClassification.isEmpty()){
436
    		return null;
437
    	}
438
    	Map<String, List<TaxonNode>> sortedGenusMap = new HashMap<String, List<TaxonNode>>();
439
    	for(TaxonNode node:allNodesOfClassification){
440
    		final TaxonNode tn = node;
441
    		Taxon taxon = node.getTaxon();
442
    		NonViralName name = CdmBase.deproxy(taxon.getName(), NonViralName.class);
443
    		String genusOrUninomial = name.getGenusOrUninomial();
444
    		//if rank unknown split string and take first word
445
    		if(genusOrUninomial == null){
446
    			String titleCache = taxon.getTitleCache();
447
    			String[] split = titleCache.split("\\s+");
448
    			for(String s:split){
449
    				genusOrUninomial = s;
450
    				break;
451
    			}
452
    		}
453
    		//if node has children
454

    
455
    		//retrieve list from map if not create List
456
    		if(sortedGenusMap.containsKey(genusOrUninomial)){
457
    			List<TaxonNode> list = sortedGenusMap.get(genusOrUninomial);
458
    			list.add(node);
459
    			sortedGenusMap.put(genusOrUninomial, list);
460
    		}else{
461
    			//create List for genus
462
    			List<TaxonNode> list = new ArrayList<TaxonNode>();
463
    			list.add(node);
464
    			sortedGenusMap.put(genusOrUninomial, list);
465
    		}
466
    	}
467
    	return sortedGenusMap;
468
    }
469

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

    
488
    	final String APPENDIX = "repaired";
489
    	String titleCache = org.apache.commons.lang.StringUtils.isBlank(classification.getTitleCache()) ? " " : classification.getTitleCache() ;
490
    	//TODO classification clone???
491
    	Classification newClassification = Classification.NewInstance(titleCache +" "+ APPENDIX);
492
    	newClassification.setReference(classification.getReference());
493

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

    
547
    			if(!tn.isTopmostNode()){
548
    				continue; //skip to next taxonNode
549
    			}
550

    
551
    			TaxonNode clone = (TaxonNode) tn.clone();
552
    			//FIXME: citation from node
553
    			//TODO: addchildNode without citation and references
554
//    			TaxonNode taxonNode = parentNode.addChildNode(clone, classification.getCitation(), classification.getMicroReference());
555
    			TaxonNode taxonNode = parentNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
556
    			result.addUnChangedObject(clone);
557
    			if(tn.hasChildNodes()){
558
    				//save hierarchy in new classification
559
    				List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tn, taxonNode, result);
560
    				if(copyAllChildrenToTaxonNode != null){
561
    					listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
562
    				}
563
    			}
564
    		}
565
    	}
566
    	dao.saveOrUpdate(newClassification);
567
    	result.setCdmEntity(newClassification);
568
    	return result;
569
    }
570

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

    
600
    /**
601
     * {@inheritDoc}
602
     */
603
    @Override
604
    public ClassificationLookupDTO classificationLookup(Classification classification) {
605
        return dao.classificationLookup(classification);
606
    }
607

    
608

    
609
    @Override
610
    public DeleteResult delete(UUID classificationUuid, TaxonDeletionConfigurator config){
611
        DeleteResult result = new DeleteResult();
612
        Classification classification = dao.findByUuid(classificationUuid);
613
        if (classification == null){
614
            result.addException(new IllegalArgumentException("The classification does not exist in database."));
615
            result.setAbort();
616
            return result;
617
        }
618
        if (!classification.hasChildNodes()){
619
            dao.delete(classification);
620
        }
621
        if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE) ){
622
            TaxonNode root = classification.getRootNode();
623
            taxonNodeDao.delete(root, true);
624
            dao.delete(classification);
625
        }
626

    
627

    
628
        return result;
629
    }
630

    
631
    @Override
632
    public List<GroupedTaxonDTO> groupTaxaByHigherTaxon(List<UUID> originalTaxonUuids, UUID classificationUuid, Rank minRank, Rank maxRank){
633
        List<GroupedTaxonDTO> result = new ArrayList<>();
634

    
635
        //get treeindex for each taxonUUID
636
        Map<UUID, String> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
637

    
638
        //build treeindex list (or tree)
639
        List<String> treeIndexClosure = new ArrayList<>();
640
        for (String treeIndex : taxonIdTreeIndexMap.values()){
641
            String[] splits = treeIndex.substring(1).split(ITreeNode.separator);
642
            String currentIndex = ITreeNode.separator;
643
            for (String split : splits){
644
                if (split.equals("")){
645
                    continue;
646
                }
647
                currentIndex += split + ITreeNode.separator;
648
                if (!treeIndexClosure.contains(currentIndex) && !split.startsWith(ITreeNode.treePrefix)){
649
                    treeIndexClosure.add(currentIndex);
650
                }
651
            }
652
        }
653

    
654
        //get rank sortindex for all parent taxa with sortindex <= minRank and sortIndex >= maxRank (if available)
655
        Integer minRankOrderIndex = minRank == null ? null : minRank.getOrderIndex();
656
        Integer maxRankOrderIndex = maxRank == null ? null : maxRank.getOrderIndex();
657
        Map<String, Integer> treeIndexSortIndexMapTmp = taxonNodeDao.rankOrderIndexForTreeIndex(treeIndexClosure, minRankOrderIndex, maxRankOrderIndex);
658

    
659
        //remove all treeindex with "exists child in above map(and child.sortindex > xxx)
660
        List<String> treeIndexList = new ArrayList<>(treeIndexSortIndexMapTmp.keySet());
661
        Collections.sort(treeIndexList, new TreeIndexComparator());
662
        Map<String, Integer> treeIndexSortIndexMap = new HashMap<>();
663
        String lastTreeIndex = null;
664
        for (String treeIndex : treeIndexList){
665
            if (lastTreeIndex != null && treeIndex.startsWith(lastTreeIndex)){
666
                treeIndexSortIndexMap.remove(lastTreeIndex);
667
            }
668
            treeIndexSortIndexMap.put(treeIndex, treeIndexSortIndexMapTmp.get(treeIndex));
669
            lastTreeIndex = treeIndex;
670
        }
671

    
672
        //get taxonID for treeIndexes
673
        Map<String, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(treeIndexSortIndexMap.keySet());
674

    
675
        //fill result list
676
        for (UUID originalTaxonUuid : originalTaxonUuids){
677
            GroupedTaxonDTO item = new GroupedTaxonDTO();
678
            result.add(item);
679
            item.setTaxonUuid(originalTaxonUuid);
680
            String groupIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
681
            while (groupIndex != null){
682
                if (treeIndexTaxonIdMap.get(groupIndex) != null){
683
                    UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupIndex);
684
                    item.setGroupTaxonUuid(uuidAndLabel.getUuid());
685
                    item.setGroupTaxonName(uuidAndLabel.getTitleCache());
686
                    break;
687
                }else{
688
                    int index = groupIndex.substring(0, groupIndex.length()-1).lastIndexOf(ITreeNode.separator);
689
                    groupIndex = index<0 ? null : groupIndex.substring(0, index+1);
690
                }
691
            }
692
        }
693

    
694
        return result;
695
    }
696

    
697
    /**
698
     * {@inheritDoc}
699
     */
700
    @Override
701
    public UUID getTaxonNodeUuidByTaxonUuid(UUID classificationUuid, UUID taxonUuid) {
702
        Map<UUID, UUID> map = dao.getTaxonNodeUuidByTaxonUuid(classificationUuid, Arrays.asList(taxonUuid));
703
        UUID taxonNodeUuid = map.get(taxonUuid);
704
        return taxonNodeUuid;
705
    }
706

    
707
    /**
708
     * {@inheritDoc}
709
     */
710
    @Override
711
    public TaxonInContextDTO getTaxonInContext(UUID classificationUuid, UUID taxonUuid,
712
            Boolean doChildren, Boolean doSynonyms, List<UUID> ancestorMarkers,
713
            NodeSortMode sortMode) {
714
        TaxonInContextDTO result = new TaxonInContextDTO();
715
        TaxonBase<?> taxonBase = taxonDao.load(taxonUuid);
716

    
717
        TaxonNameBase<?,?> name = taxonBase.getName();
718
        result.setNameUuid(name.getUuid());
719
        result.setNameLabel(name.getTitleCache());
720
        if (name.isInstanceOf(NonViralName.class)){
721
            NonViralName<?> nvn = CdmBase.deproxy(name, NonViralName.class);
722

    
723
            result.setNameWithoutAuthor(nvn.getNameCache());
724
            result.setGenusOrUninomial(nvn.getGenusOrUninomial());
725
            result.setInfraGenericEpithet(nvn.getInfraGenericEpithet());
726
            result.setSpeciesEpithet(nvn.getSpecificEpithet());
727
            result.setInfraSpecificEpithet(nvn.getInfraSpecificEpithet());
728

    
729
            result.setAuthorship(nvn.getAuthorshipCache());
730

    
731
            Rank rank = name.getRank();
732
            if (rank != null){
733
                result.setRankUuid(rank.getUuid());
734
                result.setRankLabel(rank.getTitleCache());
735
            }
736
        }
737

    
738
        UUID taxonNodeUuid = getTaxonNodeUuidByTaxonUuid(classificationUuid, taxonUuid);
739
        result.setTaxonNodeUuid(taxonNodeUuid);
740

    
741
        boolean recursive = false;
742
        int pageSize = Integer.MAX_VALUE;
743
        int pageIndex = 0;
744
        Pager<TaxonNodeDto> children = taxonNodeService.pageChildNodesDTOs(taxonNodeUuid, recursive, doSynonyms, sortMode, pageSize, pageIndex);
745

    
746
        //children
747
        for (TaxonNodeDto childDto : children.getRecords()){
748
            if (doChildren && childDto.getStatus().equals(TaxonStatus.Accepted)){
749
                EntityDTO<Taxon> child = new EntityDTO<Taxon>(childDto.getTaxonUuid(), childDto.getTitleCache());
750
                result.addChild(child);
751
            }else if (doSynonyms && childDto.getStatus().isSynonym()){
752
                EntityDTO<Synonym> child = new EntityDTO<Synonym>(childDto.getTaxonUuid(), childDto.getTitleCache());
753
                result.addSynonym(child);
754
            }
755
        }
756

    
757
        //marked ancestors
758
        return result;
759
    }
760

    
761
}
(7-7/98)