Project

General

Profile

Download (41.2 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9

    
10
package eu.etaxonomy.cdm.api.service;
11

    
12
import java.util.ArrayList;
13
import java.util.Arrays;
14
import java.util.Collection;
15
import java.util.Collections;
16
import java.util.Comparator;
17
import java.util.HashMap;
18
import java.util.HashSet;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Set;
22
import java.util.TreeMap;
23
import java.util.UUID;
24

    
25
import javax.persistence.EntityNotFoundException;
26

    
27
import org.apache.commons.collections.CollectionUtils;
28
import org.apache.commons.lang.StringUtils;
29
import org.apache.log4j.Logger;
30
import org.springframework.beans.factory.annotation.Autowired;
31
import org.springframework.stereotype.Service;
32
import org.springframework.transaction.annotation.Transactional;
33

    
34
import eu.etaxonomy.cdm.api.service.config.CreateHierarchyForClassificationConfigurator;
35
import eu.etaxonomy.cdm.api.service.config.NodeDeletionConfigurator.ChildHandling;
36
import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
37
import eu.etaxonomy.cdm.api.service.dto.EntityDTO;
38
import eu.etaxonomy.cdm.api.service.dto.GroupedTaxonDTO;
39
import eu.etaxonomy.cdm.api.service.dto.MarkedEntityDTO;
40
import eu.etaxonomy.cdm.api.service.dto.TaxonInContextDTO;
41
import eu.etaxonomy.cdm.api.service.pager.Pager;
42
import eu.etaxonomy.cdm.api.service.pager.PagerUtils;
43
import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
44
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
45
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
46
import eu.etaxonomy.cdm.hibernate.HHH_9751_Util;
47
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
48
import eu.etaxonomy.cdm.model.common.CdmBase;
49
import eu.etaxonomy.cdm.model.common.DefinedTermBase;
50
import eu.etaxonomy.cdm.model.common.ITreeNode;
51
import eu.etaxonomy.cdm.model.common.MarkerType;
52
import eu.etaxonomy.cdm.model.common.TreeIndex;
53
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
54
import eu.etaxonomy.cdm.model.description.TaxonDescription;
55
import eu.etaxonomy.cdm.model.media.Media;
56
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
57
import eu.etaxonomy.cdm.model.media.MediaUtils;
58
import eu.etaxonomy.cdm.model.name.INonViralName;
59
import eu.etaxonomy.cdm.model.name.Rank;
60
import eu.etaxonomy.cdm.model.name.TaxonName;
61
import eu.etaxonomy.cdm.model.reference.Reference;
62
import eu.etaxonomy.cdm.model.taxon.Classification;
63
import eu.etaxonomy.cdm.model.taxon.ITaxonNodeComparator;
64
import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
65
import eu.etaxonomy.cdm.model.taxon.Synonym;
66
import eu.etaxonomy.cdm.model.taxon.Taxon;
67
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
68
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
69
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
70
import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
71
import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
72
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
73
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
74
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
75
import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
76
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
77
import eu.etaxonomy.cdm.persistence.dto.TaxonStatus;
78
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
79
import eu.etaxonomy.cdm.persistence.query.OrderHint;
80
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
81
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
82

    
83
/**
84
 * @author n.hoffmann
85
 * @since Sep 21, 2009
86
 */
87
@Service
88
@Transactional(readOnly = true)
89
public class ClassificationServiceImpl extends IdentifiableServiceBase<Classification, IClassificationDao>
90
    implements IClassificationService {
91
    private static final Logger logger = Logger.getLogger(ClassificationServiceImpl.class);
92

    
93
    @Autowired
94
    private ITaxonNodeDao taxonNodeDao;
95

    
96
    @Autowired
97
    private ITaxonDao taxonDao;
98

    
99
    @Autowired
100
    private ITaxonNodeService taxonNodeService;
101

    
102
    @Autowired
103
    private IDefinedTermDao termDao;
104

    
105
    @Autowired
106
    private IBeanInitializer defaultBeanInitializer;
107

    
108
    @Override
109
    @Autowired
110
    protected void setDao(IClassificationDao dao) {
111
        this.dao = dao;
112
    }
113

    
114
    private Comparator<? super TaxonNode> taxonNodeComparator;
115

    
116
    @Autowired
117
    public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){
118
        this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;
119
    }
120

    
121
    @Override
122
    public TaxonNode loadTaxonNodeByTaxon(Taxon taxon, UUID classificationUuid, List<String> propertyPaths){
123
        Classification tree = dao.load(classificationUuid);
124
        TaxonNode node = tree.getNode(taxon);
125

    
126
        return loadTaxonNode(node.getUuid(), propertyPaths);
127
    }
128

    
129
    @Override
130
    @Deprecated // use loadTaxonNode(UUID, List<String>) instead
131
    public TaxonNode loadTaxonNode(TaxonNode taxonNode, List<String> propertyPaths){
132
        return taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
133
    }
134

    
135
    public TaxonNode loadTaxonNode(UUID taxonNodeUuid, List<String> propertyPaths){
136
        return taxonNodeDao.load(taxonNodeUuid, propertyPaths);
137
    }
138

    
139
    @Override
140
    @Transactional(readOnly = false)
141
    public UpdateResult cloneClassification(UUID classificationUuid,
142
    		String name, Reference sec, TaxonRelationshipType relationshipType) {
143
        UpdateResult result = new UpdateResult();
144
    	Classification classification = load(classificationUuid);
145
    	Classification clone = Classification.NewInstance(name);
146
    	clone.setReference(sec);
147

    
148
    	//clone taxa and taxon nodes
149
    	List<TaxonNode> childNodes = classification.getRootNode().getChildNodes();
150
    	for (TaxonNode taxonNode : childNodes) {
151
    		addChildTaxa(taxonNode, null, clone, relationshipType);
152
    	}
153
    	dao.saveOrUpdate(clone);
154
    	result.setCdmEntity(clone);
155
    	return result;
156
    }
157

    
158
    private void addChildTaxa(TaxonNode originalParentNode, TaxonNode cloneParentNode, Classification classification, TaxonRelationshipType relationshipType){
159
        Reference reference = classification.getReference();
160
    	Taxon cloneTaxon = (Taxon) HibernateProxyHelper.deproxy(originalParentNode.getTaxon(), Taxon.class).clone();
161
    	cloneTaxon.setSec(reference);
162
		String microReference = null;
163
		List<TaxonNode> originalChildNodes = originalParentNode.getChildNodes();
164
		HHH_9751_Util.removeAllNull(originalChildNodes);
165

    
166
		//add relation between taxa
167
		if (relationshipType != null){
168
		    cloneTaxon.addTaxonRelation(originalParentNode.getTaxon(), relationshipType, reference, microReference);
169
		}
170

    
171
		TaxonNode cloneChildNode = null;
172
    	//add taxon node to either parent node or classification (no parent node)
173
    	if(cloneParentNode==null){
174
    		cloneChildNode = classification.addChildTaxon(cloneTaxon, reference, microReference);
175
    	}
176
    	else{
177
    		cloneChildNode = cloneParentNode.addChildTaxon(cloneTaxon, reference, microReference);
178
    	}
179
    	taxonNodeDao.saveOrUpdate(cloneChildNode);
180
    	//add children
181
		for (TaxonNode originalChildNode : originalChildNodes) {
182
    		addChildTaxa(originalChildNode, cloneChildNode, classification, relationshipType);
183
    	}
184
    }
185

    
186
    @Override
187
    public List<TaxonNode> listRankSpecificRootNodes(Classification classification, Rank rank, Integer pageSize,
188
            Integer pageIndex, List<String> propertyPaths) {
189
        return pageRankSpecificRootNodes(classification, rank, pageSize, pageIndex, propertyPaths).getRecords();
190
    }
191

    
192
    @Override
193
    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, Rank rank, Integer pageSize,
194
            Integer pageIndex, List<String> propertyPaths) {
195
        long[] numberOfResults = dao.countRankSpecificRootNodes(classification, rank);
196
        long totalNumberOfResults = numberOfResults[0] + (numberOfResults.length > 1 ? numberOfResults[1] : 0);
197

    
198
        List<TaxonNode> results = new ArrayList<TaxonNode>();
199

    
200
        if (AbstractPagerImpl.hasResultsInRange(totalNumberOfResults, pageIndex, pageSize)) { // no point checking again
201
            Integer limit = PagerUtils.limitFor(pageSize);
202
            Integer start = PagerUtils.startFor(pageSize, pageIndex);
203

    
204
            Integer remainingLimit = limit;
205
            int[] queryIndexes = rank == null ? new int[]{0} : new int[]{0,1};
206

    
207
            for(int queryIndex: queryIndexes) {
208
                if(start != null && start > numberOfResults[queryIndex]) {
209
                    // start in next query with new start value
210
                    start = start - (int)numberOfResults[queryIndex];
211
                    continue;
212
                }
213

    
214
                List<TaxonNode> perQueryResults = dao.listRankSpecificRootNodes(classification, rank, remainingLimit, start, propertyPaths, queryIndex);
215
                results.addAll(perQueryResults);
216
                if(remainingLimit != null ){
217
                    remainingLimit = remainingLimit - results.size();
218
                    if(remainingLimit <= 0) {
219
                        // no need to run further queries if first query returned enough items!
220
                        break;
221
                    }
222
                    // start at with fist item of next query to fetch the remaining items
223
                    start = 0;
224
                }
225
            }
226
        }
227
//        long start_t = System.currentTimeMillis();
228
        Collections.sort(results, taxonNodeComparator); // TODO is ordering during the hibernate query in the dao possible?
229
//        System.err.println("service.pageRankSpecificRootNodes() - Collections.sort(results,  taxonNodeComparator) " + (System.currentTimeMillis() - start_t));
230
        return new DefaultPagerImpl<TaxonNode>(pageIndex, (int) totalNumberOfResults, pageSize, results);
231

    
232
    }
233

    
234
    /**
235
     * @implements {@link IClassificationService#loadTreeBranch(TaxonNode, Rank, List)
236
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#loadTreeBranchTo(eu.etaxonomy.cdm.model.taxon.TaxonNode, eu.etaxonomy.cdm.model.name.Rank, java.util.List)
237
     * FIXME Candidate for harmonization
238
     * move to classification service
239
     */
240
    @Override
241
    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, Rank baseRank, List<String> propertyPaths){
242

    
243
        TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
244
        if(baseRank != null){
245
            baseRank = (Rank) termDao.load(baseRank.getUuid());
246
        }
247
        List<TaxonNode> pathToRoot = new ArrayList<TaxonNode>();
248
        pathToRoot.add(thisNode);
249

    
250
        while(!thisNode.isTopmostNode()){
251
            //TODO why do we need to deproxy here?
252
            //     without this thisNode.getParent() will return NULL in
253
            //     some cases (environment dependend?) even if the parent exits
254
            TaxonNode parentNode = CdmBase.deproxy(thisNode, TaxonNode.class).getParent();
255

    
256
            if(parentNode == null){
257
                throw new NullPointerException("taxonNode " + thisNode + " must have a parent since it is not top most");
258
            }
259
            if(parentNode.getTaxon() == null){
260
                throw new NullPointerException("The taxon associated with taxonNode " + parentNode + " is NULL");
261
            }
262
            if(parentNode.getTaxon().getName() == null){
263
                throw new NullPointerException("The name of the taxon associated with taxonNode " + parentNode + " is NULL");
264
            }
265

    
266
            Rank parentNodeRank = parentNode.getTaxon().getName() == null ? null : parentNode.getTaxon().getName().getRank();
267
            // stop if the next parent is higher than the baseRank
268
            if(baseRank != null && parentNodeRank != null && baseRank.isLower(parentNodeRank)){
269
                break;
270
            }
271

    
272
            pathToRoot.add(parentNode);
273
            thisNode = parentNode;
274
        }
275

    
276
        // initialize and invert order of nodes in list
277
        defaultBeanInitializer.initializeAll(pathToRoot, propertyPaths);
278
        Collections.reverse(pathToRoot);
279

    
280
        return pathToRoot;
281
    }
282

    
283
    @Override
284
    public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification, Rank baseRank, List<String> propertyPaths){
285
        Classification tree = dao.load(classification.getUuid());
286
        taxon = (Taxon) taxonDao.load(taxon.getUuid());
287
        TaxonNode node = tree.getNode(taxon);
288
        if(node == null){
289
            logger.warn("The specified taxon is not found in the given tree.");
290
            return null;
291
        }
292
        return loadTreeBranch(node, baseRank, propertyPaths);
293
    }
294

    
295

    
296
    @Override
297
    public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
298
            List<String> propertyPaths) {
299
        taxonNode = taxonNodeDao.load(taxonNode.getUuid());
300
        List<TaxonNode> childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
301
        defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
302
        Collections.sort(childNodes, taxonNodeComparator);
303
        return childNodes;
304
    }
305

    
306
    @Override
307
    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
308
            Integer pageIndex, List<String> propertyPaths){
309

    
310
        Classification classification = dao.load(classificationUuid);
311
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
312

    
313
        List<TaxonNode> results = dao.listChildrenOf(taxon, classification, pageSize, pageIndex, propertyPaths);
314
        Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
315
        return results;
316
    }
317

    
318
    @Override
319
    public Pager<TaxonNode> pageSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
320
            Integer pageIndex, List<String> propertyPaths){
321

    
322
        Classification classification = dao.load(classificationUuid);
323
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
324

    
325
        long numberOfResults = dao.countSiblingsOf(taxon, classification);
326

    
327
        List<TaxonNode> results;
328
        if(PagerUtils.hasResultsInRange(numberOfResults, pageIndex, pageSize)) {
329
            results = dao.listSiblingsOf(taxon, classification, pageSize, pageIndex, propertyPaths);
330
            Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
331
        } else {
332
            results = new ArrayList<>();
333
        }
334

    
335
        return new DefaultPagerImpl<TaxonNode>(pageIndex, numberOfResults, pageSize, results);
336
    }
337

    
338
    @Override
339
    public List<TaxonNode> listSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
340
            Integer pageIndex, List<String> propertyPaths){
341

    
342
        Pager<TaxonNode> pager = pageSiblingsOfTaxon(taxonUuid, classificationUuid, pageSize, pageIndex, propertyPaths);
343
        return pager.getRecords();
344
    }
345

    
346
    @Override
347
    public TaxonNode getTaxonNodeByUuid(UUID uuid) {
348
        return taxonNodeDao.findByUuid(uuid);
349
    }
350

    
351
    @Override
352
    public ITaxonTreeNode getTreeNodeByUuid(UUID uuid){
353
        ITaxonTreeNode treeNode = taxonNodeDao.findByUuid(uuid);
354
        if(treeNode == null){
355
            treeNode = dao.findByUuid(uuid);
356
        }
357

    
358
        return treeNode;
359
    }
360

    
361
    @Override
362
    public TaxonNode getRootNode(UUID classificationUuid){
363
        return dao.getRootNode(classificationUuid);
364
    }
365

    
366
    @Override
367
    public List<Classification> listClassifications(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
368
        return dao.list(limit, start, orderHints, propertyPaths);
369
    }
370

    
371
    @Override
372
    public UUID removeTaxonNode(TaxonNode taxonNode) {
373
        return taxonNodeDao.delete(taxonNode);
374
    }
375
    @Override
376
    public UUID removeTreeNode(ITaxonTreeNode treeNode) {
377
        if(treeNode instanceof Classification){
378
            return dao.delete((Classification) treeNode);
379
        }else if(treeNode instanceof TaxonNode){
380
            return taxonNodeDao.delete((TaxonNode)treeNode);
381
        }
382
        return null;
383
    }
384
    @Override
385
    public UUID saveTaxonNode(TaxonNode taxonNode) {
386
        return taxonNodeDao.save(taxonNode).getUuid();
387
    }
388

    
389
    @Override
390
    public Map<UUID, TaxonNode> saveTaxonNodeAll(
391
            Collection<TaxonNode> taxonNodeCollection) {
392
        return taxonNodeDao.saveAll(taxonNodeCollection);
393
    }
394

    
395
    @Override
396
    public UUID saveClassification(Classification classification) {
397

    
398
       taxonNodeDao.saveOrUpdateAll(classification.getAllNodes());
399
       UUID result =dao.saveOrUpdate(classification);
400
       return result;
401
    }
402

    
403
    @Override
404
    public UUID saveTreeNode(ITaxonTreeNode treeNode) {
405
        if(treeNode instanceof Classification){
406
            return dao.save((Classification) treeNode).getUuid();
407
        }else if(treeNode instanceof TaxonNode){
408
            return taxonNodeDao.save((TaxonNode)treeNode).getUuid();
409
        }
410
        return null;
411
    }
412

    
413
    @Override
414
    public List<TaxonNode> getAllNodes(){
415
        return taxonNodeDao.list(null,null);
416
    }
417

    
418
    @Override
419
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, Integer limit, String pattern) {
420
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid),  limit, pattern);
421
    }
422

    
423
    @Override
424
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification,  Integer limit, String pattern) {
425
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification,  limit, pattern);
426
    }
427

    
428
    @Override
429
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid ) {
430
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), null, null);
431
    }
432

    
433
    @Override
434
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification ) {
435
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, null, null);
436
    }
437

    
438
    @Override
439
    public List<UuidAndTitleCache<Classification>> getUuidAndTitleCache(Integer limit, String pattern) {
440
        return dao.getUuidAndTitleCache(limit, pattern);
441
    }
442

    
443
    @Override
444
    public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(
445
            TaxonNode taxonNode, List<String> propertyPaths, int size,
446
            int height, int widthOrDuration, String[] mimeTypes) {
447

    
448
        TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<UUID, List<MediaRepresentation>>();
449
        List<Media> taxonMedia = new ArrayList<Media>();
450
        List<MediaRepresentation> mediaRepresentations = new ArrayList<MediaRepresentation>();
451

    
452
        //add all media of the children to the result map
453
        if (taxonNode != null){
454

    
455
            List<TaxonNode> nodes = new ArrayList<TaxonNode>();
456

    
457
            nodes.add(loadTaxonNode(taxonNode, propertyPaths));
458
            nodes.addAll(loadChildNodesOfTaxonNode(taxonNode, propertyPaths));
459

    
460
            if (nodes != null){
461
                for(TaxonNode node : nodes){
462
                    Taxon taxon = node.getTaxon();
463
                    for (TaxonDescription taxonDescription: taxon.getDescriptions()){
464
                        for (DescriptionElementBase descriptionElement: taxonDescription.getElements()){
465
                            for(Media media : descriptionElement.getMedia()){
466
                                taxonMedia.add(media);
467

    
468
                                //find the best matching representation
469
                                mediaRepresentations.add(MediaUtils.findBestMatchingRepresentation(media,null, size, height, widthOrDuration, mimeTypes));
470

    
471
                            }
472
                        }
473
                    }
474
                    result.put(taxon.getUuid(), mediaRepresentations);
475

    
476
                }
477
            }
478

    
479
        }
480

    
481
        return result;
482

    
483
    }
484

    
485

    
486
    @Override
487
    @Transactional(readOnly = false)
488
    public void updateTitleCache(Class<? extends Classification> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Classification> cacheStrategy, IProgressMonitor monitor) {
489
        if (clazz == null){
490
            clazz = Classification.class;
491
        }
492
        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
493
    }
494

    
495
    /**
496
     *
497
     * @param allNodesOfClassification
498
     * @return null - if  allNodesOfClassification is empty <br>
499
     */
500

    
501
    private Map<String, List<TaxonNode>> getSortedGenusList(Collection<TaxonNode> allNodesOfClassification){
502

    
503
    	if(allNodesOfClassification == null || allNodesOfClassification.isEmpty()){
504
    		return null;
505
    	}
506
    	Map<String, List<TaxonNode>> sortedGenusMap = new HashMap<>();
507
    	for(TaxonNode node:allNodesOfClassification){
508
    		final TaxonNode tn = node;
509
    		Taxon taxon = node.getTaxon();
510
    		INonViralName name = taxon.getName();
511
    		String genusOrUninomial = name.getGenusOrUninomial();
512
    		//if rank unknown split string and take first word
513
    		if(genusOrUninomial == null){
514
    			String titleCache = taxon.getTitleCache();
515
    			String[] split = titleCache.split("\\s+");
516
    			for(String s:split){
517
    				genusOrUninomial = s;
518
    				break;
519
    			}
520
    		}
521
    		//if node has children
522

    
523
    		//retrieve list from map if not create List
524
    		if(sortedGenusMap.containsKey(genusOrUninomial)){
525
    			List<TaxonNode> list = sortedGenusMap.get(genusOrUninomial);
526
    			list.add(node);
527
    			sortedGenusMap.put(genusOrUninomial, list);
528
    		}else{
529
    			//create List for genus
530
    			List<TaxonNode> list = new ArrayList<TaxonNode>();
531
    			list.add(node);
532
    			sortedGenusMap.put(genusOrUninomial, list);
533
    		}
534
    	}
535
    	return sortedGenusMap;
536
    }
537

    
538
    /**
539
     *
540
     * creates new Classification and parent TaxonNodes at genus level
541
     *
542
     *
543
     * @param map GenusMap which holds a name (Genus) and all the same Taxa as a list
544
     * @param classification you want to improve the hierarchy (will not be modified)
545
     * @param configurator to change certain settings, if null then standard settings will be taken
546
     * @return new classification with parentNodes for each entry in the map
547
     */
548
    @SuppressWarnings({ "unchecked" })
549
	@Transactional(readOnly = false)
550
	@Override
551
    public UpdateResult createHierarchyInClassification(Classification classification, CreateHierarchyForClassificationConfigurator configurator){
552
        UpdateResult result = new UpdateResult();
553
    	classification = dao.findByUuid(classification.getUuid());
554
    	Map<String, List<TaxonNode>> map = getSortedGenusList(classification.getAllNodes());
555

    
556
    	final String APPENDIX = "repaired";
557
    	String titleCache = StringUtils.isBlank(classification.getTitleCache()) ? " " : classification.getTitleCache() ;
558
    	//TODO classification clone???
559
    	Classification newClassification = Classification.NewInstance(titleCache +" "+ APPENDIX);
560
    	newClassification.setReference(classification.getReference());
561

    
562
    	for(Map.Entry<String, List<TaxonNode>> entry:map.entrySet()){
563
    		String genus = entry.getKey();
564
    		List<TaxonNode> listOfTaxonNodes = entry.getValue();
565
    		TaxonNode parentNode = null;
566
    		//Search for genus in list
567
    		for(TaxonNode tNode:listOfTaxonNodes){
568
    			//take that taxonNode as parent and remove from list with all it possible children
569
    			//FIXME NPE for name
570
    			TaxonName name = tNode.getTaxon().getName();
571
    			if(name.getNameCache().equalsIgnoreCase(genus)){
572
    				TaxonNode clone = (TaxonNode) tNode.clone();
573
    				if(!tNode.hasChildNodes()){
574
    					//FIXME remove classification
575
//    					parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
576
    					parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
577
    					//remove taxonNode from list because just added to classification
578
    					result.addUpdatedObject(tNode);
579
    					listOfTaxonNodes.remove(tNode);
580
    				}else{
581
    					//get all childNodes
582
    					//save prior Hierarchy and remove them from the list
583
    					List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tNode, clone, result);
584
//    					parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
585
      					//FIXME remove classification
586
    					parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
587
    					//remove taxonNode from list because just added to classification
588
    					result.addUpdatedObject(tNode);
589
    					listOfTaxonNodes.remove(tNode);
590
    					if(copyAllChildrenToTaxonNode != null){
591
    						listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
592
    					}
593
    				}
594
    				break;
595
    			}
596
    		}
597
    		if(parentNode == null){
598
    			//if no match found in list, create parentNode
599
    			NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
600
    			TaxonName TaxonName = (TaxonName)parser.parseFullName(genus);
601
    			//TODO Sec via configurator
602
    			Taxon taxon = Taxon.NewInstance(TaxonName, null);
603
    			parentNode = newClassification.addChildTaxon(taxon, 0, null, null);
604
    			result.addUpdatedObject(parentNode);
605
    		}
606
    		//iterate over the rest of the list
607
    		for(TaxonNode tn : listOfTaxonNodes){
608
    			//if TaxonNode has a parent and this is not the classification then skip it
609
    			//and add to new classification via the parentNode as children of it
610
    			//this should assures to keep the already existing hierarchy
611
    			//FIXME: Assert is not rootnode --> entrypoint is not classification in future but rather rootNode
612

    
613
    			if(!tn.isTopmostNode()){
614
    				continue; //skip to next taxonNode
615
    			}
616

    
617
    			TaxonNode clone = (TaxonNode) tn.clone();
618
    			//FIXME: citation from node
619
    			//TODO: addchildNode without citation and references
620
//    			TaxonNode taxonNode = parentNode.addChildNode(clone, classification.getCitation(), classification.getMicroReference());
621
    			TaxonNode taxonNode = parentNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
622
    			result.addUnChangedObject(clone);
623
    			if(tn.hasChildNodes()){
624
    				//save hierarchy in new classification
625
    				List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tn, taxonNode, result);
626
    				if(copyAllChildrenToTaxonNode != null){
627
    					listOfTaxonNodes = (List<TaxonNode>) CollectionUtils.removeAll(listOfTaxonNodes, copyAllChildrenToTaxonNode);
628
    				}
629
    			}
630
    		}
631
    	}
632
    	dao.saveOrUpdate(newClassification);
633
    	result.setCdmEntity(newClassification);
634
    	return result;
635
    }
636

    
637
    /**
638
     *
639
     * recursive method to get all childnodes of taxonNode in classification.
640
     *
641
     * @param classification just for References and Citation, can be null
642
     * @param copyFromNode TaxonNode with Children
643
     * @param copyToNode TaxonNode which will receive the children
644
     * @return List of ChildNode which has been added. If node has no children returns null
645
     */
646
   private List<TaxonNode> copyAllChildrenToTaxonNode(TaxonNode copyFromNode, TaxonNode copyToNode, UpdateResult result) {
647
		List<TaxonNode> childNodes;
648
		if(!copyFromNode.hasChildNodes()){
649
			return null;
650
		}else{
651
			childNodes = copyFromNode.getChildNodes();
652
		}
653
		for(TaxonNode childNode:childNodes){
654
			TaxonNode clone = (TaxonNode) childNode.clone();
655
			result.addUnChangedObject(clone);
656
			if(childNode.hasChildNodes()){
657
				copyAllChildrenToTaxonNode(childNode, clone, result);
658
			}
659
			//FIXME: citation from node instead of classification
660
//			copyToNode.addChildNode(clone,classification.getCitation(), classification.getMicroReference());
661
			copyToNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
662
		}
663
		return childNodes;
664
	}
665

    
666
    /**
667
     * {@inheritDoc}
668
     */
669
    @Override
670
    public ClassificationLookupDTO classificationLookup(Classification classification) {
671
        return dao.classificationLookup(classification);
672
    }
673

    
674

    
675
    @Override
676
    @Transactional
677
    public DeleteResult delete(UUID classificationUuid, TaxonDeletionConfigurator config){
678
        DeleteResult result = new DeleteResult();
679
        Classification classification = dao.findByUuid(classificationUuid);
680
        if (classification == null){
681
            result.addException(new IllegalArgumentException("The classification does not exist in database."));
682
            result.setAbort();
683
            return result;
684
        }
685
        if (!classification.hasChildNodes()){
686
            dao.delete(classification);
687
            result.addDeletedObject(classification);
688
            return result;
689
        }
690
        if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){
691
//            TaxonNode root = classification.getRootNode();
692
//            result.includeResult(taxonNodeService.deleteTaxonNode(HibernateProxyHelper.deproxy(root), config));
693
//            result.addDeletedObject(classification);
694
            dao.delete(classification);
695
            result.addDeletedObject(classification);
696
            return result;
697
        }
698

    
699

    
700
        return result;
701
    }
702

    
703
    @Override
704
    public List<GroupedTaxonDTO> groupTaxaByHigherTaxon(List<UUID> originalTaxonUuids, UUID classificationUuid, Rank minRank, Rank maxRank){
705
        List<GroupedTaxonDTO> result = new ArrayList<>();
706

    
707
        //get treeindex for each taxonUUID
708
        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
709

    
710
        //build treeindex list (or tree)
711
        //TODO make it work with TreeIndex or move there
712
        List<String> treeIndexClosureStr = new ArrayList<>();
713
        for (TreeIndex treeIndex : taxonIdTreeIndexMap.values()){
714
            String[] splits = treeIndex.toString().substring(1).split(ITreeNode.separator);
715
            String currentIndex = ITreeNode.separator;
716
            for (String split : splits){
717
                if (split.equals("")){
718
                    continue;
719
                }
720
                currentIndex += split + ITreeNode.separator;
721
                if (!treeIndexClosureStr.contains(currentIndex) && !split.startsWith(ITreeNode.treePrefix)){
722
                    treeIndexClosureStr.add(currentIndex);
723
                }
724
            }
725
        }
726

    
727
        //get rank sortindex for all parent taxa with sortindex <= minRank and sortIndex >= maxRank (if available)
728
        Integer minRankOrderIndex = minRank == null ? null : minRank.getOrderIndex();
729
        Integer maxRankOrderIndex = maxRank == null ? null : maxRank.getOrderIndex();
730
        List<TreeIndex> treeIndexClosure = TreeIndex.NewListInstance(treeIndexClosureStr);
731

    
732
        Map<TreeIndex, Integer> treeIndexSortIndexMapTmp = taxonNodeDao.rankOrderIndexForTreeIndex(treeIndexClosure, minRankOrderIndex, maxRankOrderIndex);
733

    
734
        //remove all treeindex with "exists child in above map(and child.sortindex > xxx)
735
        List<TreeIndex> treeIndexList = TreeIndex.sort(treeIndexSortIndexMapTmp.keySet());
736

    
737
        Map<TreeIndex, Integer> treeIndexSortIndexMap = new HashMap<>();
738
        TreeIndex lastTreeIndex = null;
739
        for (TreeIndex treeIndex : treeIndexList){
740
            if (lastTreeIndex != null && lastTreeIndex.hasChild(treeIndex)){
741
                treeIndexSortIndexMap.remove(lastTreeIndex);
742
            }
743
            treeIndexSortIndexMap.put(treeIndex, treeIndexSortIndexMapTmp.get(treeIndex));
744
            lastTreeIndex = treeIndex;
745
        }
746

    
747
        //get taxonID for treeIndexes
748
        Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(treeIndexSortIndexMap.keySet());
749

    
750
        //fill result list
751
        for (UUID originalTaxonUuid : originalTaxonUuids){
752
            GroupedTaxonDTO item = new GroupedTaxonDTO();
753
            result.add(item);
754
            item.setTaxonUuid(originalTaxonUuid);
755
            TreeIndex groupTreeIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
756
            String groupIndexX = TreeIndex.toString(groupTreeIndex);
757
            while (groupTreeIndex != null){
758
                if (treeIndexTaxonIdMap.get(groupTreeIndex) != null){
759
                    UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupTreeIndex);
760
                    item.setGroupTaxonUuid(uuidAndLabel.getUuid());
761
                    item.setGroupTaxonName(uuidAndLabel.getTitleCache());
762
                    break;
763
                }else{
764
                    groupTreeIndex = groupTreeIndex.parent();
765
//                    int index = groupIndex.substring(0, groupIndex.length()-1).lastIndexOf(ITreeNode.separator);
766
//                    groupIndex = index < 0 ? null : groupIndex.substring(0, index+1);
767
                }
768
            }
769
        }
770

    
771
        return result;
772
    }
773

    
774
    /**
775
     * {@inheritDoc}
776
     */
777
    @Override
778
    public List<GroupedTaxonDTO> groupTaxaByMarkedParents(List<UUID> originalTaxonUuids, UUID classificationUuid,
779
            MarkerType markerType, Boolean flag) {
780

    
781
        List<GroupedTaxonDTO> result = new ArrayList<>();
782

    
783
        //get treeindex for each taxonUUID
784
        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
785

    
786
        //get all marked tree indexes
787
        Set<TreeIndex> markedTreeIndexes = dao.getMarkedTreeIndexes(markerType, flag);
788

    
789

    
790
        Map<TreeIndex, TreeIndex> groupedMap = TreeIndex.group(markedTreeIndexes, taxonIdTreeIndexMap.values());
791
        Set<TreeIndex> notNullGroups = new HashSet<>(groupedMap.values());
792
        notNullGroups.remove(null);
793

    
794
        //get taxonInfo for treeIndexes
795
        Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(notNullGroups);
796

    
797
        //fill result list
798
        for (UUID originalTaxonUuid : originalTaxonUuids){
799
            GroupedTaxonDTO item = new GroupedTaxonDTO();
800
            result.add(item);
801
            item.setTaxonUuid(originalTaxonUuid);
802

    
803
            TreeIndex toBeGroupedTreeIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
804
            TreeIndex groupTreeIndex = groupedMap.get(toBeGroupedTreeIndex);
805
            UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupTreeIndex);
806
            if (uuidAndLabel != null){
807
                item.setGroupTaxonUuid(uuidAndLabel.getUuid());
808
                item.setGroupTaxonName(uuidAndLabel.getTitleCache());
809
            }
810
        }
811

    
812
        return result;
813
    }
814

    
815
    /**
816
     * {@inheritDoc}
817
     */
818
    @Override
819
    public UUID getTaxonNodeUuidByTaxonUuid(UUID classificationUuid, UUID taxonUuid) {
820
        Map<UUID, UUID> map = dao.getTaxonNodeUuidByTaxonUuid(classificationUuid, Arrays.asList(taxonUuid));
821
        UUID taxonNodeUuid = map.get(taxonUuid);
822
        return taxonNodeUuid;
823
    }
824

    
825
    /**
826
     * {@inheritDoc}
827
     */
828
    @Override
829
    public TaxonInContextDTO getTaxonInContext(UUID classificationUuid, UUID taxonBaseUuid,
830
            Boolean doChildren, Boolean doSynonyms, List<UUID> ancestorMarkers,
831
            NodeSortMode sortMode) {
832
        TaxonInContextDTO result = new TaxonInContextDTO();
833

    
834
        TaxonBase<?> taxonBase = taxonDao.load(taxonBaseUuid);
835
        if (taxonBase == null){
836
            throw new EntityNotFoundException("Taxon with uuid " + taxonBaseUuid + " not found in datasource");
837
        }
838
        boolean isSynonym = false;
839
        Taxon acceptedTaxon;
840
        if (taxonBase.isInstanceOf(Synonym.class)){
841
            isSynonym = true;
842
            Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
843
            acceptedTaxon = synonym.getAcceptedTaxon();
844
            if (acceptedTaxon == null) {
845
                throw new EntityNotFoundException("Accepted taxon not found for synonym"  );
846
            }
847
            TaxonStatus taxonStatus = TaxonStatus.Synonym;
848
            if (synonym.getName()!= null && acceptedTaxon.getName() != null
849
                    && synonym.getName().getHomotypicalGroup().equals(acceptedTaxon.getName().getHomotypicalGroup())){
850
                taxonStatus = TaxonStatus.SynonymObjective;
851
            }
852
            result.setTaxonStatus(taxonStatus);
853

    
854
        }else{
855
            acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
856
            result.setTaxonStatus(TaxonStatus.Accepted);
857
        }
858
        UUID acceptedTaxonUuid = acceptedTaxon.getUuid();
859

    
860
        UUID taxonNodeUuid = getTaxonNodeUuidByTaxonUuid(classificationUuid, acceptedTaxonUuid);
861
        if (taxonNodeUuid == null) {
862
            throw new EntityNotFoundException("Taxon not found in classficiation with uuid " + classificationUuid + ". Either classification does not exist or does not contain taxon/synonym with uuid " + taxonBaseUuid );
863
        }
864
        result.setTaxonNodeUuid(taxonNodeUuid);
865

    
866
        //TODO make it a dao call
867
        Taxon parentTaxon = getParentTaxon(classificationUuid, acceptedTaxon);
868
        if (parentTaxon != null){
869
            result.setParentTaxonUuid(parentTaxon.getUuid());
870
            result.setParentTaxonLabel(parentTaxon.getTitleCache());
871
            if (parentTaxon.getName() != null){
872
                result.setParentNameLabel(parentTaxon.getName().getTitleCache());
873
            }
874
        }
875

    
876

    
877
        result.setTaxonUuid(taxonBaseUuid);
878
        result.setClassificationUuid(classificationUuid);
879
        if (taxonBase.getSec() != null){
880
            result.setSecundumUuid(taxonBase.getSec().getUuid());
881
            result.setSecundumLabel(taxonBase.getSec().getTitleCache());
882
        }
883
        result.setTaxonLabel(taxonBase.getTitleCache());
884

    
885
        TaxonName name = taxonBase.getName();
886
        result.setNameUuid(name.getUuid());
887
        result.setNameLabel(name.getTitleCache());
888
        result.setNameWithoutAuthor(name.getNameCache());
889
        result.setGenusOrUninomial(name.getGenusOrUninomial());
890
        result.setInfraGenericEpithet(name.getInfraGenericEpithet());
891
        result.setSpeciesEpithet(name.getSpecificEpithet());
892
        result.setInfraSpecificEpithet(name.getInfraSpecificEpithet());
893

    
894
        result.setAuthorship(name.getAuthorshipCache());
895

    
896
        Rank rank = name.getRank();
897
        if (rank != null){
898
            result.setRankUuid(rank.getUuid());
899
            String rankLabel = rank.getAbbreviation();
900
            if (StringUtils.isBlank(rankLabel)){
901
                rankLabel = rank.getLabel();
902
            }
903
            result.setRankLabel(rankLabel);
904
        }
905

    
906
        boolean recursive = false;
907
        Integer pageSize = null;
908
        Integer pageIndex = null;
909
        Pager<TaxonNodeDto> children = taxonNodeService.pageChildNodesDTOs(taxonNodeUuid, recursive, doSynonyms, sortMode, pageSize, pageIndex);
910

    
911
        //children
912
        if(! isSynonym) {
913
            for (TaxonNodeDto childDto : children.getRecords()){
914
                if (doChildren && childDto.getStatus().equals(TaxonStatus.Accepted)){
915
                    EntityDTO<Taxon> child = new EntityDTO<Taxon>(childDto.getTaxonUuid(), childDto.getTitleCache());
916
                    result.addChild(child);
917
                }else if (doSynonyms && childDto.getStatus().isSynonym()){
918
                    EntityDTO<Synonym> child = new EntityDTO<Synonym>(childDto.getTaxonUuid(), childDto.getTitleCache());
919
                    result.addSynonym(child);
920
                }
921
            }
922
        }else{
923
            result.setAcceptedTaxonUuid(acceptedTaxonUuid);
924
            String nameTitel = acceptedTaxon.getName() == null ? null : acceptedTaxon.getName().getTitleCache();
925
            result.setAcceptedTaxonLabel(acceptedTaxon.getTitleCache());
926
            result.setAcceptedNameLabel(nameTitel);
927
        }
928

    
929
        //marked ancestors
930
        if (ancestorMarkers != null && !ancestorMarkers.isEmpty()){
931
            List<DefinedTermBase> markerTypesTerms = termDao.list(ancestorMarkers, pageSize, null, null, null);
932
            List<MarkerType> markerTypes = new ArrayList<>();
933
            for (DefinedTermBase<?> term : markerTypesTerms){
934
                if (term.isInstanceOf(MarkerType.class)){
935
                    markerTypes.add(CdmBase.deproxy(term, MarkerType.class));
936
                }
937
            }
938
            if (! markerTypes.isEmpty()){
939
                TaxonNode node = taxonNodeDao.findByUuid(taxonNodeUuid);
940
                handleAncestorsForMarkersRecursive(result, markerTypes, node);
941
            }
942
        }
943

    
944
        return result;
945
    }
946

    
947
    /**
948
     * @param classificationUuid
949
     * @param acceptedTaxon
950
     * @return
951
     */
952
    private Taxon getParentTaxon(UUID classificationUuid, Taxon acceptedTaxon) {
953
        if (classificationUuid == null){
954
            return null;
955
        }
956
        TaxonNode parent = null;
957
        for (TaxonNode node : acceptedTaxon.getTaxonNodes()){
958
            if (classificationUuid.equals(node.getClassification().getUuid())){
959
                parent = node.getParent();
960
            }
961
        }
962
        if (parent != null){
963
            return parent.getTaxon();
964
        }
965
        return null;
966
    }
967

    
968
    /**
969
     * @param result
970
     * @param markerTypes
971
     * @param node
972
     */
973
    private void handleAncestorsForMarkersRecursive(TaxonInContextDTO result, List<MarkerType> markerTypes, TaxonNode node) {
974
       for (MarkerType type : markerTypes){
975
            Taxon taxon = node.getTaxon();
976
            if (taxon != null && taxon.hasMarker(type, true)){
977
                String label =  taxon.getName() == null? taxon.getTitleCache() : taxon.getName().getTitleCache();
978
                MarkedEntityDTO<Taxon> dto = new MarkedEntityDTO<>(type, true, taxon.getUuid(), label);
979
                result.addMarkedAncestor(dto);
980
            }
981
        }
982
        TaxonNode parentNode = node.getParent();
983
        if (parentNode != null){
984
            handleAncestorsForMarkersRecursive(result, markerTypes, parentNode);
985
        }
986
    }
987

    
988

    
989
}
(7-7/105)