Project

General

Profile

Download (41.5 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.HibernateProxyHelper;
47
import eu.etaxonomy.cdm.model.common.CdmBase;
48
import eu.etaxonomy.cdm.model.common.DefinedTermBase;
49
import eu.etaxonomy.cdm.model.common.ITreeNode;
50
import eu.etaxonomy.cdm.model.common.MarkerType;
51
import eu.etaxonomy.cdm.model.common.TreeIndex;
52
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
53
import eu.etaxonomy.cdm.model.description.TaxonDescription;
54
import eu.etaxonomy.cdm.model.media.Media;
55
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
56
import eu.etaxonomy.cdm.model.media.MediaUtils;
57
import eu.etaxonomy.cdm.model.name.NonViralName;
58
import eu.etaxonomy.cdm.model.name.Rank;
59
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
60
import eu.etaxonomy.cdm.model.reference.Reference;
61
import eu.etaxonomy.cdm.model.taxon.Classification;
62
import eu.etaxonomy.cdm.model.taxon.ITaxonNodeComparator;
63
import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
64
import eu.etaxonomy.cdm.model.taxon.Synonym;
65
import eu.etaxonomy.cdm.model.taxon.Taxon;
66
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
67
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
68
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
69
import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
70
import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
71
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
72
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
73
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
74
import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
75
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
76
import eu.etaxonomy.cdm.persistence.dto.TaxonStatus;
77
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
78
import eu.etaxonomy.cdm.persistence.query.OrderHint;
79
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
80
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
81

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

    
92
    @Autowired
93
    private ITaxonNodeDao taxonNodeDao;
94

    
95
    @Autowired
96
    private ITaxonDao taxonDao;
97

    
98
    @Autowired
99
    private ITaxonNodeService taxonNodeService;
100

    
101
    @Autowired
102
    private IDefinedTermDao termDao;
103

    
104
    @Autowired
105
    private IBeanInitializer defaultBeanInitializer;
106

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

    
113
    private Comparator<? super TaxonNode> taxonNodeComparator;
114

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

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

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

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

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

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

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

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

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

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

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

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

    
196
        List<TaxonNode> results = new ArrayList<TaxonNode>();
197

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

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

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

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

    
230
    }
231

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

    
241
        TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
242
        List<TaxonNode> pathToRoot = new ArrayList<TaxonNode>();
243
        pathToRoot.add(thisNode);
244

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

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

    
261
            Rank parentNodeRank = parentNode.getTaxon().getName() == null ? null : parentNode.getTaxon().getName().getRank();
262
            // stop if the next parent is higher than the baseRank
263
            if(baseRank != null && parentNodeRank != null && baseRank.isLower(parentNodeRank)){
264
                break;
265
            }
266

    
267
            pathToRoot.add(parentNode);
268
            thisNode = parentNode;
269
        }
270

    
271
        // initialize and invert order of nodes in list
272
        defaultBeanInitializer.initializeAll(pathToRoot, propertyPaths);
273
        Collections.reverse(pathToRoot);
274

    
275
        return pathToRoot;
276
    }
277

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

    
290

    
291
    @Override
292
    public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
293
            List<String> propertyPaths) {
294
        taxonNode = taxonNodeDao.load(taxonNode.getUuid());
295
        List<TaxonNode> childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
296
        defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
297
        Collections.sort(childNodes, taxonNodeComparator);
298
        return childNodes;
299
    }
300

    
301
    @Override
302
    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
303
            Integer pageIndex, List<String> propertyPaths){
304

    
305
        Classification classification = dao.load(classificationUuid);
306
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
307

    
308
        List<TaxonNode> results = dao.listChildrenOf(taxon, classification, pageSize, pageIndex, propertyPaths);
309
        Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
310
        return results;
311
    }
312

    
313
    @Override
314
    public Pager<TaxonNode> pageSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
315
            Integer pageIndex, List<String> propertyPaths){
316

    
317
        Classification classification = dao.load(classificationUuid);
318
        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
319

    
320
        long numberOfResults = dao.countSiblingsOf(taxon, classification);
321

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

    
330
        return new DefaultPagerImpl<TaxonNode>(pageIndex, numberOfResults, pageSize, results);
331
    }
332

    
333
    @Override
334
    public List<TaxonNode> listSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
335
            Integer pageIndex, List<String> propertyPaths){
336

    
337
        Pager<TaxonNode> pager = pageSiblingsOfTaxon(taxonUuid, classificationUuid, pageSize, pageIndex, propertyPaths);
338
        return pager.getRecords();
339
    }
340

    
341
    @Override
342
    public TaxonNode getTaxonNodeByUuid(UUID uuid) {
343
        return taxonNodeDao.findByUuid(uuid);
344
    }
345

    
346
    @Override
347
    public ITaxonTreeNode getTreeNodeByUuid(UUID uuid){
348
        ITaxonTreeNode treeNode = taxonNodeDao.findByUuid(uuid);
349
        if(treeNode == null){
350
            treeNode = dao.findByUuid(uuid);
351
        }
352

    
353
        return treeNode;
354
    }
355

    
356
    @Override
357
    public TaxonNode getRootNode(UUID classificationUuid){
358
        return dao.getRootNode(classificationUuid);
359
    }
360

    
361
    @Override
362
    public List<Classification> listClassifications(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
363
        return dao.list(limit, start, orderHints, propertyPaths);
364
    }
365

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

    
384
    @Override
385
    public Map<UUID, TaxonNode> saveTaxonNodeAll(
386
            Collection<TaxonNode> taxonNodeCollection) {
387
        return taxonNodeDao.saveAll(taxonNodeCollection);
388
    }
389

    
390
    @Override
391
    public UUID saveClassification(Classification classification) {
392

    
393
       taxonNodeDao.saveOrUpdateAll(classification.getAllNodes());
394
       UUID result =dao.saveOrUpdate(classification);
395
       return result;
396
    }
397

    
398
    @Override
399
    public UUID saveTreeNode(ITaxonTreeNode treeNode) {
400
        if(treeNode instanceof Classification){
401
            return dao.save((Classification) treeNode).getUuid();
402
        }else if(treeNode instanceof TaxonNode){
403
            return taxonNodeDao.save((TaxonNode)treeNode).getUuid();
404
        }
405
        return null;
406
    }
407

    
408
    @Override
409
    public List<TaxonNode> getAllNodes(){
410
        return taxonNodeDao.list(null,null);
411
    }
412

    
413
    @Override
414
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, Integer limit, String pattern) {
415
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid),  limit, pattern);
416
    }
417

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

    
423
    @Override
424
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid ) {
425
        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), null, null);
426
    }
427

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

    
433
    @Override
434
    public List<UuidAndTitleCache<Classification>> getUuidAndTitleCache(Integer limit, String pattern) {
435
        return dao.getUuidAndTitleCache(limit, pattern);
436
    }
437

    
438
    @Override
439
    public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(
440
            TaxonNode taxonNode, List<String> propertyPaths, int size,
441
            int height, int widthOrDuration, String[] mimeTypes) {
442

    
443
        TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<UUID, List<MediaRepresentation>>();
444
        List<Media> taxonMedia = new ArrayList<Media>();
445
        List<MediaRepresentation> mediaRepresentations = new ArrayList<MediaRepresentation>();
446

    
447
        //add all media of the children to the result map
448
        if (taxonNode != null){
449

    
450
            List<TaxonNode> nodes = new ArrayList<TaxonNode>();
451

    
452
            nodes.add(loadTaxonNode(taxonNode, propertyPaths));
453
            nodes.addAll(loadChildNodesOfTaxonNode(taxonNode, propertyPaths));
454

    
455
            if (nodes != null){
456
                for(TaxonNode node : nodes){
457
                    Taxon taxon = node.getTaxon();
458
                    for (TaxonDescription taxonDescription: taxon.getDescriptions()){
459
                        for (DescriptionElementBase descriptionElement: taxonDescription.getElements()){
460
                            for(Media media : descriptionElement.getMedia()){
461
                                taxonMedia.add(media);
462

    
463
                                //find the best matching representation
464
                                mediaRepresentations.add(MediaUtils.findBestMatchingRepresentation(media,null, size, height, widthOrDuration, mimeTypes));
465

    
466
                            }
467
                        }
468
                    }
469
                    result.put(taxon.getUuid(), mediaRepresentations);
470

    
471
                }
472
            }
473

    
474
        }
475

    
476
        return result;
477

    
478
    }
479

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

    
484
        return getAllMediaForChildNodes(node, propertyPaths, size, height, widthOrDuration, mimeTypes);
485
    }
486

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

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

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

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

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

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

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

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

    
616
    			if(!tn.isTopmostNode()){
617
    				continue; //skip to next taxonNode
618
    			}
619

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

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

    
669
    /**
670
     * {@inheritDoc}
671
     */
672
    @Override
673
    public ClassificationLookupDTO classificationLookup(Classification classification) {
674
        return dao.classificationLookup(classification);
675
    }
676

    
677

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

    
696

    
697
        return result;
698
    }
699

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

    
704
        //get treeindex for each taxonUUID
705
        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
706

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

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

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

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

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

    
744
        //get taxonID for treeIndexes
745
        Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(treeIndexSortIndexMap.keySet());
746

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

    
768
        return result;
769
    }
770

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

    
778
        List<GroupedTaxonDTO> result = new ArrayList<>();
779

    
780
        //get treeindex for each taxonUUID
781
        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
782

    
783
        //get all marked tree indexes
784
        Set<TreeIndex> markedTreeIndexes = dao.getMarkedTreeIndexes(markerType, flag);
785

    
786

    
787
        Map<TreeIndex, TreeIndex> groupedMap = TreeIndex.group(markedTreeIndexes, taxonIdTreeIndexMap.values());
788
        Set<TreeIndex> notNullGroups = new HashSet<>(groupedMap.values());
789
        notNullGroups.remove(null);
790

    
791
        //get taxonInfo for treeIndexes
792
        Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(notNullGroups);
793

    
794
        //fill result list
795
        for (UUID originalTaxonUuid : originalTaxonUuids){
796
            GroupedTaxonDTO item = new GroupedTaxonDTO();
797
            result.add(item);
798
            item.setTaxonUuid(originalTaxonUuid);
799

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

    
809
        return result;
810
    }
811

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

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

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

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

    
857
        UUID taxonNodeUuid = getTaxonNodeUuidByTaxonUuid(classificationUuid, acceptedTaxonUuid);
858
        if (taxonNodeUuid == null) {
859
            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 );
860
        }
861
        result.setTaxonNodeUuid(taxonNodeUuid);
862

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

    
873

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

    
882
        TaxonNameBase<?,?> name = taxonBase.getName();
883
        result.setNameUuid(name.getUuid());
884
        result.setNameLabel(name.getTitleCache());
885
        if (name.isInstanceOf(NonViralName.class)){
886
            NonViralName<?> nvn = CdmBase.deproxy(name, NonViralName.class);
887

    
888
            result.setNameWithoutAuthor(nvn.getNameCache());
889
            result.setGenusOrUninomial(nvn.getGenusOrUninomial());
890
            result.setInfraGenericEpithet(nvn.getInfraGenericEpithet());
891
            result.setSpeciesEpithet(nvn.getSpecificEpithet());
892
            result.setInfraSpecificEpithet(nvn.getInfraSpecificEpithet());
893

    
894
            result.setAuthorship(nvn.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

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

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

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

    
945
        return result;
946
    }
947

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

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

    
989

    
990
}
(7-7/97)