Project

General

Profile

Download (58.4 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
import java.util.stream.Collectors;
24

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

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

    
115

    
116
@Service
117
@Transactional(readOnly = true)
118
public class NameServiceImpl
119
          extends IdentifiableServiceBase<TaxonName,ITaxonNameDao>
120
          implements INameService {
121

    
122
    static private final Logger logger = Logger.getLogger(NameServiceImpl.class);
123

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

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

    
152
//***************************** CONSTRUCTOR **********************************/
153

    
154
    /**
155
     * Constructor
156
     */
157
    public NameServiceImpl(){}
158

    
159
//********************* METHODS ***********************************************//
160

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

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

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

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

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

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

    
201
             //all type designation relationships are removed as they belong to the name
202
            deleteTypeDesignation(name, null);
203
            //if original spellings should be deleted, remove it from the nomenclatural source
204
            Set<TaxonName> namesToUpdate = new HashSet<>();
205
            for (Object o: result.getRelatedObjects()){
206
                if (o instanceof NomenclaturalSource && ((NomenclaturalSource)o).getNameUsedInSource() != null && ((NomenclaturalSource)o).getNameUsedInSource().equals(name)){
207
                    NomenclaturalSource nomSource = (NomenclaturalSource)o;
208
                    nomSource.setNameUsedInSource(null);
209
                    namesToUpdate.add(nomSource.getSourcedName());
210
                }
211
            }
212

    
213
            try{
214
                if (!namesToUpdate.isEmpty()){
215
                    Map<UUID, TaxonName> updatedNames = dao.saveOrUpdateAll(namesToUpdate);
216
                    Set<TaxonName> names = new HashSet<>(updatedNames.values());
217
                    result.addUpdatedObjects(names);
218
                }
219
                dao.delete(name);
220
                result.addDeletedObject(name);
221

    
222
            }catch(Exception e){
223
                result.addException(e);
224
                result.setError();
225
            }
226
            return result;
227
        }
228

    
229
        return result;
230
    }
231

    
232
    @Override
233
    @Transactional(readOnly = false)
234
    public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {
235

    
236
        TaxonName name = dao.load(nameUUID);
237
        return delete(name, config);
238
    }
239

    
240
    @Override
241
    @Transactional(readOnly = false)
242
    public UpdateResult cloneTypeDesignation(UUID nameUuid, SpecimenTypeDesignation baseDesignation,
243
            String accessionNumber, String barcode, String catalogNumber,
244
            UUID collectionUuid, SpecimenTypeDesignationStatus typeStatus, URI preferredStableUri){
245
        UpdateResult result = new UpdateResult();
246

    
247
        DerivedUnit baseSpecimen = HibernateProxyHelper.deproxy(occurrenceService.load(baseDesignation.getTypeSpecimen().getUuid(), Arrays.asList("collection")), DerivedUnit.class);
248
        DerivedUnit duplicate = DerivedUnit.NewInstance(baseSpecimen.getRecordBasis());
249
        DerivationEvent derivedFrom = baseSpecimen.getDerivedFrom();
250
        Collection<FieldUnit> fieldUnits = occurrenceService.findFieldUnits(baseSpecimen.getUuid(), null);
251
        if(fieldUnits.size()!=1){
252
            result.addException(new Exception("More than one or no field unit found for specimen"));
253
            result.setError();
254
            return result;
255
        }
256
        for (SpecimenOrObservationBase original : derivedFrom.getOriginals()) {
257
            DerivationEvent.NewSimpleInstance(original, duplicate, derivedFrom.getType());
258

    
259
        }
260
        duplicate.setAccessionNumber(accessionNumber);
261
        duplicate.setBarcode(barcode);
262
        duplicate.setCatalogNumber(catalogNumber);
263
        duplicate.setCollection(collectionService.load(collectionUuid));
264
        SpecimenTypeDesignation typeDesignation = SpecimenTypeDesignation.NewInstance();
265
        typeDesignation.setTypeSpecimen(duplicate);
266
        typeDesignation.setTypeStatus(typeStatus);
267
        typeDesignation.getTypeSpecimen().setPreferredStableUri(preferredStableUri);
268

    
269
        TaxonName name = load(nameUuid);
270
        name.getTypeDesignations().add(typeDesignation);
271

    
272
        result.setCdmEntity(typeDesignation);
273
        result.addUpdatedObject(name);
274
        return result;
275
    }
276

    
277
    @Override
278
    @Transactional
279
    public DeleteResult deleteTypeDesignation(TaxonName name, TypeDesignationBase<?> typeDesignation){
280
    	if(typeDesignation != null && typeDesignation .isPersited()){
281
    		typeDesignation = HibernateProxyHelper.deproxy(sourcedEntityDao.load(typeDesignation.getUuid()), TypeDesignationBase.class);
282
    	}
283

    
284
        DeleteResult result = new DeleteResult();
285
        if (name == null && typeDesignation == null){
286
            result.setError();
287
            return result;
288
        }else if (name != null && typeDesignation != null){
289
            removeSingleDesignation(name, typeDesignation);
290
        }else if (name != null){
291
            @SuppressWarnings("rawtypes")
292
            Set<TypeDesignationBase<?>> designationSet = new HashSet(name.getTypeDesignations());
293
            for (TypeDesignationBase<?> desig : designationSet){
294
                desig = CdmBase.deproxy(desig);
295
                removeSingleDesignation(name, desig);
296
            }
297
        }else if (typeDesignation != null){
298
            @SuppressWarnings("unchecked")
299
            Set<TaxonName> nameSet = new HashSet(typeDesignation.getTypifiedNames());
300
            for (TaxonName singleName : nameSet){
301
                singleName = CdmBase.deproxy(singleName);
302
                removeSingleDesignation(singleName, typeDesignation);
303
            }
304
        }
305
        result.addDeletedObject(typeDesignation);
306
        result.addUpdatedObject(name);
307
        return result;
308
    }
309

    
310

    
311
    @Override
312
    @Transactional(readOnly = false)
313
    public DeleteResult deleteTypeDesignation(UUID nameUuid, UUID typeDesignationUuid){
314
        TaxonName nameBase = load(nameUuid);
315
        TypeDesignationBase<?> typeDesignation = HibernateProxyHelper.deproxy(sourcedEntityDao.load(typeDesignationUuid), TypeDesignationBase.class);
316
        return deleteTypeDesignation(nameBase, typeDesignation);
317
    }
318

    
319
    /**
320
     * @param name
321
     * @param typeDesignation
322
     */
323
    @Transactional
324
    private void removeSingleDesignation(TaxonName name, TypeDesignationBase<?> typeDesignation) {
325

    
326
        name.removeTypeDesignation(typeDesignation);
327
        if (typeDesignation.getTypifiedNames().isEmpty()){
328
            typeDesignation.removeType();
329
            if (!typeDesignation.getRegistrations().isEmpty()){
330
                for(Object reg: typeDesignation.getRegistrations()){
331
                    if (reg instanceof Registration){
332
                        ((Registration)reg).removeTypeDesignation(typeDesignation);
333
                    }
334
                }
335
            }
336

    
337
            typeDesignationDao.delete(typeDesignation);
338

    
339
        }
340
    }
341

    
342

    
343

    
344
    /**
345
     * @param name
346
     * @param config
347
     */
348
    private void removeNameRelationshipsByDeleteConfig(TaxonName name, NameDeletionConfigurator config) {
349
        try {
350
            if (config.isRemoveAllNameRelationships()){
351
                Set<NameRelationship> rels = getModifiableSet(name.getNameRelations());
352
                for (NameRelationship rel : rels){
353
                    name.removeNameRelationship(rel);
354
                }
355
            }else{
356
                //relations to this name
357
                Set<NameRelationship> rels = getModifiableSet(name.getRelationsToThisName());
358
                for (NameRelationship rel : rels){
359
                    if (config.isIgnoreHasBasionym() && NameRelationshipType.BASIONYM().equals(rel.getType() )){
360
                            name.removeNameRelationship(rel);
361
                    }else if (config.isIgnoreHasReplacedSynonym() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
362
                        name.removeNameRelationship(rel);
363
                    }
364
                }
365
                //relations from this name
366
                rels = getModifiableSet(name.getRelationsFromThisName());
367
                for (NameRelationship rel : rels){
368
                    if (config.isIgnoreIsBasionymFor() && NameRelationshipType.BASIONYM().equals(rel.getType())  ){
369
                        name.removeNameRelationship(rel);
370
                    }else if (config.isIgnoreIsReplacedSynonymFor() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
371
                        name.removeNameRelationship(rel);
372
                    }
373
                }
374

    
375
            }
376
        } catch (Exception e) {
377
            throw new RuntimeException(e);
378
        }
379
    }
380

    
381
    /**
382
     * @param name
383
     * @return
384
     */
385
    private Set<NameRelationship> getModifiableSet(Set<NameRelationship> relations) {
386
        Set<NameRelationship> rels = new HashSet<NameRelationship>();
387
        for (NameRelationship rel : relations){
388
            rels.add(rel);
389
        }
390
        return rels;
391
    }
392

    
393
//********************* METHODS ****************************************************************//
394

    
395

    
396
    /**
397
     * TODO candidate for harmonization
398
     * new name findByName
399
     */
400
    @Override
401
    @Deprecated
402
    public List<TaxonName> getNamesByNameCache(String nameCache){
403
        boolean includeAuthors = false;
404
        List result = dao.findByName(includeAuthors, nameCache, MatchMode.EXACT, null, null, null, null);
405
        return result;
406
    }
407

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

    
421
    /**
422
     * TODO candidate for harmonization
423
     * new name saveHomotypicalGroups
424
     *
425
     * findByTitle
426
     */
427
    @Override
428
    @Deprecated
429
    public List<TaxonName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){
430
        List<TaxonName> result = dao.findByName(false, nameCache, matchMode, null, null, null , propertyPaths);
431
        return result;
432
    }
433

    
434
    @Override
435
    public Pager<TaxonNameParts> findTaxonNameParts(Optional<String> genusOrUninomial,
436
            Optional<String> infraGenericEpithet, Optional<String> specificEpithet,
437
            Optional<String> infraSpecificEpithet, Rank rank, Set<UUID> excludedNamesUuids,
438
            Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
439

    
440

    
441
        long count = dao.countTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraGenericEpithet, rank, excludedNamesUuids);
442

    
443
        List<TaxonNameParts> results;
444
        if(AbstractPagerImpl.hasResultsInRange(count, pageIndex, pageSize)){
445
            results = dao.findTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
446
                    rank, excludedNamesUuids,
447
                    pageSize, pageIndex, orderHints);
448
        } else {
449
            results = new ArrayList<>();
450
        }
451

    
452
        return new DefaultPagerImpl<TaxonNameParts>(pageIndex, count, pageSize, results);
453
    }
454

    
455
    /**
456
     * {@inheritDoc}
457
     */
458
    @Override
459
    public Pager<TaxonNameParts> findTaxonNameParts(TaxonNamePartsFilter filter, String namePartQueryString,
460
            Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
461

    
462
        return findTaxonNameParts(
463
                filter.uninomialQueryString(namePartQueryString),
464
                filter.infraGenericEpithet(namePartQueryString),
465
                filter.specificEpithet(namePartQueryString),
466
                filter.infraspecificEpithet(namePartQueryString),
467
                filter.getRank(),
468
                filter.getExludedNamesUuids(),
469
                pageSize, pageIndex, orderHints);
470
    }
471

    
472
    /**
473
     * TODO candidate for harmonization
474
     * new name saveHomotypicalGroups
475
     */
476
    @Override
477
    @Transactional(readOnly = false)
478
    public Map<UUID, HomotypicalGroup> saveAllHomotypicalGroups(Collection<HomotypicalGroup> homotypicalGroups){
479
        return homotypicalGroupDao.saveAll(homotypicalGroups);
480
    }
481

    
482
    /**
483
     * TODO candidate for harmonization
484
     * new name saveTypeDesignations
485
     */
486
    @Override
487
    @Transactional(readOnly = false)
488
    public Map<UUID, TypeDesignationBase<?>> saveTypeDesignationAll(Collection<TypeDesignationBase<?>> typeDesignationCollection){
489
        return typeDesignationDao.saveAll(typeDesignationCollection);
490
    }
491

    
492
    /**
493
     * TODO candidate for harmonization
494
     * new name getNomenclaturalStatus
495
     */
496
    @Override
497
    public List<NomenclaturalStatus> getAllNomenclaturalStatus(int limit, int start){
498
        return nomStatusDao.list(limit, start);
499
    }
500

    
501
    @Override
502
    public NomenclaturalStatus loadNomenclaturalStatus(UUID uuid,  List<String> propertyPaths){
503
        return nomStatusDao.load(uuid, propertyPaths);
504
    }
505

    
506
    /**
507
     * TODO candidate for harmonization
508
     * new name getTypeDesignations
509
     */
510
    @Override
511
    public List<TypeDesignationBase<?>> getAllTypeDesignations(int limit, int start){
512
        return typeDesignationDao.getAllTypeDesignations(limit, start);
513
    }
514

    
515
    @Override
516
    public TypeDesignationBase<?> loadTypeDesignation(int id, List<String> propertyPaths){
517
        return typeDesignationDao.load(id, propertyPaths);
518
    }
519

    
520
    @Override
521
    public TypeDesignationBase<?> loadTypeDesignation(UUID uuid, List<String> propertyPaths){
522
        return typeDesignationDao.load(uuid, propertyPaths);
523
    }
524

    
525
    @Override
526
    public List<TypeDesignationBase<?>> loadTypeDesignations(List<UUID> uuids, List<String> propertyPaths){
527
    	if(uuids == null) {
528
            return null;
529
        }
530

    
531
        List<TypeDesignationBase<?>> entities = new ArrayList<>();
532
        for(UUID uuid : uuids) {
533
            entities.add(uuid == null ? null : typeDesignationDao.load(uuid, propertyPaths));
534
        }
535
        return entities;
536
    }
537

    
538
    /**
539
     * FIXME Candidate for harmonization
540
     * homotypicalGroupService.list
541
     */
542
    @Override
543
    public List<HomotypicalGroup> getAllHomotypicalGroups(int limit, int start){
544
        return homotypicalGroupDao.list(limit, start);
545
    }
546

    
547

    
548
    @Override
549
    public List<NomenclaturalSource> listOriginalSpellings(Integer pageSize, Integer pageNumber,
550
            List<OrderHint> orderHints, List<String> propertyPaths) {
551

    
552
        Long numberOfResults = sourcedDao.countWithNameUsedInSource(NomenclaturalSource.class);
553
        List<NomenclaturalSource> results = new ArrayList<>();
554
        if(numberOfResults > 0) {
555
            results = sourcedDao.listWithNameUsedInSource(NomenclaturalSource.class, pageSize, pageNumber, orderHints, propertyPaths);
556
        }
557
        return results;
558
    }
559

    
560
    @Override
561
    public List<NameRelationship> listNameRelationships(Set<NameRelationshipType> types,
562
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
563

    
564
        Long numberOfResults = dao.countNameRelationships(types);
565
        List<NameRelationship> results = new ArrayList<>();
566
        if(numberOfResults > 0) {
567
            results = dao.getNameRelationships(types, pageSize, pageNumber, orderHints, propertyPaths);
568
        }
569
        return results;
570
    }
571

    
572
    @Override
573
    public List<HybridRelationship> listHybridRelationships(Set<HybridRelationshipType> types,
574
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
575

    
576
        Long numberOfResults = dao.countHybridRelationships(types);
577
        List<HybridRelationship> results = new ArrayList<>();
578
        if(numberOfResults > 0) {
579
            results = dao.getHybridRelationships(types, pageSize, pageNumber, orderHints, propertyPaths);
580
        }
581
        return results;
582
    }
583

    
584

    
585
    @Override
586
    @Autowired
587
    protected void setDao(ITaxonNameDao dao) {
588
        this.dao = dao;
589
    }
590

    
591
    @Override
592
    public Pager<HybridRelationship> getHybridNames(INonViralName name,	HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
593
        Integer numberOfResults = dao.countHybridNames(name, type);
594

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

    
600
        return new DefaultPagerImpl<HybridRelationship>(pageNumber, numberOfResults, pageSize, results);
601
    }
602

    
603
    @Override
604
    public List<NameRelationship> listNameRelationships(TaxonName name,	Direction direction, NameRelationshipType type, Integer pageSize,
605
            Integer pageNumber, List<OrderHint> orderHints,	List<String> propertyPaths) {
606

    
607
        Integer numberOfResults = dao.countNameRelationships(name, direction, type);
608

    
609
        List<NameRelationship> results = new ArrayList<NameRelationship>();
610
        if (AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
611
            results = dao.getNameRelationships(name, direction, type, pageSize,	pageNumber, orderHints, propertyPaths);
612
        }
613
        return results;
614
    }
615

    
616

    
617
    protected LuceneSearch prepareFindByFuzzyNameSearch(Class<? extends CdmBase> clazz,
618
            INonViralName nvn,
619
            float accuracy,
620
            int maxNoOfResults,
621
            List<Language> languages,
622
            boolean highlightFragments) {
623
        String similarity = Float.toString(accuracy);
624
        String searchSuffix = "~" + similarity;
625

    
626

    
627
        Builder finalQueryBuilder = new Builder();
628
        finalQueryBuilder.setDisableCoord(false);
629
        Builder textQueryBuilder = new Builder();
630
        textQueryBuilder.setDisableCoord(false);
631

    
632
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
633
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
634

    
635
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
636
//    	luceneSearch.setSortFields(sortFields);
637

    
638
        // ---- search criteria
639
        luceneSearch.setCdmTypRestriction(clazz);
640

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

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

    
656
        if(nvn.getSpecificEpithet() != null && !nvn.getSpecificEpithet().equals("")){
657
            fltq.addTerms(nvn.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy, 3);
658
        } else {
659
            //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
660
            textQueryBuilder.add(queryFactory.newTermQuery("specificEpithet", "_null_", false), Occur.MUST);
661
        }
662

    
663
        if(nvn.getInfraSpecificEpithet() != null && !nvn.getInfraSpecificEpithet().equals("")){
664
            fltq.addTerms(nvn.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy, 3);
665
        } else {
666
            //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
667
            textQueryBuilder.add(queryFactory.newTermQuery("infraSpecificEpithet", "_null_", false), Occur.MUST);
668
        }
669

    
670
        if(nvn.getAuthorshipCache() != null && !nvn.getAuthorshipCache().equals("")){
671
            fltq.addTerms(nvn.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy, 3);
672
        } else {
673
            //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
674
        }
675

    
676
        textQueryBuilder.add(fltq, Occur.MUST);
677

    
678
        BooleanQuery textQuery = textQueryBuilder.build();
679
        finalQueryBuilder.add(textQuery, Occur.MUST);
680

    
681
        luceneSearch.setQuery(finalQueryBuilder.build());
682

    
683
        if(highlightFragments){
684
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
685
        }
686
        return luceneSearch;
687
    }
688

    
689
    protected LuceneSearch prepareFindByFuzzyNameCacheSearch(Class<? extends CdmBase> clazz,
690
            String name,
691
            float accuracy,
692
            int maxNoOfResults,
693
            List<Language> languages,
694
            boolean highlightFragments) {
695

    
696
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
697
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
698

    
699
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
700
//    	luceneSearch.setSortFields(sortFields);
701

    
702
        // ---- search criteria
703
        luceneSearch.setCdmTypRestriction(clazz);
704
        FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
705

    
706
        fltq.addTerms(name, "nameCache", accuracy, 3);
707

    
708
         BooleanQuery finalQuery = new BooleanQuery(false);
709

    
710
         finalQuery.add(fltq, Occur.MUST);
711

    
712
        luceneSearch.setQuery(finalQuery);
713

    
714
        if(highlightFragments){
715
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
716
        }
717
        return luceneSearch;
718
    }
719

    
720
    protected LuceneSearch prepareFindByExactNameSearch(Class<? extends CdmBase> clazz,
721
            String name,
722
            boolean wildcard,
723
            List<Language> languages,
724
            boolean highlightFragments) {
725
        Builder textQueryBuilder = new Builder();
726

    
727
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
728
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
729

    
730
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
731
//    	luceneSearch.setSortFields(sortFields);
732

    
733
        // ---- search criteria
734
        luceneSearch.setCdmTypRestriction(clazz);
735

    
736
        if(name != null && !name.equals("")) {
737
            if(wildcard) {
738
                textQueryBuilder.add(new WildcardQuery(new Term("nameCache", name + "*")), Occur.MUST);
739
            } else {
740
                textQueryBuilder.add(queryFactory.newTermQuery("nameCache", name, false), Occur.MUST);
741
            }
742
        }
743

    
744
        luceneSearch.setQuery(textQueryBuilder.build());
745

    
746
        if(highlightFragments){
747
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
748
        }
749
        return luceneSearch;
750
    }
751

    
752
    @Override
753
    public List<SearchResult<TaxonName>> findByNameFuzzySearch(
754
            String name,
755
            float accuracy,
756
            List<Language> languages,
757
            boolean highlightFragments,
758
            List<String> propertyPaths,
759
            int maxNoOfResults) throws IOException, LuceneParseException {
760

    
761
        logger.info("Name to fuzzy search for : " + name);
762
        // parse the input name
763
        NonViralNameParserImpl parser = new NonViralNameParserImpl();
764
        INonViralName nvn = parser.parseFullName(name);
765
        if(name != null && !name.equals("") && nvn == null) {
766
            throw new LuceneParseException("Could not parse name " + name);
767
        }
768
        LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
769

    
770
        // --- execute search
771
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
772

    
773

    
774
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
775
        idFieldMap.put(CdmBaseType.TAXON_NAME, "id");
776

    
777
        // --- initialize taxa, highlight matches ....
778
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
779

    
780
        @SuppressWarnings("rawtypes")
781
        List<SearchResult<TaxonName>> searchResults = searchResultBuilder.createResultSet(
782
                topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
783

    
784
        return searchResults;
785

    
786
    }
787

    
788
    @Override
789
    public List<DocumentSearchResult> findByNameFuzzySearch(
790
            String name,
791
            float accuracy,
792
            List<Language> languages,
793
            boolean highlightFragments,
794
            int maxNoOfResults) throws IOException, LuceneParseException {
795

    
796
        logger.info("Name to fuzzy search for : " + name);
797
        // parse the input name
798
        NonViralNameParserImpl parser = new NonViralNameParserImpl();
799
        INonViralName nvn = parser.parseFullName(name);
800
        if(name != null && !name.equals("") && nvn == null) {
801
            throw new LuceneParseException("Could not parse name " + name);
802
        }
803
        LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
804

    
805
        // --- execute search
806
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
807

    
808
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
809

    
810
        // --- initialize taxa, highlight matches ....
811
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
812

    
813
        @SuppressWarnings("rawtypes")
814
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
815

    
816
        return searchResults;
817
    }
818

    
819
    @Override
820
    public List<DocumentSearchResult> findByFuzzyNameCacheSearch(
821
            String name,
822
            float accuracy,
823
            List<Language> languages,
824
            boolean highlightFragments,
825
            int maxNoOfResults) throws IOException, LuceneParseException {
826

    
827
        logger.info("Name to fuzzy search for : " + name);
828

    
829
        LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);
830

    
831
        // --- execute search
832
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
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 List<DocumentSearchResult> findByNameExactSearch(
846
            String name,
847
            boolean wildcard,
848
            List<Language> languages,
849
            boolean highlightFragments,
850
            int maxNoOfResults) throws IOException, LuceneParseException {
851

    
852
        logger.info("Name to exact search for : " + name);
853

    
854
        LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);
855

    
856
        // --- execute search
857

    
858

    
859
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
860

    
861
        // --- initialize taxa, highlight matches ....
862
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
863

    
864
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
865

    
866
        return searchResults;
867
    }
868

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

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

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

    
887
    @Override
888
    public List<NameRelationship> listToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
889
        return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
890
    }
891

    
892
    @Override
893
    public Pager<NameRelationship> pageToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
894
        List<NameRelationship> results = listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
895
        return new DefaultPagerImpl<>(pageNumber, results.size(), pageSize, results);
896
    }
897

    
898
    @Override
899
    public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
900
            Integer pageSize, Integer pageNumber) {
901
        return getTypeDesignations(name, status, pageSize, pageNumber, null);
902
    }
903

    
904
    @Override
905
    public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
906
                Integer pageSize, Integer pageNumber, List<String> propertyPaths){
907
        long numberOfResults = dao.countTypeDesignations(name, status);
908

    
909
        List<TypeDesignationBase> results = new ArrayList<>();
910
        if(AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)) {
911
            results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);
912
        }
913
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
914
    }
915

    
916
    @Override
917
    public List<TypeDesignationBase> getTypeDesignationsInHomotypicalGroup(UUID nameUuid, Integer pageSize,
918
            Integer pageNumber, List<String> propertyPaths){
919
        TaxonName name = load(nameUuid, Arrays.asList("nomenclaturalSource.citation.authorship"));
920
        Set<TypeDesignationBase<?>> typeDesignations = name.getHomotypicalGroup().getTypeDesignations();
921
        List<TypeDesignationBase> result = defaultBeanInitializer.initializeAll(new ArrayList(typeDesignations), propertyPaths);
922
        return result;
923
    }
924

    
925
    /**
926
     * FIXME Candidate for harmonization
927
     * rename search
928
     */
929
    @Override
930
    public Pager<TaxonName> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,	Integer pageNumber, List<OrderHint> orderHints,
931
            List<String> propertyPaths) {
932
        long numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
933

    
934
        List<TaxonName> results = new ArrayList<>();
935
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
936
            results = dao.searchNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber, orderHints, propertyPaths);
937
        }
938

    
939
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
940
    }
941

    
942
    @Override
943
    public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames(Integer limit, String pattern) {
944
        return dao.getUuidAndTitleCacheOfNames(limit, pattern);
945
    }
946

    
947
    @Override
948
    public Pager<TaxonName> findByName(Class<TaxonName> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria,
949
            Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
950
         Long numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
951

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

    
957
         return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
958
    }
959

    
960
    @Override
961
    public List<TaxonName> findByFullTitle(Class<TaxonName> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria,
962
            Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
963
         Long numberOfResults = dao.countByFullTitle(clazz, queryString, matchmode, criteria);
964

    
965
         List<TaxonName> results = new ArrayList<>();
966
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
967
                results = dao.findByFullTitle(queryString, matchmode, pageSize, pageNumber, criteria, propertyPaths);
968
         }
969

    
970
         return results;
971
    }
972

    
973

    
974
    @Override
975
    public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
976
        return homotypicalGroupDao.findByUuid(uuid);
977
    }
978

    
979
    @Override
980
    @Transactional(readOnly = false)
981
    public UpdateResult updateCaches(Class<? extends TaxonName> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonName> cacheStrategy, IProgressMonitor monitor) {
982
        if (clazz == null){
983
            clazz = TaxonName.class;
984
        }
985
        return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
986
    }
987

    
988

    
989
    @Override
990
    public List<TaggedText> getTaggedName(UUID uuid) {
991
        TaxonName taxonName = dao.load(uuid);
992
        List<TaggedText> taggedName = taxonName.getTaggedName();
993
        return taggedName;
994
    }
995

    
996

    
997
    public DeleteResult isDeletable(TaxonName name, DeleteConfiguratorBase config, UUID taxonUuid){
998
        DeleteResult result = new DeleteResult();
999

    
1000
         NameDeletionConfigurator nameConfig = null;
1001
        if (config instanceof NameDeletionConfigurator){
1002
            nameConfig = (NameDeletionConfigurator) config;
1003
        }else{
1004
             result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
1005
             result.setError();
1006
             return result;
1007
        }
1008

    
1009
        if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
1010
            HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
1011

    
1012
            if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
1013
                result.addException(new Exception( "Name can't be deleted as it is a basionym."));
1014
                result.setAbort();
1015
            }
1016
            if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){
1017
                result.addException(new Exception( "Name can't be deleted as it has a basionym."));
1018
                result.setAbort();
1019
            }
1020
            Set<NameRelationship> relationships = name.getNameRelations();
1021
            for (NameRelationship rel: relationships){
1022
                if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
1023
                    result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
1024
                    result.setAbort();
1025
                    break;
1026
                }
1027
            }
1028
        }
1029
        //concepts
1030
        if (!name.getTaxonBases().isEmpty()){
1031
            boolean isDeletableTaxon = true;
1032
            List <TaxonBase> notDeletedTaxonBases = name.getTaxonBases().stream()
1033
                    .filter((taxonBase) -> !taxonBase.getUuid().equals(taxonUuid))
1034
                    .collect(Collectors.toList());
1035
            if (!notDeletedTaxonBases.isEmpty()){
1036
                result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
1037
                result.setAbort();
1038
            }
1039
        }
1040

    
1041
        //hybrid relationships
1042
        if (name.isNonViral()){
1043
            INonViralName nvn = name;
1044
            Set<HybridRelationship> parentHybridRelations = nvn.getHybridParentRelations();
1045
            //Hibernate.initialize(parentHybridRelations);
1046
            if (! parentHybridRelations.isEmpty()){
1047
                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."));
1048
                result.setAbort();
1049
            }
1050
        }
1051
        Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);
1052
        for (CdmBase referencingObject : referencingObjects){
1053
            //DerivedUnit?.storedUnder
1054
            if (referencingObject.isInstanceOf(DerivedUnit.class)){
1055
                String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
1056
                message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());
1057
                result.addException(new ReferencedObjectUndeletableException(message));
1058
                result.addRelatedObject(referencingObject);
1059
                result.setAbort();
1060
            }
1061

    
1062
            //DescriptionElementSource#nameUsedInSource
1063
            else if (referencingObject.isInstanceOf(DescriptionElementSource.class) && !referencingObject.isInstanceOf(NomenclaturalSource.class) ){
1064
                String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
1065
                result.addException(new ReferencedObjectUndeletableException(message));
1066
                result.addRelatedObject(referencingObject);
1067
                result.setAbort();
1068
            }
1069
            //NameTypeDesignation#typeName
1070
            else if (referencingObject.isInstanceOf(NameTypeDesignation.class)){
1071
                NameTypeDesignation typeDesignation = HibernateProxyHelper.deproxy(referencingObject, NameTypeDesignation.class);
1072

    
1073
                if (typeDesignation.getTypeName().equals(name) && !typeDesignation.getTypifiedNames().isEmpty()){
1074
                    String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
1075
                    result.addException(new ReferencedObjectUndeletableException(message));
1076
                    result.addRelatedObject(referencingObject);
1077
                    result.setAbort();
1078
                }
1079
            }
1080
            //DeterminationEvent#taxonName
1081
            else if (referencingObject.isInstanceOf(DeterminationEvent.class)){
1082
                String message = "Name can't be deleted as it is used as a determination event";
1083
                result.addException(new ReferencedObjectUndeletableException(message));
1084
                result.addRelatedObject(referencingObject);
1085
                result.setAbort();
1086
            }
1087
            //original spelling
1088
            else if (referencingObject.isInstanceOf(NomenclaturalSource.class) && !((NameDeletionConfigurator)config).isIgnoreIsOriginalSpellingFor()){
1089
                if (((NomenclaturalSource)referencingObject).getNameUsedInSource() != null && ((NomenclaturalSource)referencingObject).getNameUsedInSource().equals(name)){
1090
                    String message = "Name can't be deleted as it is used as original spelling";
1091
                    result.addException(new ReferencedObjectUndeletableException(message));
1092
                    result.addRelatedObject(referencingObject);
1093
                    result.setAbort();
1094
                }
1095
            }
1096
            if (referencingObject.isInstanceOf(NomenclaturalSource.class)){
1097
                if (((NomenclaturalSource)referencingObject).getNameUsedInSource() != null && ((NomenclaturalSource)referencingObject).getNameUsedInSource().equals(name)){
1098
                    result.addRelatedObject(referencingObject);
1099
                }
1100

    
1101
            }
1102
        }
1103

    
1104
        //TODO inline references
1105

    
1106

    
1107
        if (!nameConfig.isIgnoreIsReplacedSynonymFor() && name.isReplacedSynonym()){
1108
            String message = "Name can't be deleted as it is a replaced synonym.";
1109
            result.addException(new Exception(message));
1110
            result.setAbort();
1111
        }
1112
        if (!nameConfig.isIgnoreHasReplacedSynonym() && (name.getReplacedSynonyms().size()>0)){
1113
            String message = "Name can't be deleted as it has a replaced synonym.";
1114
            result.addException(new Exception(message));
1115
            result.setAbort();
1116
        }
1117
        return result;
1118

    
1119
    }
1120

    
1121

    
1122
    @Override
1123
    public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config){
1124
        TaxonName name = this.load(nameUUID);
1125
        return isDeletable(name, config, null);
1126
    }
1127

    
1128
    @Override
1129
    public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config, UUID taxonUuid){
1130
        TaxonName name = this.load(nameUUID);
1131
        return isDeletable(name, config, taxonUuid);
1132
    }
1133

    
1134
    @Override
1135
    @Transactional(readOnly = true)
1136
    public UpdateResult setAsGroupsBasionym(UUID nameUuid) {
1137
        TaxonName name = dao.load(nameUuid);
1138
        UpdateResult result = new UpdateResult();
1139
        name.setAsGroupsBasionym();
1140
        result.addUpdatedObject(name);
1141
        return result;
1142

    
1143
    }
1144

    
1145
    @Override
1146
    public List<HashMap<String,String>> getNameRecords(){
1147
		return dao.getNameRecords();
1148

    
1149
    }
1150

    
1151
    @Override
1152
    public List<TypeDesignationStatusBase> getTypeDesignationStatusInUse(){
1153
        return typeDesignationDao.getTypeDesignationStatusInUse();
1154
    }
1155

    
1156
    @Override
1157
    public Collection<TypeDesignationStatusFilter> getTypeDesignationStatusFilterTerms(List<Language> preferredLanguages){
1158
        List<TypeDesignationStatusBase> termList = typeDesignationDao.getTypeDesignationStatusInUse();
1159
        Map<String, TypeDesignationStatusFilter>  filterMap = new HashMap<>();
1160
        for(TypeDesignationStatusBase term : termList){
1161
            TypeDesignationStatusFilter filter = new TypeDesignationStatusFilter(term, preferredLanguages, true);
1162
            String key = filter.getKey();
1163
            if(filterMap.containsKey(key)){
1164
                filterMap.get(key).addStatus(term);
1165
            } else {
1166
                filterMap.put(key, filter);
1167
            }
1168
        }
1169
        return filterMap.values();
1170
    }
1171

    
1172
    @Override
1173
    public <S extends TaxonName> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
1174
            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
1175
        return page(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, INCLUDE_UNPUBLISHED);
1176
    }
1177

    
1178
    @Override
1179
    public <S extends TaxonName> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
1180
            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths, boolean includeUnpublished) {
1181

    
1182
        List<S> records;
1183
        long resultSize = dao.count(clazz, restrictions);
1184
        if(AbstractPagerImpl.hasResultsInRange(resultSize, pageIndex, pageSize)){
1185
            records = dao.list(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, includeUnpublished);
1186
        } else {
1187
            records = new ArrayList<>();
1188
        }
1189
        Pager<S> pager = new DefaultPagerImpl<>(pageIndex, resultSize, pageSize, records);
1190
        return pager;
1191
    }
1192

    
1193
    @Override
1194
    public List<UuidAndTitleCache> getUuidAndTitleCacheOfSynonymy(Integer limit, UUID taxonUuid) {
1195
        List<String> propertyPaths = new ArrayList<>();
1196
        propertyPaths.add("synonyms.name.*");
1197
        TaxonBase<?> taxonBase = taxonService.load(taxonUuid, propertyPaths);
1198
        if (taxonBase instanceof Taxon){
1199
            Taxon taxon = (Taxon)taxonBase;
1200
            Set<TaxonName> names = taxon.getSynonymNames();
1201
            List<UuidAndTitleCache> uuidAndTitleCacheList = new ArrayList<>();
1202
            UuidAndTitleCache<TaxonName> uuidAndTitleCache;
1203
            for (TaxonName name: names){
1204
                uuidAndTitleCache = new UuidAndTitleCache<TaxonName>(TaxonName.class, name.getUuid(), name.getId(), name.getTitleCache());
1205
                uuidAndTitleCacheList.add(uuidAndTitleCache);
1206
            }
1207
        }
1208
        return null;
1209
    }
1210

    
1211
    @Override
1212
    public UpdateResult parseName(String stringToBeParsed, NomenclaturalCode code, Rank preferredRank, boolean doDeduplicate) {
1213
        TaxonName name = TaxonNameFactory.NewNameInstance(code, preferredRank);
1214
        return parseName(name, stringToBeParsed, preferredRank, true, doDeduplicate);
1215
    }
1216

    
1217
    @Override
1218
    public UpdateResult parseName(TaxonName nameToBeFilled, String stringToBeParsed, Rank preferredRank,
1219
            boolean doEmpty, boolean doDeduplicate){
1220

    
1221
        UpdateResult result = new UpdateResult();
1222
        NonViralNameParserImpl nonViralNameParser = NonViralNameParserImpl.NewInstance();
1223
        nonViralNameParser.parseReferencedName(nameToBeFilled, stringToBeParsed, preferredRank, doEmpty);
1224
        TaxonName name = nameToBeFilled;
1225
        if(doDeduplicate) {
1226
            try {
1227
//                Level sqlLogLevel = Logger.getLogger("org.hibernate.SQL").getLevel();
1228
//                Logger.getLogger("org.hibernate.SQL").setLevel(Level.TRACE);
1229

    
1230
                //references
1231
                if (name.getNomenclaturalReference()!= null && !name.getNomenclaturalReference().isPersited()){
1232
                    Reference nomRef = name.getNomenclaturalReference();
1233
                    IMatchStrategy referenceMatcher = MatchStrategyFactory.NewParsedReferenceInstance(nomRef);
1234
                    List<Reference> matchingReferences = commonService.findMatching(nomRef, referenceMatcher);
1235
                    if(matchingReferences.size() >= 1){
1236
                        Reference duplicate = findBestMatching(nomRef, matchingReferences, referenceMatcher);
1237
                        name.setNomenclaturalReference(duplicate);
1238
                    }else{
1239
                        if (nomRef.getInReference() != null){
1240
                            List<Reference> matchingInReferences = commonService.findMatching(nomRef.getInReference(), MatchStrategyFactory.NewParsedReferenceInstance(nomRef.getInReference()));
1241
                            if(matchingInReferences.size() >= 1){
1242
                                Reference duplicate = findBestMatching(nomRef, matchingInReferences, referenceMatcher);
1243
                                nomRef.setInReference(duplicate);
1244
                            }
1245
                        }
1246
                        TeamOrPersonBase<?> author = deduplicateAuthor(nomRef.getAuthorship());
1247
                        nomRef.setAuthorship(author);
1248
                    }
1249
                }
1250
                Reference nomRef = name.getNomenclaturalReference();
1251

    
1252
                //authors
1253
                IParsedMatchStrategy authorMatcher = MatchStrategyFactory.NewParsedTeamOrPersonInstance();
1254
                if (name.getCombinationAuthorship()!= null && !name.getCombinationAuthorship().isPersited()){
1255
                    //use same nom.ref. author if possible (should always be possible if nom.ref. exists)
1256
                    if (nomRef != null && nomRef.getAuthorship() != null){
1257
                        if(authorMatcher.invoke(name.getCombinationAuthorship(), nomRef.getAuthorship()).isSuccessful()){
1258
                            name.setCombinationAuthorship(nomRef.getAuthorship());
1259
                        }
1260
                    }
1261
                    name.setCombinationAuthorship(deduplicateAuthor(name.getCombinationAuthorship()));
1262
                }
1263
                if (name.getExCombinationAuthorship()!= null && !name.getExCombinationAuthorship().isPersited()){
1264
                    name.setExCombinationAuthorship(deduplicateAuthor(name.getExCombinationAuthorship()));
1265
                }
1266
                if (name.getBasionymAuthorship()!= null && !name.getBasionymAuthorship().isPersited()){
1267
                    name.setBasionymAuthorship(deduplicateAuthor(name.getBasionymAuthorship()));
1268
                }
1269
                if (name.getExBasionymAuthorship()!= null && !name.getExBasionymAuthorship().isPersited()){
1270
                    name.setExBasionymAuthorship(deduplicateAuthor(name.getExBasionymAuthorship()));
1271
                }
1272

    
1273
                //originalSpelling
1274
                if (name.getOriginalSpelling()!= null && !name.getOriginalSpelling().isPersited()){
1275
                    TaxonName origName = name.getOriginalSpelling();
1276
                    IMatchStrategy nameMatcher = MatchStrategyFactory.NewParsedOriginalSpellingInstance();
1277
                    List<TaxonName> matchingNames = commonService.findMatching(origName, nameMatcher);
1278
                    if(matchingNames.size() >= 1){
1279
                        TaxonName duplicate = findBestMatching(origName, matchingNames, nameMatcher);
1280
                        name.setOriginalSpelling(duplicate);
1281
                    }
1282
                }
1283

    
1284
//              Logger.getLogger("org.hibernate.SQL").setLevel(sqlLogLevel);
1285
            } catch (MatchException e) {
1286
                throw new RuntimeException(e);
1287
            }
1288
        }
1289
        result.setCdmEntity(name);
1290
        return result;
1291
    }
1292

    
1293
    private TeamOrPersonBase<?> deduplicateAuthor(TeamOrPersonBase<?> authorship) throws MatchException {
1294
        if (authorship == null){
1295
            return null;
1296
        }
1297
        IParsedMatchStrategy authorMatcher = MatchStrategyFactory.NewParsedTeamOrPersonInstance();
1298
        List<TeamOrPersonBase<?>> matchingAuthors = commonService.findMatching(authorship, authorMatcher);
1299
        if(matchingAuthors.size() >= 1){
1300
            TeamOrPersonBase<?> duplicate = findBestMatching(authorship, matchingAuthors, authorMatcher);
1301
            return duplicate;
1302
        }else{
1303
            if (authorship instanceof Team){
1304
                deduplicateTeam((Team)authorship);
1305
            }
1306
            return authorship;
1307
        }
1308
    }
1309

    
1310
    private void deduplicateTeam(Team team) throws MatchException {
1311
        List<Person> members = team.getTeamMembers();
1312
        IParsedMatchStrategy personMatcher = MatchStrategyFactory.NewParsedPersonInstance();
1313
        for (int i =0; i< members.size(); i++){
1314
            Person person = CdmBase.deproxy(members.get(i));
1315
            List<Person> matchingPersons = commonService.findMatching(person, personMatcher);
1316
            if (matchingPersons.size() > 0){
1317
                person = findBestMatching(person, matchingPersons, personMatcher);
1318
                members.set(i, person);
1319
            }
1320
        }
1321
    }
1322

    
1323
    private <M extends IMatchable> M findBestMatching(M matchable, List<M> matchingList,
1324
            IMatchStrategy matcher) {
1325
        // FIXME TODO resolve multiple duplications. Use first match for a start
1326
        if(matchingList.isEmpty()){
1327
            return null;
1328
        }
1329
        M bestMatching = matchingList.iterator().next();
1330
        return bestMatching;
1331
    }
1332
}
(72-72/97)