Project

General

Profile

Download (55.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.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.NomenclaturalSource;
74
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
75
import eu.etaxonomy.cdm.model.name.Rank;
76
import eu.etaxonomy.cdm.model.name.Registration;
77
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
78
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
79
import eu.etaxonomy.cdm.model.name.TaxonName;
80
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
81
import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
82
import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
83
import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
84
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
85
import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
86
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
87
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
88
import eu.etaxonomy.cdm.model.reference.Reference;
89
import eu.etaxonomy.cdm.model.taxon.Taxon;
90
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
91
import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
92
import eu.etaxonomy.cdm.persistence.dao.common.ISourcedEntityDao;
93
import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
94
import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
95
import eu.etaxonomy.cdm.persistence.dao.name.IHomotypicalGroupDao;
96
import eu.etaxonomy.cdm.persistence.dao.name.INomenclaturalStatusDao;
97
import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
98
import eu.etaxonomy.cdm.persistence.dao.name.ITypeDesignationDao;
99
import eu.etaxonomy.cdm.persistence.dao.reference.IOriginalSourceDao;
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

    
120
    static private final Logger logger = Logger.getLogger(NameServiceImpl.class);
121

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

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

    
150
//***************************** CONSTRUCTOR **********************************/
151

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

    
157
//********************* METHODS ***********************************************//
158

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

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

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

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

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

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

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

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

    
217
        return result;
218
    }
219

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

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

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

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

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

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

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

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

    
296

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

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

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

    
323
            typeDesignationDao.delete(typeDesignation);
324

    
325
        }
326
    }
327

    
328

    
329

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

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

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

    
379
//********************* METHODS ****************************************************************//
380

    
381

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

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

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

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

    
426

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
533

    
534
    @Override
535
    public List<NomenclaturalSource> listOriginalSpellings(Integer pageSize, Integer pageNumber,
536
            List<OrderHint> orderHints, List<String> propertyPaths) {
537

    
538
        Long numberOfResults = sourcedDao.countWithNameUsedInSource(NomenclaturalSource.class);
539
        List<NomenclaturalSource> results = new ArrayList<>();
540
        if(numberOfResults > 0) {
541
            results = sourcedDao.listWithNameUsedInSource(NomenclaturalSource.class, pageSize, pageNumber, orderHints, propertyPaths);
542
        }
543
        return results;
544
    }
545

    
546
    @Override
547
    public List<NameRelationship> listNameRelationships(Set<NameRelationshipType> types,
548
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
549

    
550
        Long numberOfResults = dao.countNameRelationships(types);
551
        List<NameRelationship> results = new ArrayList<>();
552
        if(numberOfResults > 0) {
553
            results = dao.getNameRelationships(types, pageSize, pageNumber, orderHints, propertyPaths);
554
        }
555
        return results;
556
    }
557

    
558
    @Override
559
    public List<HybridRelationship> listHybridRelationships(Set<HybridRelationshipType> types,
560
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
561

    
562
        Long numberOfResults = dao.countHybridRelationships(types);
563
        List<HybridRelationship> results = new ArrayList<>();
564
        if(numberOfResults > 0) {
565
            results = dao.getHybridRelationships(types, pageSize, pageNumber, orderHints, propertyPaths);
566
        }
567
        return results;
568
    }
569

    
570

    
571
    @Override
572
    @Autowired
573
    protected void setDao(ITaxonNameDao dao) {
574
        this.dao = dao;
575
    }
576

    
577
    @Override
578
    public Pager<HybridRelationship> getHybridNames(INonViralName name,	HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
579
        Integer numberOfResults = dao.countHybridNames(name, type);
580

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

    
586
        return new DefaultPagerImpl<HybridRelationship>(pageNumber, numberOfResults, pageSize, results);
587
    }
588

    
589
    @Override
590
    public List<NameRelationship> listNameRelationships(TaxonName name,	Direction direction, NameRelationshipType type, Integer pageSize,
591
            Integer pageNumber, List<OrderHint> orderHints,	List<String> propertyPaths) {
592

    
593
        Integer numberOfResults = dao.countNameRelationships(name, direction, type);
594

    
595
        List<NameRelationship> results = new ArrayList<NameRelationship>();
596
        if (AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
597
            results = dao.getNameRelationships(name, direction, type, pageSize,	pageNumber, orderHints, propertyPaths);
598
        }
599
        return results;
600
    }
601

    
602

    
603
    protected LuceneSearch prepareFindByFuzzyNameSearch(Class<? extends CdmBase> clazz,
604
            INonViralName nvn,
605
            float accuracy,
606
            int maxNoOfResults,
607
            List<Language> languages,
608
            boolean highlightFragments) {
609
        String similarity = Float.toString(accuracy);
610
        String searchSuffix = "~" + similarity;
611

    
612

    
613
        Builder finalQueryBuilder = new Builder();
614
        finalQueryBuilder.setDisableCoord(false);
615
        Builder textQueryBuilder = new Builder();
616
        textQueryBuilder.setDisableCoord(false);
617

    
618
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
619
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
620

    
621
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
622
//    	luceneSearch.setSortFields(sortFields);
623

    
624
        // ---- search criteria
625
        luceneSearch.setCdmTypRestriction(clazz);
626

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

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

    
642
        if(nvn.getSpecificEpithet() != null && !nvn.getSpecificEpithet().equals("")){
643
            fltq.addTerms(nvn.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy, 3);
644
        } else {
645
            //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
646
            textQueryBuilder.add(queryFactory.newTermQuery("specificEpithet", "_null_", false), Occur.MUST);
647
        }
648

    
649
        if(nvn.getInfraSpecificEpithet() != null && !nvn.getInfraSpecificEpithet().equals("")){
650
            fltq.addTerms(nvn.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy, 3);
651
        } else {
652
            //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
653
            textQueryBuilder.add(queryFactory.newTermQuery("infraSpecificEpithet", "_null_", false), Occur.MUST);
654
        }
655

    
656
        if(nvn.getAuthorshipCache() != null && !nvn.getAuthorshipCache().equals("")){
657
            fltq.addTerms(nvn.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy, 3);
658
        } else {
659
            //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
660
        }
661

    
662
        textQueryBuilder.add(fltq, Occur.MUST);
663

    
664
        BooleanQuery textQuery = textQueryBuilder.build();
665
        finalQueryBuilder.add(textQuery, Occur.MUST);
666

    
667
        luceneSearch.setQuery(finalQueryBuilder.build());
668

    
669
        if(highlightFragments){
670
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
671
        }
672
        return luceneSearch;
673
    }
674

    
675
    protected LuceneSearch prepareFindByFuzzyNameCacheSearch(Class<? extends CdmBase> clazz,
676
            String name,
677
            float accuracy,
678
            int maxNoOfResults,
679
            List<Language> languages,
680
            boolean highlightFragments) {
681

    
682
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
683
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
684

    
685
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
686
//    	luceneSearch.setSortFields(sortFields);
687

    
688
        // ---- search criteria
689
        luceneSearch.setCdmTypRestriction(clazz);
690
        FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
691

    
692
        fltq.addTerms(name, "nameCache", accuracy, 3);
693

    
694
         BooleanQuery finalQuery = new BooleanQuery(false);
695

    
696
         finalQuery.add(fltq, Occur.MUST);
697

    
698
        luceneSearch.setQuery(finalQuery);
699

    
700
        if(highlightFragments){
701
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
702
        }
703
        return luceneSearch;
704
    }
705

    
706
    protected LuceneSearch prepareFindByExactNameSearch(Class<? extends CdmBase> clazz,
707
            String name,
708
            boolean wildcard,
709
            List<Language> languages,
710
            boolean highlightFragments) {
711
        Builder textQueryBuilder = new Builder();
712

    
713
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
714
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
715

    
716
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
717
//    	luceneSearch.setSortFields(sortFields);
718

    
719
        // ---- search criteria
720
        luceneSearch.setCdmTypRestriction(clazz);
721

    
722
        if(name != null && !name.equals("")) {
723
            if(wildcard) {
724
                textQueryBuilder.add(new WildcardQuery(new Term("nameCache", name + "*")), Occur.MUST);
725
            } else {
726
                textQueryBuilder.add(queryFactory.newTermQuery("nameCache", name, false), Occur.MUST);
727
            }
728
        }
729

    
730
        luceneSearch.setQuery(textQueryBuilder.build());
731

    
732
        if(highlightFragments){
733
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
734
        }
735
        return luceneSearch;
736
    }
737

    
738
    @Override
739
    public List<SearchResult<TaxonName>> findByNameFuzzySearch(
740
            String name,
741
            float accuracy,
742
            List<Language> languages,
743
            boolean highlightFragments,
744
            List<String> propertyPaths,
745
            int maxNoOfResults) throws IOException, LuceneParseException {
746

    
747
        logger.info("Name to fuzzy search for : " + name);
748
        // parse the input name
749
        NonViralNameParserImpl parser = new NonViralNameParserImpl();
750
        INonViralName nvn = parser.parseFullName(name);
751
        if(name != null && !name.equals("") && nvn == null) {
752
            throw new LuceneParseException("Could not parse name " + name);
753
        }
754
        LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
755

    
756
        // --- execute search
757
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
758

    
759

    
760
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
761
        idFieldMap.put(CdmBaseType.NONVIRALNAME, "id");
762

    
763
        // --- initialize taxa, highlight matches ....
764
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
765

    
766
        @SuppressWarnings("rawtypes")
767
        List<SearchResult<TaxonName>> searchResults = searchResultBuilder.createResultSet(
768
                topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
769

    
770
        return searchResults;
771

    
772
    }
773

    
774
    @Override
775
    public List<DocumentSearchResult> findByNameFuzzySearch(
776
            String name,
777
            float accuracy,
778
            List<Language> languages,
779
            boolean highlightFragments,
780
            int maxNoOfResults) throws IOException, LuceneParseException {
781

    
782
        logger.info("Name to fuzzy search for : " + name);
783
        // parse the input name
784
        NonViralNameParserImpl parser = new NonViralNameParserImpl();
785
        INonViralName nvn = parser.parseFullName(name);
786
        if(name != null && !name.equals("") && nvn == null) {
787
            throw new LuceneParseException("Could not parse name " + name);
788
        }
789
        LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
790

    
791
        // --- execute search
792
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
793

    
794
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
795

    
796
        // --- initialize taxa, highlight matches ....
797
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
798

    
799
        @SuppressWarnings("rawtypes")
800
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
801

    
802
        return searchResults;
803
    }
804

    
805
    @Override
806
    public List<DocumentSearchResult> findByFuzzyNameCacheSearch(
807
            String name,
808
            float accuracy,
809
            List<Language> languages,
810
            boolean highlightFragments,
811
            int maxNoOfResults) throws IOException, LuceneParseException {
812

    
813
        logger.info("Name to fuzzy search for : " + name);
814

    
815
        LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);
816

    
817
        // --- execute search
818
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
819
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
820

    
821
        // --- initialize taxa, highlight matches ....
822
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
823

    
824
        @SuppressWarnings("rawtypes")
825
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
826

    
827
        return searchResults;
828
    }
829

    
830
    @Override
831
    public List<DocumentSearchResult> findByNameExactSearch(
832
            String name,
833
            boolean wildcard,
834
            List<Language> languages,
835
            boolean highlightFragments,
836
            int maxNoOfResults) throws IOException, LuceneParseException {
837

    
838
        logger.info("Name to exact search for : " + name);
839

    
840
        LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);
841

    
842
        // --- execute search
843

    
844

    
845
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
846

    
847
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
848

    
849
        // --- initialize taxa, highlight matches ....
850
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
851

    
852
        @SuppressWarnings("rawtypes")
853
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
854

    
855
        return searchResults;
856
    }
857

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

    
865
    @Override
866
    public List<NameRelationship> listFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
867
        return listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
868
    }
869

    
870
    @Override
871
    public Pager<NameRelationship> pageFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
872
        List<NameRelationship> results = listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
873
        return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
874
    }
875

    
876
    @Override
877
    public List<NameRelationship> listToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
878
        return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
879
    }
880

    
881
    @Override
882
    public Pager<NameRelationship> pageToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
883
        List<NameRelationship> results = listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
884
        return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
885
    }
886

    
887
    @Override
888
    public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
889
            Integer pageSize, Integer pageNumber) {
890
        return getTypeDesignations(name, status, pageSize, pageNumber, null);
891
    }
892

    
893
    @Override
894
    public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
895
                Integer pageSize, Integer pageNumber, List<String> propertyPaths){
896
        long numberOfResults = dao.countTypeDesignations(name, status);
897

    
898
        List<TypeDesignationBase> results = new ArrayList<>();
899
        if(AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)) {
900
            results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);
901
        }
902
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
903
    }
904

    
905
    @Override
906
    public List<TypeDesignationBase> getTypeDesignationsInHomotypicalGroup(UUID nameUuid, Integer pageSize,
907
            Integer pageNumber, List<String> propertyPaths){
908
        TaxonName name = load(nameUuid, Arrays.asList("nomenclaturalSource.citation.authorship"));
909
        Set<TypeDesignationBase<?>> typeDesignations = name.getHomotypicalGroup().getTypeDesignations();
910
        List<TypeDesignationBase> result = defaultBeanInitializer.initializeAll(new ArrayList(typeDesignations), propertyPaths);
911
        return result;
912
    }
913

    
914
    /**
915
     * FIXME Candidate for harmonization
916
     * rename search
917
     */
918
    @Override
919
    public Pager<TaxonName> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,	Integer pageNumber, List<OrderHint> orderHints,
920
            List<String> propertyPaths) {
921
        long numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
922

    
923
        List<TaxonName> results = new ArrayList<>();
924
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
925
            results = dao.searchNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber, orderHints, propertyPaths);
926
        }
927

    
928
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
929
    }
930

    
931
    @Override
932
    public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames(Integer limit, String pattern) {
933
        return dao.getUuidAndTitleCacheOfNames(limit, pattern);
934
    }
935

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

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

    
946
         return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
947
    }
948

    
949
    @Override
950
    public List<TaxonName> findByFullTitle(Class<TaxonName> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria,
951
            Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
952
         Long numberOfResults = dao.countByFullTitle(clazz, queryString, matchmode, criteria);
953

    
954
         List<TaxonName> results = new ArrayList<>();
955
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
956
                results = dao.findByFullTitle(queryString, matchmode, pageSize, pageNumber, criteria, propertyPaths);
957
         }
958

    
959
         return results;
960
    }
961

    
962

    
963
    @Override
964
    public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
965
        return homotypicalGroupDao.findByUuid(uuid);
966
    }
967

    
968
    @Override
969
    @Transactional(readOnly = false)
970
    public UpdateResult updateCaches(Class<? extends TaxonName> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonName> cacheStrategy, IProgressMonitor monitor) {
971
        if (clazz == null){
972
            clazz = TaxonName.class;
973
        }
974
        return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
975
    }
976

    
977

    
978
    @Override
979
    public List<TaggedText> getTaggedName(UUID uuid) {
980
        TaxonName taxonName = dao.load(uuid);
981
        List<TaggedText> taggedName = taxonName.getTaggedName();
982
        return taggedName;
983
    }
984

    
985

    
986
    public DeleteResult isDeletable(TaxonName name, DeleteConfiguratorBase config){
987
        DeleteResult result = new DeleteResult();
988

    
989
        NameDeletionConfigurator nameConfig = null;
990
        if (config instanceof NameDeletionConfigurator){
991
            nameConfig = (NameDeletionConfigurator) config;
992
        }else{
993
             result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
994
             result.setError();
995
             return result;
996
        }
997

    
998
        if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
999
            HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
1000

    
1001
            if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
1002
                result.addException(new Exception( "Name can't be deleted as it is a basionym."));
1003
                result.setAbort();
1004
            }
1005
            if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){
1006
                result.addException(new Exception( "Name can't be deleted as it has a basionym."));
1007
                result.setAbort();
1008
            }
1009
            Set<NameRelationship> relationships = name.getNameRelations();
1010
            for (NameRelationship rel: relationships){
1011
                if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
1012
                    result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
1013
                    result.setAbort();
1014
                    break;
1015
                }
1016
            }
1017
        }
1018

    
1019
        //concepts
1020
        if (! name.getTaxonBases().isEmpty()){
1021
            result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
1022
            result.setAbort();
1023
        }
1024

    
1025
        //hybrid relationships
1026
        if (name.isNonViral()){
1027
            INonViralName nvn = name;
1028
            Set<HybridRelationship> parentHybridRelations = nvn.getHybridParentRelations();
1029
            //Hibernate.initialize(parentHybridRelations);
1030
            if (! parentHybridRelations.isEmpty()){
1031
                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."));
1032
                result.setAbort();
1033
            }
1034
        }
1035
        Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);
1036
        for (CdmBase referencingObject : referencingObjects){
1037
            //DerivedUnit?.storedUnder
1038
            if (referencingObject.isInstanceOf(DerivedUnit.class)){
1039
                String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
1040
                message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());
1041
                result.addException(new ReferencedObjectUndeletableException(message));
1042
                result.addRelatedObject(referencingObject);
1043
                result.setAbort();
1044
            }
1045
            //DescriptionElementSource#nameUsedInSource
1046
            else if (referencingObject.isInstanceOf(DescriptionElementSource.class)){
1047
                String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
1048
                result.addException(new ReferencedObjectUndeletableException(message));
1049
                result.addRelatedObject(referencingObject);
1050
                result.setAbort();
1051
            }
1052
            //NameTypeDesignation#typeName
1053
            else if (referencingObject.isInstanceOf(NameTypeDesignation.class)){
1054
                NameTypeDesignation typeDesignation = HibernateProxyHelper.deproxy(referencingObject, NameTypeDesignation.class);
1055

    
1056
                if (typeDesignation.getTypeName().equals(name) && !typeDesignation.getTypifiedNames().isEmpty()){
1057
                    String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
1058
                    result.addException(new ReferencedObjectUndeletableException(message));
1059
                    result.addRelatedObject(referencingObject);
1060
                    result.setAbort();
1061
                }
1062
            }
1063
            //DeterminationEvent#taxonName
1064
            else if (referencingObject.isInstanceOf(DeterminationEvent.class)){
1065
                String message = "Name can't be deleted as it is used as a determination event";
1066
                result.addException(new ReferencedObjectUndeletableException(message));
1067
                result.addRelatedObject(referencingObject);
1068
                result.setAbort();
1069
        }
1070
        }
1071

    
1072
        //TODO inline references
1073

    
1074

    
1075
        if (!nameConfig.isIgnoreIsReplacedSynonymFor() && name.isReplacedSynonym()){
1076
            String message = "Name can't be deleted as it is a replaced synonym.";
1077
            result.addException(new Exception(message));
1078
            result.setAbort();
1079
        }
1080
        if (!nameConfig.isIgnoreHasReplacedSynonym() && (name.getReplacedSynonyms().size()>0)){
1081
            String message = "Name can't be deleted as it has a replaced synonym.";
1082
            result.addException(new Exception(message));
1083
            result.setAbort();
1084
        }
1085
        return result;
1086

    
1087
    }
1088

    
1089

    
1090
    @Override
1091
    public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config){
1092
        TaxonName name = this.load(nameUUID);
1093
        return isDeletable(name, config);
1094
    }
1095

    
1096
    @Override
1097
    @Transactional(readOnly = true)
1098
    public UpdateResult setAsGroupsBasionym(UUID nameUuid) {
1099
        TaxonName name = dao.load(nameUuid);
1100
        UpdateResult result = new UpdateResult();
1101
        name.setAsGroupsBasionym();
1102
        result.addUpdatedObject(name);
1103
        return result;
1104

    
1105
    }
1106

    
1107
    @Override
1108
    public List<HashMap<String,String>> getNameRecords(){
1109
		return dao.getNameRecords();
1110

    
1111
    }
1112

    
1113
    @Override
1114
    public List<TypeDesignationStatusBase> getTypeDesignationStatusInUse(){
1115
        return typeDesignationDao.getTypeDesignationStatusInUse();
1116
    }
1117

    
1118
    @Override
1119
    public Collection<TypeDesignationStatusFilter> getTypeDesignationStatusFilterTerms(List<Language> preferredLanguages){
1120
        List<TypeDesignationStatusBase> termList = typeDesignationDao.getTypeDesignationStatusInUse();
1121
        Map<String, TypeDesignationStatusFilter>  filterMap = new HashMap<>();
1122
        for(TypeDesignationStatusBase term : termList){
1123
            TypeDesignationStatusFilter filter = new TypeDesignationStatusFilter(term, preferredLanguages, true);
1124
            String key = filter.getKey();
1125
            if(filterMap.containsKey(key)){
1126
                filterMap.get(key).addStatus(term);
1127
            } else {
1128
                filterMap.put(key, filter);
1129
            }
1130
        }
1131
        return filterMap.values();
1132
    }
1133

    
1134
    @Override
1135
    public <S extends TaxonName> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
1136
            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
1137
        return page(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, INCLUDE_UNPUBLISHED);
1138
    }
1139

    
1140
    @Override
1141
    public <S extends TaxonName> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
1142
            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths, boolean includeUnpublished) {
1143

    
1144
        List<S> records;
1145
        long resultSize = dao.count(clazz, restrictions);
1146
        if(AbstractPagerImpl.hasResultsInRange(resultSize, pageIndex, pageSize)){
1147
            records = dao.list(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, includeUnpublished);
1148
        } else {
1149
            records = new ArrayList<>();
1150
        }
1151
        Pager<S> pager = new DefaultPagerImpl<>(pageIndex, resultSize, pageSize, records);
1152
        return pager;
1153
    }
1154

    
1155
    @Override
1156
    public List<UuidAndTitleCache> getUuidAndTitleCacheOfSynonymy(Integer limit, UUID taxonUuid) {
1157
        List<String> propertyPaths = new ArrayList<>();
1158
        propertyPaths.add("synonyms.name.*");
1159
        TaxonBase<?> taxonBase = taxonService.load(taxonUuid, propertyPaths);
1160
        if (taxonBase instanceof Taxon){
1161
            Taxon taxon = (Taxon)taxonBase;
1162
            Set<TaxonName> names = taxon.getSynonymNames();
1163
            List<UuidAndTitleCache> uuidAndTitleCacheList = new ArrayList<>();
1164
            UuidAndTitleCache<TaxonName> uuidAndTitleCache;
1165
            for (TaxonName name: names){
1166
                uuidAndTitleCache = new UuidAndTitleCache<TaxonName>(TaxonName.class, name.getUuid(), name.getId(), name.getTitleCache());
1167
                uuidAndTitleCacheList.add(uuidAndTitleCache);
1168
            }
1169
        }
1170
        return null;
1171
    }
1172

    
1173
    @Override
1174
    public UpdateResult parseName(String stringToBeParsed, NomenclaturalCode code, Rank preferredRank, boolean doDeduplicate) {
1175
        TaxonName name = TaxonNameFactory.NewNameInstance(code, preferredRank);
1176
        return parseName(name, stringToBeParsed, preferredRank, true, doDeduplicate);
1177
    }
1178

    
1179
    @Override
1180
    public UpdateResult parseName(TaxonName nameToBeFilled, String stringToBeParsed, Rank preferredRank,
1181
            boolean doEmpty, boolean doDeduplicate){
1182
        UpdateResult result = new UpdateResult();
1183
        NonViralNameParserImpl nonViralNameParser = NonViralNameParserImpl.NewInstance();
1184
        nonViralNameParser.parseReferencedName(nameToBeFilled, stringToBeParsed, preferredRank, doEmpty);
1185
        TaxonName name = nameToBeFilled;
1186
        if(doDeduplicate) {
1187
            try {
1188
//                Level sqlLogLevel = Logger.getLogger("org.hibernate.SQL").getLevel();
1189
//                Logger.getLogger("org.hibernate.SQL").setLevel(Level.TRACE);
1190

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

    
1241
    private TeamOrPersonBase<?> deduplicateAuthor(TeamOrPersonBase<?> authorship) throws MatchException {
1242
        if (authorship == null){
1243
            return null;
1244
        }
1245
        IParsedMatchStrategy authorMatcher = MatchStrategyFactory.NewParsedTeamOrPersonInstance();
1246
        List<TeamOrPersonBase<?>> matchingAuthors = commonService.findMatching(authorship, authorMatcher);
1247
        if(matchingAuthors.size() >= 1){
1248
            TeamOrPersonBase<?> duplicate = findBestMatching(authorship, matchingAuthors, authorMatcher);
1249
            return duplicate;
1250
        }else{
1251
            if (authorship instanceof Team){
1252
                deduplicateTeam((Team)authorship);
1253
            }
1254
            return authorship;
1255
        }
1256
    }
1257

    
1258
    private void deduplicateTeam(Team team) throws MatchException {
1259
        List<Person> members = team.getTeamMembers();
1260
        IParsedMatchStrategy personMatcher = MatchStrategyFactory.NewParsedPersonInstance();
1261
        for (int i =0; i< members.size(); i++){
1262
            Person person = CdmBase.deproxy(members.get(i));
1263
            List<Person> matchingPersons = commonService.findMatching(person, personMatcher);
1264
            if (matchingPersons.size() > 0){
1265
                person = findBestMatching(person, matchingPersons, personMatcher);
1266
                members.set(i, person);
1267
            }
1268
        }
1269
    }
1270

    
1271
    private <M extends IMatchable> M findBestMatching(M matchable, List<M> matchingList,
1272
            IMatchStrategy referenceMatcher) {
1273
        // FIXME TODO resolve multiple duplications. Use first match for a start
1274
        if(matchingList.isEmpty()){
1275
            return null;
1276
        }
1277
        M bestMatching = matchingList.iterator().next();
1278
        return bestMatching;
1279
    }
1280

    
1281
}
(74-74/100)