Project

General

Profile

Download (55 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.io.IOException;
13
import java.util.ArrayList;
14
import java.util.Arrays;
15
import java.util.Collection;
16
import java.util.HashMap;
17
import java.util.HashSet;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.Optional;
21
import java.util.Set;
22
import java.util.UUID;
23

    
24
import org.apache.log4j.Logger;
25
import org.apache.lucene.index.Term;
26
import org.apache.lucene.sandbox.queries.FuzzyLikeThisQuery;
27
import org.apache.lucene.search.BooleanClause.Occur;
28
import org.apache.lucene.search.BooleanQuery;
29
import org.apache.lucene.search.BooleanQuery.Builder;
30
import org.apache.lucene.search.TopDocs;
31
import org.apache.lucene.search.WildcardQuery;
32
import org.hibernate.criterion.Criterion;
33
import org.springframework.beans.factory.annotation.Autowired;
34
import org.springframework.beans.factory.annotation.Qualifier;
35
import org.springframework.stereotype.Service;
36
import org.springframework.transaction.annotation.Transactional;
37

    
38
import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
39
import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
40
import eu.etaxonomy.cdm.api.service.dto.TypeDesignationStatusFilter;
41
import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
42
import eu.etaxonomy.cdm.api.service.pager.Pager;
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.api.service.search.DocumentSearchResult;
46
import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
47
import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
48
import eu.etaxonomy.cdm.api.service.search.LuceneParseException;
49
import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
50
import eu.etaxonomy.cdm.api.service.search.QueryFactory;
51
import eu.etaxonomy.cdm.api.service.search.SearchResult;
52
import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
53
import eu.etaxonomy.cdm.api.utility.TaxonNamePartsFilter;
54
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
55
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
56
import eu.etaxonomy.cdm.model.CdmBaseType;
57
import eu.etaxonomy.cdm.model.agent.Person;
58
import eu.etaxonomy.cdm.model.agent.Team;
59
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
60
import eu.etaxonomy.cdm.model.common.CdmBase;
61
import eu.etaxonomy.cdm.model.common.Language;
62
import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
63
import eu.etaxonomy.cdm.model.common.SourcedEntityBase;
64
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
65
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
66
import eu.etaxonomy.cdm.model.name.HybridRelationship;
67
import eu.etaxonomy.cdm.model.name.HybridRelationshipType;
68
import eu.etaxonomy.cdm.model.name.INonViralName;
69
import eu.etaxonomy.cdm.model.name.NameRelationship;
70
import eu.etaxonomy.cdm.model.name.NameRelationshipType;
71
import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
72
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
73
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
74
import eu.etaxonomy.cdm.model.name.Rank;
75
import eu.etaxonomy.cdm.model.name.Registration;
76
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
77
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
78
import eu.etaxonomy.cdm.model.name.TaxonName;
79
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
80
import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
81
import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
82
import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
83
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
84
import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
85
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
86
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
87
import eu.etaxonomy.cdm.model.reference.Reference;
88
import eu.etaxonomy.cdm.model.taxon.Taxon;
89
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
90
import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
91
import eu.etaxonomy.cdm.persistence.dao.common.ISourcedEntityDao;
92
import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
93
import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
94
import eu.etaxonomy.cdm.persistence.dao.name.IHomotypicalGroupDao;
95
import eu.etaxonomy.cdm.persistence.dao.name.INomenclaturalStatusDao;
96
import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
97
import eu.etaxonomy.cdm.persistence.dao.name.ITypeDesignationDao;
98
import eu.etaxonomy.cdm.persistence.dao.term.IOrderedTermVocabularyDao;
99
import eu.etaxonomy.cdm.persistence.dao.term.ITermVocabularyDao;
100
import eu.etaxonomy.cdm.persistence.dto.TaxonNameParts;
101
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
102
import eu.etaxonomy.cdm.persistence.query.MatchMode;
103
import eu.etaxonomy.cdm.persistence.query.OrderHint;
104
import eu.etaxonomy.cdm.strategy.cache.TaggedText;
105
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
106
import eu.etaxonomy.cdm.strategy.match.IMatchStrategy;
107
import eu.etaxonomy.cdm.strategy.match.IMatchable;
108
import eu.etaxonomy.cdm.strategy.match.IParsedMatchStrategy;
109
import eu.etaxonomy.cdm.strategy.match.MatchException;
110
import eu.etaxonomy.cdm.strategy.match.MatchStrategyFactory;
111
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
112

    
113

    
114
@Service
115
@Transactional(readOnly = true)
116
public class NameServiceImpl
117
          extends IdentifiableServiceBase<TaxonName,ITaxonNameDao>
118
          implements INameService {
119
    static private final Logger logger = Logger.getLogger(NameServiceImpl.class);
120

    
121
    @Autowired
122
    protected ITermVocabularyDao vocabularyDao;
123
    @Autowired
124
    protected IOrderedTermVocabularyDao orderedVocabularyDao;
125
    @Autowired
126
    protected IOccurrenceService occurrenceService;
127
    @Autowired
128
    protected ICollectionService collectionService;
129
    @Autowired
130
    protected ITaxonService taxonService;
131
    @Autowired
132
    @Qualifier("sourcedEntityDao")
133
    protected ISourcedEntityDao<SourcedEntityBase<?>> sourcedEntityDao;
134
    @Autowired
135
    private INomenclaturalStatusDao nomStatusDao;
136
    @Autowired
137
    private ITypeDesignationDao typeDesignationDao;
138
    @Autowired
139
    private IHomotypicalGroupDao homotypicalGroupDao;
140
    @Autowired
141
    private ICdmGenericDao genericDao;
142
    @Autowired
143
    private ILuceneIndexToolProvider luceneIndexToolProvider;
144
    @Autowired
145
    protected ICommonService commonService;
146

    
147
    @Autowired
148
    // @Qualifier("defaultBeanInitializer")
149
    protected IBeanInitializer defaultBeanInitializer;
150

    
151
    /**
152
     * Constructor
153
     */
154
    public NameServiceImpl(){}
155

    
156
//********************* METHODS ****************************************************************//
157

    
158
    @Override
159
    @Transactional(readOnly = false)
160
    public DeleteResult delete(UUID nameUUID){
161
        NameDeletionConfigurator config = new NameDeletionConfigurator();
162
        DeleteResult result = delete(nameUUID, config);
163
        return result;
164
    }
165

    
166
    @Override
167
    public DeleteResult delete(TaxonName name){
168
        return delete(name.getUuid());
169
    }
170

    
171
    @Override
172
    @Transactional(readOnly = false)
173
    public DeleteResult delete(TaxonName name, NameDeletionConfigurator config) {
174
        DeleteResult result = new DeleteResult();
175

    
176
        if (name == null){
177
            result.setAbort();
178
            return result;
179
        }
180

    
181
        try{
182
            result = this.isDeletable(name, config);
183
        }catch(Exception e){
184
            result.addException(e);
185
            result.setError();
186
            return result;
187
        }
188
        if (result.isOk()){
189
        //remove references to this name
190
            removeNameRelationshipsByDeleteConfig(name, config);
191

    
192
           //remove name from homotypical group
193
            HomotypicalGroup homotypicalGroup = name.getHomotypicalGroup();
194
            if (homotypicalGroup != null){
195
                homotypicalGroup.removeTypifiedName(name, false);
196
            }
197

    
198
             //all type designation relationships are removed as they belong to the name
199
            deleteTypeDesignation(name, null);
200
    //      //type designations
201
    //      if (! name.getTypeDesignations().isEmpty()){
202
    //          String message = "Name can't be deleted as it has types. Remove types prior to deletion.";
203
    //          throw new ReferrencedObjectUndeletableException(message);
204
    //      }
205

    
206
            try{
207
                dao.delete(name);
208
                result.addDeletedObject(name);
209
            }catch(Exception e){
210
                result.addException(e);
211
                result.setError();
212
            }
213
            return result;
214
        }
215

    
216
        return result;
217
    }
218

    
219
    @Override
220
    @Transactional(readOnly = false)
221
    public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {
222

    
223
        TaxonName name = dao.load(nameUUID);
224
        return delete(name, config);
225
    }
226

    
227
    @Override
228
    @Transactional(readOnly = false)
229
    public UpdateResult cloneTypeDesignation(UUID nameUuid, SpecimenTypeDesignation baseDesignation,
230
            String accessionNumber, String barcode, String catalogNumber,
231
            UUID collectionUuid, SpecimenTypeDesignationStatus typeStatus){
232
        UpdateResult result = new UpdateResult();
233

    
234
        DerivedUnit baseSpecimen = HibernateProxyHelper.deproxy(occurrenceService.load(baseDesignation.getTypeSpecimen().getUuid(), Arrays.asList("collection")), DerivedUnit.class);
235
        DerivedUnit duplicate = DerivedUnit.NewInstance(baseSpecimen.getRecordBasis());
236
        DerivationEvent derivedFrom = baseSpecimen.getDerivedFrom();
237
        Collection<FieldUnit> fieldUnits = occurrenceService.findFieldUnits(baseSpecimen.getUuid(), null);
238
        if(fieldUnits.size()!=1){
239
            result.addException(new Exception("More than one or no field unit found for specimen"));
240
            result.setError();
241
            return result;
242
        }
243
        for (SpecimenOrObservationBase original : derivedFrom.getOriginals()) {
244
            DerivationEvent.NewSimpleInstance(original, duplicate, derivedFrom.getType());
245
        }
246
        duplicate.setAccessionNumber(accessionNumber);
247
        duplicate.setBarcode(barcode);
248
        duplicate.setCatalogNumber(catalogNumber);
249
        duplicate.setCollection(collectionService.load(collectionUuid));
250
        SpecimenTypeDesignation typeDesignation = SpecimenTypeDesignation.NewInstance();
251
        typeDesignation.setTypeSpecimen(duplicate);
252
        typeDesignation.setTypeStatus(typeStatus);
253

    
254
        TaxonName name = load(nameUuid);
255
        name.getTypeDesignations().add(typeDesignation);
256

    
257
        result.setCdmEntity(typeDesignation);
258
        result.addUpdatedObject(name);
259
        return result;
260
    }
261

    
262
    @Override
263
    @Transactional
264
    public DeleteResult deleteTypeDesignation(TaxonName name, TypeDesignationBase<?> typeDesignation){
265
    	if(typeDesignation != null && typeDesignation .isPersited()){
266
    		typeDesignation = HibernateProxyHelper.deproxy(sourcedEntityDao.load(typeDesignation.getUuid()), TypeDesignationBase.class);
267
    	}
268

    
269
        DeleteResult result = new DeleteResult();
270
        if (name == null && typeDesignation == null){
271
            result.setError();
272
            return result;
273
        }else if (name != null && typeDesignation != null){
274
            removeSingleDesignation(name, typeDesignation);
275
        }else if (name != null){
276
            @SuppressWarnings("rawtypes")
277
            Set<TypeDesignationBase<?>> designationSet = new HashSet(name.getTypeDesignations());
278
            for (TypeDesignationBase<?> desig : designationSet){
279
                desig = CdmBase.deproxy(desig);
280
                removeSingleDesignation(name, desig);
281
            }
282
        }else if (typeDesignation != null){
283
            @SuppressWarnings("unchecked")
284
            Set<TaxonName> nameSet = new HashSet(typeDesignation.getTypifiedNames());
285
            for (TaxonName singleName : nameSet){
286
                singleName = CdmBase.deproxy(singleName);
287
                removeSingleDesignation(singleName, typeDesignation);
288
            }
289
        }
290
        result.addDeletedObject(typeDesignation);
291
        result.addUpdatedObject(name);
292
        return result;
293
    }
294

    
295

    
296
    @Override
297
    @Transactional(readOnly = false)
298
    public DeleteResult deleteTypeDesignation(UUID nameUuid, UUID typeDesignationUuid){
299
        TaxonName nameBase = load(nameUuid);
300
        TypeDesignationBase<?> typeDesignation = HibernateProxyHelper.deproxy(sourcedEntityDao.load(typeDesignationUuid), TypeDesignationBase.class);
301
        return deleteTypeDesignation(nameBase, typeDesignation);
302
    }
303

    
304
    /**
305
     * @param name
306
     * @param typeDesignation
307
     */
308
    @Transactional
309
    private void removeSingleDesignation(TaxonName name, TypeDesignationBase<?> typeDesignation) {
310

    
311
        name.removeTypeDesignation(typeDesignation);
312
        if (typeDesignation.getTypifiedNames().isEmpty()){
313
            typeDesignation.removeType();
314
            if (!typeDesignation.getRegistrations().isEmpty()){
315
                for(Object reg: typeDesignation.getRegistrations()){
316
                    if (reg instanceof Registration){
317
                        ((Registration)reg).removeTypeDesignation(typeDesignation);
318
                    }
319
                }
320
            }
321

    
322
            typeDesignationDao.delete(typeDesignation);
323

    
324
        }
325
    }
326

    
327

    
328

    
329
    /**
330
     * @param name
331
     * @param config
332
     */
333
    private void removeNameRelationshipsByDeleteConfig(TaxonName name, NameDeletionConfigurator config) {
334
        try {
335
            if (config.isRemoveAllNameRelationships()){
336
                Set<NameRelationship> rels = getModifiableSet(name.getNameRelations());
337
                for (NameRelationship rel : rels){
338
                    name.removeNameRelationship(rel);
339
                }
340
            }else{
341
                //relations to this name
342
                Set<NameRelationship> rels = getModifiableSet(name.getRelationsToThisName());
343
                for (NameRelationship rel : rels){
344
                    if (config.isIgnoreHasBasionym() && NameRelationshipType.BASIONYM().equals(rel.getType() )){
345
                            name.removeNameRelationship(rel);
346
                    }else if (config.isIgnoreHasReplacedSynonym() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
347
                        name.removeNameRelationship(rel);
348
                    }
349
                }
350
                //relations from this name
351
                rels = getModifiableSet(name.getRelationsFromThisName());
352
                for (NameRelationship rel : rels){
353
                    if (config.isIgnoreIsBasionymFor() && NameRelationshipType.BASIONYM().equals(rel.getType())  ){
354
                        name.removeNameRelationship(rel);
355
                    }else if (config.isIgnoreIsReplacedSynonymFor() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
356
                        name.removeNameRelationship(rel);
357
                    }
358
                }
359

    
360
            }
361
        } catch (Exception e) {
362
            throw new RuntimeException(e);
363
        }
364
    }
365

    
366
    /**
367
     * @param name
368
     * @return
369
     */
370
    private Set<NameRelationship> getModifiableSet(Set<NameRelationship> relations) {
371
        Set<NameRelationship> rels = new HashSet<NameRelationship>();
372
        for (NameRelationship rel : relations){
373
            rels.add(rel);
374
        }
375
        return rels;
376
    }
377

    
378
//********************* METHODS ****************************************************************//
379

    
380

    
381
    /**
382
     * TODO candidate for harmonization
383
     * new name findByName
384
     */
385
    @Override
386
    @Deprecated
387
    public List<TaxonName> getNamesByNameCache(String nameCache){
388
        boolean includeAuthors = false;
389
        List result = dao.findByName(includeAuthors, nameCache, MatchMode.EXACT, null, null, null, null);
390
        return result;
391
    }
392

    
393
    /**
394
     * TODO candidate for harmonization
395
     * new name saveHomotypicalGroups
396
     *
397
     * findByTitle
398
     */
399
    @Override
400
    @Deprecated
401
    public List<TaxonName> findNamesByTitleCache(String titleCache, MatchMode matchMode, List<String> propertyPaths){
402
        List result = dao.findByTitle(titleCache, matchMode, null, null, null, propertyPaths);
403
        return result;
404
    }
405

    
406
    /**
407
     * TODO candidate for harmonization
408
     * new name saveHomotypicalGroups
409
     *
410
     * findByTitle
411
     */
412
    @Override
413
    @Deprecated
414
    public List<TaxonName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){
415
        List<TaxonName> result = dao.findByName(false, nameCache, matchMode, null, null, null , propertyPaths);
416
        return result;
417
    }
418

    
419
    @Override
420
    public Pager<TaxonNameParts> findTaxonNameParts(Optional<String> genusOrUninomial,
421
            Optional<String> infraGenericEpithet, Optional<String> specificEpithet,
422
            Optional<String> infraSpecificEpithet, Rank rank, Set<UUID> excludedNamesUuids,
423
            Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
424

    
425

    
426
        long count = dao.countTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraGenericEpithet, rank, excludedNamesUuids);
427

    
428
        List<TaxonNameParts> results;
429
        if(AbstractPagerImpl.hasResultsInRange(count, pageIndex, pageSize)){
430
            results = dao.findTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
431
                    rank, excludedNamesUuids,
432
                    pageSize, pageIndex, orderHints);
433
        } else {
434
            results = new ArrayList<>();
435
        }
436

    
437
        return new DefaultPagerImpl<TaxonNameParts>(pageIndex, count, pageSize, results);
438
    }
439

    
440
    /**
441
     * {@inheritDoc}
442
     */
443
    @Override
444
    public Pager<TaxonNameParts> findTaxonNameParts(TaxonNamePartsFilter filter, String namePartQueryString,
445
            Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
446

    
447
        return findTaxonNameParts(
448
                filter.uninomialQueryString(namePartQueryString),
449
                filter.infraGenericEpithet(namePartQueryString),
450
                filter.specificEpithet(namePartQueryString),
451
                filter.infraspecificEpithet(namePartQueryString),
452
                filter.getRank(),
453
                filter.getExludedNamesUuids(),
454
                pageSize, pageIndex, orderHints);
455
    }
456

    
457
    /**
458
     * TODO candidate for harmonization
459
     * new name saveHomotypicalGroups
460
     */
461
    @Override
462
    @Transactional(readOnly = false)
463
    public Map<UUID, HomotypicalGroup> saveAllHomotypicalGroups(Collection<HomotypicalGroup> homotypicalGroups){
464
        return homotypicalGroupDao.saveAll(homotypicalGroups);
465
    }
466

    
467
    /**
468
     * TODO candidate for harmonization
469
     * new name saveTypeDesignations
470
     */
471
    @Override
472
    @Transactional(readOnly = false)
473
    public Map<UUID, TypeDesignationBase<?>> saveTypeDesignationAll(Collection<TypeDesignationBase<?>> typeDesignationCollection){
474
        return typeDesignationDao.saveAll(typeDesignationCollection);
475
    }
476

    
477
    /**
478
     * TODO candidate for harmonization
479
     * new name getNomenclaturalStatus
480
     */
481
    @Override
482
    public List<NomenclaturalStatus> getAllNomenclaturalStatus(int limit, int start){
483
        return nomStatusDao.list(limit, start);
484
    }
485

    
486
    @Override
487
    public NomenclaturalStatus loadNomenclaturalStatus(UUID uuid,  List<String> propertyPaths){
488
        return nomStatusDao.load(uuid, propertyPaths);
489
    }
490

    
491
    /**
492
     * TODO candidate for harmonization
493
     * new name getTypeDesignations
494
     */
495
    @Override
496
    public List<TypeDesignationBase<?>> getAllTypeDesignations(int limit, int start){
497
        return typeDesignationDao.getAllTypeDesignations(limit, start);
498
    }
499

    
500
    @Override
501
    public TypeDesignationBase<?> loadTypeDesignation(int id, List<String> propertyPaths){
502
        return typeDesignationDao.load(id, propertyPaths);
503
    }
504

    
505
    @Override
506
    public TypeDesignationBase<?> loadTypeDesignation(UUID uuid, List<String> propertyPaths){
507
        return typeDesignationDao.load(uuid, propertyPaths);
508
    }
509

    
510
    @Override
511
    public List<TypeDesignationBase<?>> loadTypeDesignations(List<UUID> uuids, List<String> propertyPaths){
512
    	if(uuids == null) {
513
            return null;
514
        }
515

    
516
        List<TypeDesignationBase<?>> entities = new ArrayList<>();
517
        for(UUID uuid : uuids) {
518
            entities.add(uuid == null ? null : typeDesignationDao.load(uuid, propertyPaths));
519
        }
520
        return entities;
521
    }
522

    
523
    /**
524
     * FIXME Candidate for harmonization
525
     * homotypicalGroupService.list
526
     */
527
    @Override
528
    public List<HomotypicalGroup> getAllHomotypicalGroups(int limit, int start){
529
        return homotypicalGroupDao.list(limit, start);
530
    }
531

    
532
    @Override
533
    public List<NameRelationship> listNameRelationships(Set<NameRelationshipType> types,
534
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
535

    
536
        Long numberOfResults = dao.countNameRelationships(types);
537
        List<NameRelationship> results = new ArrayList<>();
538
        if(numberOfResults > 0) {
539
            results = dao.getNameRelationships(types, pageSize, pageNumber, orderHints, propertyPaths);
540
        }
541
        return results;
542
    }
543

    
544
    @Override
545
    public List<HybridRelationship> listHybridRelationships(Set<HybridRelationshipType> types,
546
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
547

    
548
        Long numberOfResults = dao.countHybridRelationships(types);
549
        List<HybridRelationship> results = new ArrayList<>();
550
        if(numberOfResults > 0) {
551
            results = dao.getHybridRelationships(types, pageSize, pageNumber, orderHints, propertyPaths);
552
        }
553
        return results;
554
    }
555

    
556

    
557
    @Override
558
    @Autowired
559
    protected void setDao(ITaxonNameDao dao) {
560
        this.dao = dao;
561
    }
562

    
563
    @Override
564
    public Pager<HybridRelationship> getHybridNames(INonViralName name,	HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
565
        Integer numberOfResults = dao.countHybridNames(name, type);
566

    
567
        List<HybridRelationship> results = new ArrayList<HybridRelationship>();
568
        if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
569
            results = dao.getHybridNames(name, type, pageSize, pageNumber,orderHints,propertyPaths);
570
        }
571

    
572
        return new DefaultPagerImpl<HybridRelationship>(pageNumber, numberOfResults, pageSize, results);
573
    }
574

    
575
    @Override
576
    public List<NameRelationship> listNameRelationships(TaxonName name,	Direction direction, NameRelationshipType type, Integer pageSize,
577
            Integer pageNumber, List<OrderHint> orderHints,	List<String> propertyPaths) {
578

    
579
        Integer numberOfResults = dao.countNameRelationships(name, direction, type);
580

    
581
        List<NameRelationship> results = new ArrayList<NameRelationship>();
582
        if (AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
583
            results = dao.getNameRelationships(name, direction, type, pageSize,	pageNumber, orderHints, propertyPaths);
584
        }
585
        return results;
586
    }
587

    
588

    
589
    protected LuceneSearch prepareFindByFuzzyNameSearch(Class<? extends CdmBase> clazz,
590
            INonViralName nvn,
591
            float accuracy,
592
            int maxNoOfResults,
593
            List<Language> languages,
594
            boolean highlightFragments) {
595
        String similarity = Float.toString(accuracy);
596
        String searchSuffix = "~" + similarity;
597

    
598

    
599
        Builder finalQueryBuilder = new Builder();
600
        finalQueryBuilder.setDisableCoord(false);
601
        Builder textQueryBuilder = new Builder();
602
        textQueryBuilder.setDisableCoord(false);
603

    
604
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
605
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
606

    
607
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
608
//    	luceneSearch.setSortFields(sortFields);
609

    
610
        // ---- search criteria
611
        luceneSearch.setCdmTypRestriction(clazz);
612

    
613
        FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
614
        if(nvn.getGenusOrUninomial() != null && !nvn.getGenusOrUninomial().equals("")) {
615
            fltq.addTerms(nvn.getGenusOrUninomial().toLowerCase(), "genusOrUninomial", accuracy, 3);
616
        } else {
617
            //textQuery.add(new RegexQuery (new Term ("genusOrUninomial", "^[a-zA-Z]*")), Occur.MUST_NOT);
618
            textQueryBuilder.add(queryFactory.newTermQuery("genusOrUninomial", "_null_", false), Occur.MUST);
619
        }
620

    
621
        if(nvn.getInfraGenericEpithet() != null && !nvn.getInfraGenericEpithet().equals("")){
622
            fltq.addTerms(nvn.getInfraGenericEpithet().toLowerCase(), "infraGenericEpithet", accuracy, 3);
623
        } else {
624
            //textQuery.add(new RegexQuery (new Term ("infraGenericEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
625
            textQueryBuilder.add(queryFactory.newTermQuery("infraGenericEpithet", "_null_", false), Occur.MUST);
626
        }
627

    
628
        if(nvn.getSpecificEpithet() != null && !nvn.getSpecificEpithet().equals("")){
629
            fltq.addTerms(nvn.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy, 3);
630
        } else {
631
            //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
632
            textQueryBuilder.add(queryFactory.newTermQuery("specificEpithet", "_null_", false), Occur.MUST);
633
        }
634

    
635
        if(nvn.getInfraSpecificEpithet() != null && !nvn.getInfraSpecificEpithet().equals("")){
636
            fltq.addTerms(nvn.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy, 3);
637
        } else {
638
            //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
639
            textQueryBuilder.add(queryFactory.newTermQuery("infraSpecificEpithet", "_null_", false), Occur.MUST);
640
        }
641

    
642
        if(nvn.getAuthorshipCache() != null && !nvn.getAuthorshipCache().equals("")){
643
            fltq.addTerms(nvn.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy, 3);
644
        } else {
645
            //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
646
        }
647

    
648
        textQueryBuilder.add(fltq, Occur.MUST);
649

    
650
        BooleanQuery textQuery = textQueryBuilder.build();
651
        finalQueryBuilder.add(textQuery, Occur.MUST);
652

    
653
        luceneSearch.setQuery(finalQueryBuilder.build());
654

    
655
        if(highlightFragments){
656
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
657
        }
658
        return luceneSearch;
659
    }
660

    
661
    protected LuceneSearch prepareFindByFuzzyNameCacheSearch(Class<? extends CdmBase> clazz,
662
            String name,
663
            float accuracy,
664
            int maxNoOfResults,
665
            List<Language> languages,
666
            boolean highlightFragments) {
667

    
668
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
669
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
670

    
671
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
672
//    	luceneSearch.setSortFields(sortFields);
673

    
674
        // ---- search criteria
675
        luceneSearch.setCdmTypRestriction(clazz);
676
        FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
677

    
678
        fltq.addTerms(name, "nameCache", accuracy, 3);
679

    
680
         BooleanQuery finalQuery = new BooleanQuery(false);
681

    
682
         finalQuery.add(fltq, Occur.MUST);
683

    
684
        luceneSearch.setQuery(finalQuery);
685

    
686
        if(highlightFragments){
687
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
688
        }
689
        return luceneSearch;
690
    }
691

    
692
    protected LuceneSearch prepareFindByExactNameSearch(Class<? extends CdmBase> clazz,
693
            String name,
694
            boolean wildcard,
695
            List<Language> languages,
696
            boolean highlightFragments) {
697
        Builder textQueryBuilder = new Builder();
698

    
699
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
700
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
701

    
702
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
703
//    	luceneSearch.setSortFields(sortFields);
704

    
705
        // ---- search criteria
706
        luceneSearch.setCdmTypRestriction(clazz);
707

    
708
        if(name != null && !name.equals("")) {
709
            if(wildcard) {
710
                textQueryBuilder.add(new WildcardQuery(new Term("nameCache", name + "*")), Occur.MUST);
711
            } else {
712
                textQueryBuilder.add(queryFactory.newTermQuery("nameCache", name, false), Occur.MUST);
713
            }
714
        }
715

    
716
        luceneSearch.setQuery(textQueryBuilder.build());
717

    
718
        if(highlightFragments){
719
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
720
        }
721
        return luceneSearch;
722
    }
723

    
724
    @Override
725
    public List<SearchResult<TaxonName>> findByNameFuzzySearch(
726
            String name,
727
            float accuracy,
728
            List<Language> languages,
729
            boolean highlightFragments,
730
            List<String> propertyPaths,
731
            int maxNoOfResults) throws IOException, LuceneParseException {
732

    
733
        logger.info("Name to fuzzy search for : " + name);
734
        // parse the input name
735
        NonViralNameParserImpl parser = new NonViralNameParserImpl();
736
        INonViralName nvn = parser.parseFullName(name);
737
        if(name != null && !name.equals("") && nvn == null) {
738
            throw new LuceneParseException("Could not parse name " + name);
739
        }
740
        LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
741

    
742
        // --- execute search
743
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
744

    
745

    
746
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
747
        idFieldMap.put(CdmBaseType.NONVIRALNAME, "id");
748

    
749
        // --- initialize taxa, highlight matches ....
750
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
751

    
752
        @SuppressWarnings("rawtypes")
753
        List<SearchResult<TaxonName>> searchResults = searchResultBuilder.createResultSet(
754
                topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
755

    
756
        return searchResults;
757

    
758
    }
759

    
760
    @Override
761
    public List<DocumentSearchResult> findByNameFuzzySearch(
762
            String name,
763
            float accuracy,
764
            List<Language> languages,
765
            boolean highlightFragments,
766
            int maxNoOfResults) throws IOException, LuceneParseException {
767

    
768
        logger.info("Name to fuzzy search for : " + name);
769
        // parse the input name
770
        NonViralNameParserImpl parser = new NonViralNameParserImpl();
771
        INonViralName nvn = parser.parseFullName(name);
772
        if(name != null && !name.equals("") && nvn == null) {
773
            throw new LuceneParseException("Could not parse name " + name);
774
        }
775
        LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
776

    
777
        // --- execute search
778
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
779

    
780
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
781

    
782
        // --- initialize taxa, highlight matches ....
783
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
784

    
785
        @SuppressWarnings("rawtypes")
786
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
787

    
788
        return searchResults;
789
    }
790

    
791
    @Override
792
    public List<DocumentSearchResult> findByFuzzyNameCacheSearch(
793
            String name,
794
            float accuracy,
795
            List<Language> languages,
796
            boolean highlightFragments,
797
            int maxNoOfResults) throws IOException, LuceneParseException {
798

    
799
        logger.info("Name to fuzzy search for : " + name);
800

    
801
        LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);
802

    
803
        // --- execute search
804
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
805
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
806

    
807
        // --- initialize taxa, highlight matches ....
808
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
809

    
810
        @SuppressWarnings("rawtypes")
811
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
812

    
813
        return searchResults;
814
    }
815

    
816
    @Override
817
    public List<DocumentSearchResult> findByNameExactSearch(
818
            String name,
819
            boolean wildcard,
820
            List<Language> languages,
821
            boolean highlightFragments,
822
            int maxNoOfResults) throws IOException, LuceneParseException {
823

    
824
        logger.info("Name to exact search for : " + name);
825

    
826
        LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);
827

    
828
        // --- execute search
829

    
830

    
831
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
832

    
833
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
834

    
835
        // --- initialize taxa, highlight matches ....
836
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
837

    
838
        @SuppressWarnings("rawtypes")
839
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
840

    
841
        return searchResults;
842
    }
843

    
844
    @Override
845
    public Pager<NameRelationship> pageNameRelationships(TaxonName name, Direction direction, NameRelationshipType type, Integer pageSize,
846
            Integer pageNumber, List<OrderHint> orderHints,	List<String> propertyPaths) {
847
        List<NameRelationship> results = listNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
848
        return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
849
    }
850

    
851
    @Override
852
    public List<NameRelationship> listFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
853
        return listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
854
    }
855

    
856
    @Override
857
    public Pager<NameRelationship> pageFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
858
        List<NameRelationship> results = listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
859
        return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
860
    }
861

    
862
    @Override
863
    public List<NameRelationship> listToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
864
        return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
865
    }
866

    
867
    @Override
868
    public Pager<NameRelationship> pageToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
869
        List<NameRelationship> results = listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
870
        return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
871
    }
872

    
873
    @Override
874
    public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
875
            Integer pageSize, Integer pageNumber) {
876
        return getTypeDesignations(name, status, pageSize, pageNumber, null);
877
    }
878

    
879
    @Override
880
    public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
881
                Integer pageSize, Integer pageNumber, List<String> propertyPaths){
882
        long numberOfResults = dao.countTypeDesignations(name, status);
883

    
884
        List<TypeDesignationBase> results = new ArrayList<>();
885
        if(AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)) {
886
            results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);
887
        }
888
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
889
    }
890

    
891
    @Override
892
    public List<TypeDesignationBase> getTypeDesignationsInHomotypicalGroup(UUID nameUuid, Integer pageSize,
893
            Integer pageNumber, List<String> propertyPaths){
894
        TaxonName name = load(nameUuid, Arrays.asList("nomenclaturalSource.citation.authorship"));
895
        Set<TypeDesignationBase<?>> typeDesignations = name.getHomotypicalGroup().getTypeDesignations();
896
        List<TypeDesignationBase> result = defaultBeanInitializer.initializeAll(new ArrayList(typeDesignations), propertyPaths);
897
        return result;
898
    }
899

    
900
    /**
901
     * FIXME Candidate for harmonization
902
     * rename search
903
     */
904
    @Override
905
    public Pager<TaxonName> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,	Integer pageNumber, List<OrderHint> orderHints,
906
            List<String> propertyPaths) {
907
        long numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
908

    
909
        List<TaxonName> results = new ArrayList<>();
910
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
911
            results = dao.searchNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber, orderHints, propertyPaths);
912
        }
913

    
914
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
915
    }
916

    
917
    @Override
918
    public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames(Integer limit, String pattern) {
919
        return dao.getUuidAndTitleCacheOfNames(limit, pattern);
920
    }
921

    
922
    @Override
923
    public Pager<TaxonName> findByName(Class<TaxonName> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria,
924
            Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
925
         Long numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
926

    
927
         List<TaxonName> results = new ArrayList<>();
928
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
929
                results = dao.findByName(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
930
         }
931

    
932
         return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
933
    }
934

    
935
    @Override
936
    public List<TaxonName> findByFullTitle(Class<TaxonName> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria,
937
            Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
938
         Long numberOfResults = dao.countByFullTitle(clazz, queryString, matchmode, criteria);
939

    
940
         List<TaxonName> results = new ArrayList<>();
941
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
942
                results = dao.findByFullTitle(queryString, matchmode, pageSize, pageNumber, criteria, propertyPaths);
943
         }
944

    
945
         return results;
946
    }
947

    
948

    
949
    @Override
950
    public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
951
        return homotypicalGroupDao.findByUuid(uuid);
952
    }
953

    
954
    @Override
955
    @Transactional(readOnly = false)
956
    public UpdateResult updateCaches(Class<? extends TaxonName> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonName> cacheStrategy, IProgressMonitor monitor) {
957
        if (clazz == null){
958
            clazz = TaxonName.class;
959
        }
960
        return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
961
    }
962

    
963

    
964
    @Override
965
    public List<TaggedText> getTaggedName(UUID uuid) {
966
        TaxonName taxonName = dao.load(uuid);
967
        List<TaggedText> taggedName = taxonName.getTaggedName();
968
        return taggedName;
969
    }
970

    
971

    
972
    public DeleteResult isDeletable(TaxonName name, DeleteConfiguratorBase config){
973
        DeleteResult result = new DeleteResult();
974

    
975
        NameDeletionConfigurator nameConfig = null;
976
        if (config instanceof NameDeletionConfigurator){
977
            nameConfig = (NameDeletionConfigurator) config;
978
        }else{
979
             result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
980
             result.setError();
981
             return result;
982
        }
983

    
984
        if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
985
            HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
986

    
987
            if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
988
                result.addException(new Exception( "Name can't be deleted as it is a basionym."));
989
                result.setAbort();
990
            }
991
            if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){
992
                result.addException(new Exception( "Name can't be deleted as it has a basionym."));
993
                result.setAbort();
994
            }
995
            Set<NameRelationship> relationships = name.getNameRelations();
996
            for (NameRelationship rel: relationships){
997
                if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
998
                    result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
999
                    result.setAbort();
1000
                    break;
1001
                }
1002
            }
1003
        }
1004

    
1005
        //concepts
1006
        if (! name.getTaxonBases().isEmpty()){
1007
            result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
1008
            result.setAbort();
1009
        }
1010

    
1011
        //hybrid relationships
1012
        if (name.isNonViral()){
1013
            INonViralName nvn = name;
1014
            Set<HybridRelationship> parentHybridRelations = nvn.getHybridParentRelations();
1015
            //Hibernate.initialize(parentHybridRelations);
1016
            if (! parentHybridRelations.isEmpty()){
1017
                result.addException(new Exception("Name can't be deleted as it is a parent in (a) hybrid relationship(s). Remove hybrid relationships prior to deletion."));
1018
                result.setAbort();
1019
            }
1020
        }
1021
        Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);
1022
        for (CdmBase referencingObject : referencingObjects){
1023
            //DerivedUnit?.storedUnder
1024
            if (referencingObject.isInstanceOf(DerivedUnit.class)){
1025
                String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
1026
                message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());
1027
                result.addException(new ReferencedObjectUndeletableException(message));
1028
                result.addRelatedObject(referencingObject);
1029
                result.setAbort();
1030
            }
1031
            //DescriptionElementSource#nameUsedInSource
1032
            else if (referencingObject.isInstanceOf(DescriptionElementSource.class)){
1033
                String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
1034
                result.addException(new ReferencedObjectUndeletableException(message));
1035
                result.addRelatedObject(referencingObject);
1036
                result.setAbort();
1037
            }
1038
            //NameTypeDesignation#typeName
1039
            else if (referencingObject.isInstanceOf(NameTypeDesignation.class)){
1040
                NameTypeDesignation typeDesignation = HibernateProxyHelper.deproxy(referencingObject, NameTypeDesignation.class);
1041

    
1042
                if (typeDesignation.getTypeName().equals(name) && !typeDesignation.getTypifiedNames().isEmpty()){
1043
                    String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
1044
                    result.addException(new ReferencedObjectUndeletableException(message));
1045
                    result.addRelatedObject(referencingObject);
1046
                    result.setAbort();
1047
                }
1048
            }
1049
            //DeterminationEvent#taxonName
1050
            else if (referencingObject.isInstanceOf(DeterminationEvent.class)){
1051
                String message = "Name can't be deleted as it is used as a determination event";
1052
                result.addException(new ReferencedObjectUndeletableException(message));
1053
                result.addRelatedObject(referencingObject);
1054
                result.setAbort();
1055
        }
1056
        }
1057

    
1058
        //TODO inline references
1059

    
1060

    
1061
        if (!nameConfig.isIgnoreIsReplacedSynonymFor() && name.isReplacedSynonym()){
1062
            String message = "Name can't be deleted as it is a replaced synonym.";
1063
            result.addException(new Exception(message));
1064
            result.setAbort();
1065
        }
1066
        if (!nameConfig.isIgnoreHasReplacedSynonym() && (name.getReplacedSynonyms().size()>0)){
1067
            String message = "Name can't be deleted as it has a replaced synonym.";
1068
            result.addException(new Exception(message));
1069
            result.setAbort();
1070
        }
1071
        return result;
1072

    
1073
    }
1074

    
1075

    
1076
    @Override
1077
    public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config){
1078
        TaxonName name = this.load(nameUUID);
1079
        return isDeletable(name, config);
1080
    }
1081

    
1082
    @Override
1083
    @Transactional(readOnly = true)
1084
    public UpdateResult setAsGroupsBasionym(UUID nameUuid) {
1085
        TaxonName name = dao.load(nameUuid);
1086
        UpdateResult result = new UpdateResult();
1087
        name.setAsGroupsBasionym();
1088
        result.addUpdatedObject(name);
1089
        return result;
1090

    
1091
    }
1092

    
1093
    @Override
1094
    public List<HashMap<String,String>> getNameRecords(){
1095
		return dao.getNameRecords();
1096

    
1097
    }
1098

    
1099
    @Override
1100
    public List<TypeDesignationStatusBase> getTypeDesignationStatusInUse(){
1101
        return typeDesignationDao.getTypeDesignationStatusInUse();
1102
    }
1103

    
1104
    @Override
1105
    public Collection<TypeDesignationStatusFilter> getTypeDesignationStatusFilterTerms(List<Language> preferredLanguages){
1106
        List<TypeDesignationStatusBase> termList = typeDesignationDao.getTypeDesignationStatusInUse();
1107
        Map<String, TypeDesignationStatusFilter>  filterMap = new HashMap<>();
1108
        for(TypeDesignationStatusBase term : termList){
1109
            TypeDesignationStatusFilter filter = new TypeDesignationStatusFilter(term, preferredLanguages, true);
1110
            String key = filter.getKey();
1111
            if(filterMap.containsKey(key)){
1112
                filterMap.get(key).addStatus(term);
1113
            } else {
1114
                filterMap.put(key, filter);
1115
            }
1116
        }
1117
        return filterMap.values();
1118
    }
1119

    
1120
    @Override
1121
    public <S extends TaxonName> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
1122
            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
1123
        return page(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, INCLUDE_UNPUBLISHED);
1124
    }
1125

    
1126
    @Override
1127
    public <S extends TaxonName> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
1128
            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths, boolean includeUnpublished) {
1129

    
1130
        List<S> records;
1131
        long resultSize = dao.count(clazz, restrictions);
1132
        if(AbstractPagerImpl.hasResultsInRange(resultSize, pageIndex, pageSize)){
1133
            records = dao.list(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, includeUnpublished);
1134
        } else {
1135
            records = new ArrayList<>();
1136
        }
1137
        Pager<S> pager = new DefaultPagerImpl<>(pageIndex, resultSize, pageSize, records);
1138
        return pager;
1139
    }
1140

    
1141
    @Override
1142
    public List<UuidAndTitleCache> getUuidAndTitleCacheOfSynonymy(Integer limit, UUID taxonUuid) {
1143
        List<String> propertyPaths = new ArrayList<>();
1144
        propertyPaths.add("synonyms.name.*");
1145
        TaxonBase<?> taxonBase = taxonService.load(taxonUuid, propertyPaths);
1146
        if (taxonBase instanceof Taxon){
1147
            Taxon taxon = (Taxon)taxonBase;
1148
            Set<TaxonName> names = taxon.getSynonymNames();
1149
            List<UuidAndTitleCache> uuidAndTitleCacheList = new ArrayList<>();
1150
            UuidAndTitleCache<TaxonName> uuidAndTitleCache;
1151
            for (TaxonName name: names){
1152
                uuidAndTitleCache = new UuidAndTitleCache<TaxonName>(TaxonName.class, name.getUuid(), name.getId(), name.getTitleCache());
1153
                uuidAndTitleCacheList.add(uuidAndTitleCache);
1154
            }
1155
        }
1156
        return null;
1157
    }
1158

    
1159
    @Override
1160
    public UpdateResult parseName(String stringToBeParsed, NomenclaturalCode code, Rank preferredRank, boolean doDeduplicate) {
1161
        TaxonName name = TaxonNameFactory.NewNameInstance(code, preferredRank);
1162
        return parseName(name, stringToBeParsed, preferredRank, true, doDeduplicate);
1163
    }
1164

    
1165
    @Override
1166
    public UpdateResult parseName(TaxonName nameToBeFilled, String stringToBeParsed, Rank preferredRank,
1167
            boolean doEmpty, boolean doDeduplicate){
1168
        UpdateResult result = new UpdateResult();
1169
        NonViralNameParserImpl nonViralNameParser = NonViralNameParserImpl.NewInstance();
1170
        nonViralNameParser.parseReferencedName(nameToBeFilled, stringToBeParsed, preferredRank, doEmpty);
1171
        TaxonName name = nameToBeFilled;
1172
        if(doDeduplicate) {
1173
            try {
1174
//                Level sqlLogLevel = Logger.getLogger("org.hibernate.SQL").getLevel();
1175
//                Logger.getLogger("org.hibernate.SQL").setLevel(Level.TRACE);
1176

    
1177
                //references
1178
                if (name.getNomenclaturalReference()!= null && !name.getNomenclaturalReference().isPersited()){
1179
                    Reference nomRef = name.getNomenclaturalReference();
1180
                    IMatchStrategy referenceMatcher = MatchStrategyFactory.NewParsedReferenceInstance(nomRef);
1181
                    List<Reference> matchingReferences = commonService.findMatching(nomRef, referenceMatcher);
1182
                    if(matchingReferences.size() >= 1){
1183
                        Reference duplicate = findBestMatching(nomRef, matchingReferences, referenceMatcher);
1184
                        name.setNomenclaturalReference(duplicate);
1185
                    }else{
1186
                        if (nomRef.getInReference() != null){
1187
                            List<Reference> matchingInReferences = commonService.findMatching(nomRef.getInReference(), MatchStrategyFactory.NewParsedReferenceInstance(nomRef.getInReference()));
1188
                            if(matchingInReferences.size() >= 1){
1189
                                Reference duplicate = findBestMatching(nomRef, matchingInReferences, referenceMatcher);
1190
                                nomRef.setInReference(duplicate);
1191
                            }
1192
                        }
1193
                        TeamOrPersonBase<?> author = deduplicateAuthor(nomRef.getAuthorship());
1194
                        nomRef.setAuthorship(author);
1195
                    }
1196
                }
1197
                Reference nomRef = name.getNomenclaturalReference();
1198
                //authors
1199
                IParsedMatchStrategy authorMatcher = MatchStrategyFactory.NewParsedTeamOrPersonInstance();
1200
                if (name.getCombinationAuthorship()!= null && !name.getCombinationAuthorship().isPersited()){
1201
                    //use same nom.ref. author if possible (should always be possible if nom.ref. exists)
1202
                    if (nomRef != null && nomRef.getAuthorship() != null){
1203
                        if(authorMatcher.invoke(name.getCombinationAuthorship(), nomRef.getAuthorship()).isSuccessful()){
1204
                            name.setCombinationAuthorship(nomRef.getAuthorship());
1205
                        }
1206
                    }
1207
                    name.setCombinationAuthorship(deduplicateAuthor(name.getCombinationAuthorship()));
1208
                }
1209
                if (name.getExCombinationAuthorship()!= null && !name.getExCombinationAuthorship().isPersited()){
1210
                    name.setExCombinationAuthorship(deduplicateAuthor(name.getExCombinationAuthorship()));
1211
                }
1212
                if (name.getBasionymAuthorship()!= null && !name.getBasionymAuthorship().isPersited()){
1213
                    name.setBasionymAuthorship(deduplicateAuthor(name.getBasionymAuthorship()));
1214
                }
1215
                if (name.getExBasionymAuthorship()!= null && !name.getExBasionymAuthorship().isPersited()){
1216
                    name.setExBasionymAuthorship(deduplicateAuthor(name.getExBasionymAuthorship()));
1217
                }
1218
//              Logger.getLogger("org.hibernate.SQL").setLevel(sqlLogLevel);
1219
            } catch (MatchException e) {
1220
                throw new RuntimeException(e);
1221
            }
1222
        }
1223
        result.setCdmEntity(name);
1224
        return result;
1225
    }
1226

    
1227
    private TeamOrPersonBase<?> deduplicateAuthor(TeamOrPersonBase<?> authorship) throws MatchException {
1228
        if (authorship == null){
1229
            return null;
1230
        }
1231
        IParsedMatchStrategy authorMatcher = MatchStrategyFactory.NewParsedTeamOrPersonInstance();
1232
        List<TeamOrPersonBase<?>> matchingAuthors = commonService.findMatching(authorship, authorMatcher);
1233
        if(matchingAuthors.size() >= 1){
1234
            TeamOrPersonBase<?> duplicate = findBestMatching(authorship, matchingAuthors, authorMatcher);
1235
            return duplicate;
1236
        }else{
1237
            if (authorship instanceof Team){
1238
                deduplicateTeam((Team)authorship);
1239
            }
1240
            return authorship;
1241
        }
1242
    }
1243

    
1244
    private void deduplicateTeam(Team team) throws MatchException {
1245
        List<Person> members = team.getTeamMembers();
1246
        IParsedMatchStrategy personMatcher = MatchStrategyFactory.NewParsedPersonInstance();
1247
        for (int i =0; i< members.size(); i++){
1248
            Person person = CdmBase.deproxy(members.get(i));
1249
            List<Person> matchingPersons = commonService.findMatching(person, personMatcher);
1250
            if (matchingPersons.size() > 0){
1251
                person = findBestMatching(person, matchingPersons, personMatcher);
1252
                members.set(i, person);
1253
            }
1254
        }
1255
    }
1256

    
1257
    private <M extends IMatchable> M findBestMatching(M matchable, List<M> matchingList,
1258
            IMatchStrategy referenceMatcher) {
1259
        // FIXME TODO resolve multiple duplications. Use first match for a start
1260
        if(matchingList.isEmpty()){
1261
            return null;
1262
        }
1263
        M bestMatching = matchingList.iterator().next();
1264
        return bestMatching;
1265
    }
1266

    
1267
}
(74-74/100)