Project

General

Profile

Download (40.7 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.Collection;
15
import java.util.HashMap;
16
import java.util.HashSet;
17
import java.util.List;
18
import java.util.Map;
19
import java.util.Optional;
20
import java.util.Set;
21
import java.util.UUID;
22

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

    
37
import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
38
import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
39
import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
40
import eu.etaxonomy.cdm.api.service.pager.Pager;
41
import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
42
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
43
import eu.etaxonomy.cdm.api.service.search.DocumentSearchResult;
44
import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
45
import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
46
import eu.etaxonomy.cdm.api.service.search.LuceneParseException;
47
import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
48
import eu.etaxonomy.cdm.api.service.search.QueryFactory;
49
import eu.etaxonomy.cdm.api.service.search.SearchResult;
50
import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
51
import eu.etaxonomy.cdm.api.utility.TaxonNamePartsFilter;
52
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
53
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
54
import eu.etaxonomy.cdm.model.CdmBaseType;
55
import eu.etaxonomy.cdm.model.common.CdmBase;
56
import eu.etaxonomy.cdm.model.common.Language;
57
import eu.etaxonomy.cdm.model.common.ReferencedEntityBase;
58
import eu.etaxonomy.cdm.model.common.RelationshipBase;
59
import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
60
import eu.etaxonomy.cdm.model.common.SourcedEntityBase;
61
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
62
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
63
import eu.etaxonomy.cdm.model.name.HybridRelationship;
64
import eu.etaxonomy.cdm.model.name.HybridRelationshipType;
65
import eu.etaxonomy.cdm.model.name.INonViralName;
66
import eu.etaxonomy.cdm.model.name.NameRelationship;
67
import eu.etaxonomy.cdm.model.name.NameRelationshipType;
68
import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
69
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
70
import eu.etaxonomy.cdm.model.name.Rank;
71
import eu.etaxonomy.cdm.model.name.Registration;
72
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
73
import eu.etaxonomy.cdm.model.name.TaxonName;
74
import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
75
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
76
import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
77
import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
78
import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
79
import eu.etaxonomy.cdm.persistence.dao.common.IReferencedEntityDao;
80
import eu.etaxonomy.cdm.persistence.dao.common.ISourcedEntityDao;
81
import eu.etaxonomy.cdm.persistence.dao.common.ITermVocabularyDao;
82
import eu.etaxonomy.cdm.persistence.dao.name.IHomotypicalGroupDao;
83
import eu.etaxonomy.cdm.persistence.dao.name.INomenclaturalStatusDao;
84
import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
85
import eu.etaxonomy.cdm.persistence.dao.name.ITypeDesignationDao;
86
import eu.etaxonomy.cdm.persistence.dto.TaxonNameParts;
87
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
88
import eu.etaxonomy.cdm.persistence.query.MatchMode;
89
import eu.etaxonomy.cdm.persistence.query.OrderHint;
90
import eu.etaxonomy.cdm.strategy.cache.TaggedText;
91
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
92
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
93

    
94

    
95
@Service
96
@Transactional(readOnly = true)
97
public class NameServiceImpl
98
          extends IdentifiableServiceBase<TaxonName,ITaxonNameDao>
99
          implements INameService {
100
    static private final Logger logger = Logger.getLogger(NameServiceImpl.class);
101

    
102
    @Autowired
103
    protected ITermVocabularyDao vocabularyDao;
104
    @Autowired
105
    protected IOrderedTermVocabularyDao orderedVocabularyDao;
106
    @Autowired
107
    @Qualifier("refEntDao")
108
    protected IReferencedEntityDao<ReferencedEntityBase> referencedEntityDao;
109
    @Autowired
110
    @Qualifier("sourcedEntityDao")
111
    protected ISourcedEntityDao<SourcedEntityBase<?>> sourcedEntityDao;
112
    @Autowired
113
    private INomenclaturalStatusDao nomStatusDao;
114
    @Autowired
115
    private ITypeDesignationDao typeDesignationDao;
116
    @Autowired
117
    private IHomotypicalGroupDao homotypicalGroupDao;
118
    @Autowired
119
    private ICdmGenericDao genericDao;
120
    @Autowired
121
    private ILuceneIndexToolProvider luceneIndexToolProvider;
122

    
123
    /**
124
     * Constructor
125
     */
126
    public NameServiceImpl(){}
127

    
128
//********************* METHODS ****************************************************************//
129

    
130
    @Override
131
    @Transactional(readOnly = false)
132
    public DeleteResult delete(UUID nameUUID){
133
        NameDeletionConfigurator config = new NameDeletionConfigurator();
134
        DeleteResult result = delete(nameUUID, config);
135
        return result;
136
    }
137

    
138
    @Override
139
    public DeleteResult delete(TaxonName name){
140
        return delete(name.getUuid());
141
    }
142

    
143
    @Override
144
    @Transactional(readOnly = false)
145
    public DeleteResult delete(TaxonName name, NameDeletionConfigurator config) {
146
        DeleteResult result = new DeleteResult();
147

    
148
        if (name == null){
149
            result.setAbort();
150
            return result;
151
        }
152

    
153
        try{
154
            result = this.isDeletable(name, config);
155
        }catch(Exception e){
156
            result.addException(e);
157
            result.setError();
158
            return result;
159
        }
160
        if (result.isOk()){
161
        //remove references to this name
162
            removeNameRelationshipsByDeleteConfig(name, config);
163

    
164
           //remove name from homotypical group
165
            HomotypicalGroup homotypicalGroup = name.getHomotypicalGroup();
166
            if (homotypicalGroup != null){
167
                homotypicalGroup.removeTypifiedName(name, false);
168
            }
169

    
170
             //all type designation relationships are removed as they belong to the name
171
            deleteTypeDesignation(name, null);
172
    //      //type designations
173
    //      if (! name.getTypeDesignations().isEmpty()){
174
    //          String message = "Name can't be deleted as it has types. Remove types prior to deletion.";
175
    //          throw new ReferrencedObjectUndeletableException(message);
176
    //      }
177

    
178
            try{
179
                dao.delete(name);
180
                result.addDeletedObject(name);
181
            }catch(Exception e){
182
                result.addException(e);
183
                result.setError();
184
            }
185
            return result;
186
        }
187

    
188
        return result;
189
    }
190

    
191
    @Override
192
    @Transactional(readOnly = false)
193
    public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {
194

    
195
        TaxonName name = dao.load(nameUUID);
196
        return delete(name, config);
197
    }
198

    
199
    @Override
200
    @Transactional
201
    public DeleteResult deleteTypeDesignation(TaxonName name, TypeDesignationBase<?> typeDesignation){
202
    	if(typeDesignation != null && typeDesignation .isPersited()){
203
    		typeDesignation = HibernateProxyHelper.deproxy(sourcedEntityDao.load(typeDesignation.getUuid()), TypeDesignationBase.class);
204
    	}
205

    
206
        DeleteResult result = new DeleteResult();
207
        if (name == null && typeDesignation == null){
208
            result.setError();
209
            return result;
210
        }else if (name != null && typeDesignation != null){
211
            removeSingleDesignation(name, typeDesignation);
212
        }else if (name != null){
213
            @SuppressWarnings("rawtypes")
214
            Set<TypeDesignationBase<?>> designationSet = new HashSet(name.getTypeDesignations());
215
            for (TypeDesignationBase<?> desig : designationSet){
216
                desig = CdmBase.deproxy(desig);
217
                removeSingleDesignation(name, desig);
218
            }
219
        }else if (typeDesignation != null){
220
            @SuppressWarnings("unchecked")
221
            Set<TaxonName> nameSet = new HashSet(typeDesignation.getTypifiedNames());
222
            for (TaxonName singleName : nameSet){
223
                singleName = CdmBase.deproxy(singleName);
224
                removeSingleDesignation(singleName, typeDesignation);
225
            }
226
        }
227
        result.addDeletedObject(typeDesignation);
228
        result.addUpdatedObject(name);
229
        return result;
230
    }
231

    
232

    
233
    @Override
234
    @Transactional(readOnly = false)
235
    public DeleteResult deleteTypeDesignation(UUID nameUuid, UUID typeDesignationUuid){
236
        TaxonName nameBase = load(nameUuid);
237
        TypeDesignationBase<?> typeDesignation = HibernateProxyHelper.deproxy(sourcedEntityDao.load(typeDesignationUuid), TypeDesignationBase.class);
238
        return deleteTypeDesignation(nameBase, typeDesignation);
239
    }
240

    
241
    /**
242
     * @param name
243
     * @param typeDesignation
244
     */
245
    @Transactional
246
    private void removeSingleDesignation(TaxonName name, TypeDesignationBase<?> typeDesignation) {
247

    
248
        name.removeTypeDesignation(typeDesignation);
249
        if (typeDesignation.getTypifiedNames().isEmpty()){
250
            typeDesignation.removeType();
251
            if (!typeDesignation.getRegistrations().isEmpty()){
252
                for(Object reg: typeDesignation.getRegistrations()){
253
                    if (reg instanceof Registration){
254
                        ((Registration)reg).removeTypeDesignation(typeDesignation);
255
                    }
256
                }
257
            }
258

    
259
            typeDesignationDao.delete(typeDesignation);
260

    
261
        }
262
    }
263

    
264

    
265

    
266
    /**
267
     * @param name
268
     * @param config
269
     */
270
    private void removeNameRelationshipsByDeleteConfig(TaxonName name, NameDeletionConfigurator config) {
271
        try {
272
            if (config.isRemoveAllNameRelationships()){
273
                Set<NameRelationship> rels = getModifiableSet(name.getNameRelations());
274
                for (NameRelationship rel : rels){
275
                    name.removeNameRelationship(rel);
276
                }
277
            }else{
278
                //relations to this name
279
                Set<NameRelationship> rels = getModifiableSet(name.getRelationsToThisName());
280
                for (NameRelationship rel : rels){
281
                    if (config.isIgnoreHasBasionym() && NameRelationshipType.BASIONYM().equals(rel.getType() )){
282
                            name.removeNameRelationship(rel);
283
                    }else if (config.isIgnoreHasReplacedSynonym() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
284
                        name.removeNameRelationship(rel);
285
                    }
286
                }
287
                //relations from this name
288
                rels = getModifiableSet(name.getRelationsFromThisName());
289
                for (NameRelationship rel : rels){
290
                    if (config.isIgnoreIsBasionymFor() && NameRelationshipType.BASIONYM().equals(rel.getType())  ){
291
                        name.removeNameRelationship(rel);
292
                    }else if (config.isIgnoreIsReplacedSynonymFor() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
293
                        name.removeNameRelationship(rel);
294
                    }
295
                }
296

    
297
            }
298
        } catch (Exception e) {
299
            throw new RuntimeException(e);
300
        }
301
    }
302

    
303
    /**
304
     * @param name
305
     * @return
306
     */
307
    private Set<NameRelationship> getModifiableSet(Set<NameRelationship> relations) {
308
        Set<NameRelationship> rels = new HashSet<NameRelationship>();
309
        for (NameRelationship rel : relations){
310
            rels.add(rel);
311
        }
312
        return rels;
313
    }
314

    
315
//********************* METHODS ****************************************************************//
316

    
317

    
318
    /**
319
     * TODO candidate for harmonization
320
     * new name findByName
321
     */
322
    @Override
323
    @Deprecated
324
    public List<TaxonName> getNamesByNameCache(String nameCache){
325
        boolean includeAuthors = false;
326
        List result = dao.findByName(includeAuthors, nameCache, MatchMode.EXACT, null, null, null, null);
327
        return result;
328
    }
329

    
330
    /**
331
     * TODO candidate for harmonization
332
     * new name saveHomotypicalGroups
333
     *
334
     * findByTitle
335
     */
336
    @Override
337
    public List<TaxonName> findNamesByTitleCache(String titleCache, MatchMode matchMode, List<String> propertyPaths){
338
        List result = dao.findByTitle(titleCache, matchMode, null, null, null, propertyPaths);
339
        return result;
340
    }
341

    
342
    /**
343
     * TODO candidate for harmonization
344
     * new name saveHomotypicalGroups
345
     *
346
     * findByTitle
347
     */
348
    @Override
349
    public List<TaxonName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){
350
        List<TaxonName> result = dao.findByName(false, nameCache, matchMode, null, null, null , propertyPaths);
351
        return result;
352
    }
353

    
354
    @Override
355
    public Pager<TaxonNameParts> findTaxonNameParts(Optional<String> genusOrUninomial,
356
            Optional<String> infraGenericEpithet, Optional<String> specificEpithet,
357
            Optional<String> infraSpecificEpithet, Rank rank, Set<UUID> excludedNamesUuids,
358
            Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
359

    
360

    
361
        long count = dao.countTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraGenericEpithet, rank, excludedNamesUuids);
362

    
363
        List<TaxonNameParts> results;
364
        if(AbstractPagerImpl.hasResultsInRange(count, pageIndex, pageSize)){
365
            results = dao.findTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
366
                    rank, excludedNamesUuids,
367
                    pageSize, pageIndex, orderHints);
368
        } else {
369
            results = new ArrayList<>();
370
        }
371

    
372
        return new DefaultPagerImpl<TaxonNameParts>(pageIndex, count, pageSize, results);
373
    }
374

    
375
    /**
376
     * {@inheritDoc}
377
     */
378
    @Override
379
    public Pager<TaxonNameParts> findTaxonNameParts(TaxonNamePartsFilter filter, String namePartQueryString,
380
            Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
381

    
382
        return findTaxonNameParts(
383
                filter.uninomialQueryString(namePartQueryString),
384
                filter.infraGenericEpithet(namePartQueryString),
385
                filter.specificEpithet(namePartQueryString),
386
                filter.infraspecificEpithet(namePartQueryString),
387
                filter.getRank(),
388
                filter.getExludedNamesUuids(),
389
                pageSize, pageIndex, orderHints);
390
    }
391

    
392
    /**
393
     * TODO candidate for harmonization
394
     * new name saveHomotypicalGroups
395
     */
396
    @Override
397
    @Transactional(readOnly = false)
398
    public Map<UUID, HomotypicalGroup> saveAllHomotypicalGroups(Collection<HomotypicalGroup> homotypicalGroups){
399
        return homotypicalGroupDao.saveAll(homotypicalGroups);
400
    }
401

    
402
    /**
403
     * TODO candidate for harmonization
404
     * new name saveTypeDesignations
405
     */
406
    @Override
407
    @Transactional(readOnly = false)
408
    public Map<UUID, TypeDesignationBase<?>> saveTypeDesignationAll(Collection<TypeDesignationBase<?>> typeDesignationCollection){
409
        return typeDesignationDao.saveAll(typeDesignationCollection);
410
    }
411

    
412
    /**
413
     * TODO candidate for harmonization
414
     * new name getNomenclaturalStatus
415
     */
416
    @Override
417
    public List<NomenclaturalStatus> getAllNomenclaturalStatus(int limit, int start){
418
        return nomStatusDao.list(limit, start);
419
    }
420

    
421
    /**
422
     * TODO candidate for harmonization
423
     * new name getTypeDesignations
424
     */
425
    @Override
426
    public List<TypeDesignationBase<?>> getAllTypeDesignations(int limit, int start){
427
        return typeDesignationDao.getAllTypeDesignations(limit, start);
428
    }
429

    
430
    @Override
431
    public TypeDesignationBase<?> loadTypeDesignation(int id, List<String> propertyPaths){
432
        return typeDesignationDao.load(id, propertyPaths);
433
    }
434

    
435
    @Override
436
    public TypeDesignationBase<?> loadTypeDesignation(UUID uuid, List<String> propertyPaths){
437
        return typeDesignationDao.load(uuid, propertyPaths);
438
    }
439

    
440
    /**
441
     * FIXME Candidate for harmonization
442
     * homotypicalGroupService.list
443
     */
444
    @Override
445
    public List<HomotypicalGroup> getAllHomotypicalGroups(int limit, int start){
446
        return homotypicalGroupDao.list(limit, start);
447
    }
448

    
449
    /**
450
     * FIXME Candidate for harmonization
451
     * remove
452
     */
453
    @Override
454
    @Deprecated
455
    public List<RelationshipBase> getAllRelationships(int limit, int start){
456
        return dao.getAllRelationships(limit, start);
457
    }
458

    
459

    
460

    
461
    @Override
462
    @Autowired
463
    protected void setDao(ITaxonNameDao dao) {
464
        this.dao = dao;
465
    }
466

    
467
    @Override
468
    public Pager<HybridRelationship> getHybridNames(INonViralName name,	HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
469
        Integer numberOfResults = dao.countHybridNames(name, type);
470

    
471
        List<HybridRelationship> results = new ArrayList<HybridRelationship>();
472
        if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
473
            results = dao.getHybridNames(name, type, pageSize, pageNumber,orderHints,propertyPaths);
474
        }
475

    
476
        return new DefaultPagerImpl<HybridRelationship>(pageNumber, numberOfResults, pageSize, results);
477
    }
478

    
479
    @Override
480
    public List<NameRelationship> listNameRelationships(TaxonName name,	Direction direction, NameRelationshipType type, Integer pageSize,
481
            Integer pageNumber, List<OrderHint> orderHints,	List<String> propertyPaths) {
482

    
483
        Integer numberOfResults = dao.countNameRelationships(name, direction, type);
484

    
485
        List<NameRelationship> results = new ArrayList<NameRelationship>();
486
        if (AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
487
            results = dao.getNameRelationships(name, direction, type, pageSize,	pageNumber, orderHints, propertyPaths);
488
        }
489
        return results;
490
    }
491

    
492

    
493
    protected LuceneSearch prepareFindByFuzzyNameSearch(Class<? extends CdmBase> clazz,
494
            INonViralName nvn,
495
            float accuracy,
496
            int maxNoOfResults,
497
            List<Language> languages,
498
            boolean highlightFragments) {
499
        String similarity = Float.toString(accuracy);
500
        String searchSuffix = "~" + similarity;
501

    
502

    
503
        Builder finalQueryBuilder = new Builder();
504
        finalQueryBuilder.setDisableCoord(false);
505
        Builder textQueryBuilder = new Builder();
506
        textQueryBuilder.setDisableCoord(false);
507

    
508
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
509
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
510

    
511
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
512
//    	luceneSearch.setSortFields(sortFields);
513

    
514
        // ---- search criteria
515
        luceneSearch.setCdmTypRestriction(clazz);
516

    
517
        FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
518
        if(nvn.getGenusOrUninomial() != null && !nvn.getGenusOrUninomial().equals("")) {
519
            fltq.addTerms(nvn.getGenusOrUninomial().toLowerCase(), "genusOrUninomial", accuracy, 3);
520
        } else {
521
            //textQuery.add(new RegexQuery (new Term ("genusOrUninomial", "^[a-zA-Z]*")), Occur.MUST_NOT);
522
            textQueryBuilder.add(queryFactory.newTermQuery("genusOrUninomial", "_null_", false), Occur.MUST);
523
        }
524

    
525
        if(nvn.getInfraGenericEpithet() != null && !nvn.getInfraGenericEpithet().equals("")){
526
            fltq.addTerms(nvn.getInfraGenericEpithet().toLowerCase(), "infraGenericEpithet", accuracy, 3);
527
        } else {
528
            //textQuery.add(new RegexQuery (new Term ("infraGenericEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
529
            textQueryBuilder.add(queryFactory.newTermQuery("infraGenericEpithet", "_null_", false), Occur.MUST);
530
        }
531

    
532
        if(nvn.getSpecificEpithet() != null && !nvn.getSpecificEpithet().equals("")){
533
            fltq.addTerms(nvn.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy, 3);
534
        } else {
535
            //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
536
            textQueryBuilder.add(queryFactory.newTermQuery("specificEpithet", "_null_", false), Occur.MUST);
537
        }
538

    
539
        if(nvn.getInfraSpecificEpithet() != null && !nvn.getInfraSpecificEpithet().equals("")){
540
            fltq.addTerms(nvn.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy, 3);
541
        } else {
542
            //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
543
            textQueryBuilder.add(queryFactory.newTermQuery("infraSpecificEpithet", "_null_", false), Occur.MUST);
544
        }
545

    
546
        if(nvn.getAuthorshipCache() != null && !nvn.getAuthorshipCache().equals("")){
547
            fltq.addTerms(nvn.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy, 3);
548
        } else {
549
            //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
550
        }
551

    
552
        textQueryBuilder.add(fltq, Occur.MUST);
553

    
554
        BooleanQuery textQuery = textQueryBuilder.build();
555
        finalQueryBuilder.add(textQuery, Occur.MUST);
556

    
557
        luceneSearch.setQuery(finalQueryBuilder.build());
558

    
559
        if(highlightFragments){
560
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
561
        }
562
        return luceneSearch;
563
    }
564

    
565
    protected LuceneSearch prepareFindByFuzzyNameCacheSearch(Class<? extends CdmBase> clazz,
566
            String name,
567
            float accuracy,
568
            int maxNoOfResults,
569
            List<Language> languages,
570
            boolean highlightFragments) {
571

    
572
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
573
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
574

    
575
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
576
//    	luceneSearch.setSortFields(sortFields);
577

    
578
        // ---- search criteria
579
        luceneSearch.setCdmTypRestriction(clazz);
580
        FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
581

    
582
        fltq.addTerms(name, "nameCache", accuracy, 3);
583

    
584
         BooleanQuery finalQuery = new BooleanQuery(false);
585

    
586
         finalQuery.add(fltq, Occur.MUST);
587

    
588
        luceneSearch.setQuery(finalQuery);
589

    
590
        if(highlightFragments){
591
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
592
        }
593
        return luceneSearch;
594
    }
595

    
596
    protected LuceneSearch prepareFindByExactNameSearch(Class<? extends CdmBase> clazz,
597
            String name,
598
            boolean wildcard,
599
            List<Language> languages,
600
            boolean highlightFragments) {
601
        Builder textQueryBuilder = new Builder();
602

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

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

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

    
612
        if(name != null && !name.equals("")) {
613
            if(wildcard) {
614
                textQueryBuilder.add(new WildcardQuery(new Term("nameCache", name + "*")), Occur.MUST);
615
            } else {
616
                textQueryBuilder.add(queryFactory.newTermQuery("nameCache", name, false), Occur.MUST);
617
            }
618
        }
619

    
620
        luceneSearch.setQuery(textQueryBuilder.build());
621

    
622
        if(highlightFragments){
623
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
624
        }
625
        return luceneSearch;
626
    }
627

    
628
    @Override
629
    public List<SearchResult<TaxonName>> findByNameFuzzySearch(
630
            String name,
631
            float accuracy,
632
            List<Language> languages,
633
            boolean highlightFragments,
634
            List<String> propertyPaths,
635
            int maxNoOfResults) throws IOException, LuceneParseException {
636

    
637
        logger.info("Name to fuzzy search for : " + name);
638
        // parse the input name
639
        NonViralNameParserImpl parser = new NonViralNameParserImpl();
640
        INonViralName nvn = parser.parseFullName(name);
641
        if(name != null && !name.equals("") && nvn == null) {
642
            throw new LuceneParseException("Could not parse name " + name);
643
        }
644
        LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
645

    
646
        // --- execute search
647
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
648

    
649

    
650
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
651
        idFieldMap.put(CdmBaseType.NONVIRALNAME, "id");
652

    
653
        // --- initialize taxa, highlight matches ....
654
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
655

    
656
        @SuppressWarnings("rawtypes")
657
        List<SearchResult<TaxonName>> searchResults = searchResultBuilder.createResultSet(
658
                topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
659

    
660
        return searchResults;
661

    
662
    }
663

    
664
    @Override
665
    public List<DocumentSearchResult> findByNameFuzzySearch(
666
            String name,
667
            float accuracy,
668
            List<Language> languages,
669
            boolean highlightFragments,
670
            int maxNoOfResults) throws IOException, LuceneParseException {
671

    
672
        logger.info("Name to fuzzy search for : " + name);
673
        // parse the input name
674
        NonViralNameParserImpl parser = new NonViralNameParserImpl();
675
        INonViralName nvn = parser.parseFullName(name);
676
        if(name != null && !name.equals("") && nvn == null) {
677
            throw new LuceneParseException("Could not parse name " + name);
678
        }
679
        LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
680

    
681
        // --- execute search
682
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
683

    
684
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
685

    
686
        // --- initialize taxa, highlight matches ....
687
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
688

    
689
        @SuppressWarnings("rawtypes")
690
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
691

    
692
        return searchResults;
693
    }
694

    
695
    @Override
696
    public List<DocumentSearchResult> findByFuzzyNameCacheSearch(
697
            String name,
698
            float accuracy,
699
            List<Language> languages,
700
            boolean highlightFragments,
701
            int maxNoOfResults) throws IOException, LuceneParseException {
702

    
703
        logger.info("Name to fuzzy search for : " + name);
704

    
705
        LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);
706

    
707
        // --- execute search
708
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
709
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
710

    
711
        // --- initialize taxa, highlight matches ....
712
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
713

    
714
        @SuppressWarnings("rawtypes")
715
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
716

    
717
        return searchResults;
718
    }
719

    
720
    @Override
721
    public List<DocumentSearchResult> findByNameExactSearch(
722
            String name,
723
            boolean wildcard,
724
            List<Language> languages,
725
            boolean highlightFragments,
726
            int maxNoOfResults) throws IOException, LuceneParseException {
727

    
728
        logger.info("Name to exact search for : " + name);
729

    
730
        LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);
731

    
732
        // --- execute search
733

    
734

    
735
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
736

    
737
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
738

    
739
        // --- initialize taxa, highlight matches ....
740
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
741

    
742
        @SuppressWarnings("rawtypes")
743
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
744

    
745
        return searchResults;
746
    }
747

    
748
    @Override
749
    public Pager<NameRelationship> pageNameRelationships(TaxonName name, Direction direction, NameRelationshipType type, Integer pageSize,
750
            Integer pageNumber, List<OrderHint> orderHints,	List<String> propertyPaths) {
751
        List<NameRelationship> results = listNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
752
        return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
753
    }
754

    
755
    @Override
756
    public List<NameRelationship> listFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
757
        return listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
758
    }
759

    
760
    @Override
761
    public Pager<NameRelationship> pageFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
762
        List<NameRelationship> results = listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
763
        return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
764
    }
765

    
766
    @Override
767
    public List<NameRelationship> listToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
768
        return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
769
    }
770

    
771
    @Override
772
    public Pager<NameRelationship> pageToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
773
        List<NameRelationship> results = listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
774
        return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
775
    }
776

    
777
    @Override
778
    public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
779
            Integer pageSize, Integer pageNumber) {
780
        return getTypeDesignations(name, status, pageSize, pageNumber, null);
781
    }
782

    
783
    @Override
784
    public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
785
                Integer pageSize, Integer pageNumber, List<String> propertyPaths){
786
        long numberOfResults = dao.countTypeDesignations(name, status);
787

    
788
        List<TypeDesignationBase> results = new ArrayList<>();
789
        if(AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)) {
790
            results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);
791
        }
792

    
793
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
794
    }
795

    
796
    /**
797
     * FIXME Candidate for harmonization
798
     * rename search
799
     */
800
    @Override
801
    public Pager<TaxonName> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,	Integer pageNumber, List<OrderHint> orderHints,
802
            List<String> propertyPaths) {
803
        long numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
804

    
805
        List<TaxonName> results = new ArrayList<>();
806
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
807
            results = dao.searchNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber, orderHints, propertyPaths);
808
        }
809

    
810
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
811
    }
812

    
813
    @Override
814
    public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames(Integer limit, String pattern) {
815
        return dao.getUuidAndTitleCacheOfNames(limit, pattern);
816
    }
817

    
818
    @Override
819
    public Pager<TaxonName> findByName(Class<TaxonName> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria,
820
            Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
821
         Long numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
822

    
823
         List<TaxonName> results = new ArrayList<>();
824
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
825
                results = dao.findByName(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
826
         }
827

    
828
         return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
829
    }
830

    
831
    @Override
832
    public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
833
        return homotypicalGroupDao.findByUuid(uuid);
834
    }
835

    
836
    @Override
837
    @Transactional(readOnly = false)
838
    public void updateCaches(Class<? extends TaxonName> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonName> cacheStrategy, IProgressMonitor monitor) {
839
        if (clazz == null){
840
            clazz = TaxonName.class;
841
        }
842
        super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
843
    }
844

    
845

    
846
    @Override
847
    public List<TaggedText> getTaggedName(UUID uuid) {
848
        TaxonName taxonName = dao.load(uuid);
849
        List<TaggedText> taggedName = taxonName.getTaggedName();
850
        return taggedName;
851
    }
852

    
853

    
854
    public DeleteResult isDeletable(TaxonName name, DeleteConfiguratorBase config){
855
        DeleteResult result = new DeleteResult();
856

    
857
        NameDeletionConfigurator nameConfig = null;
858
        if (config instanceof NameDeletionConfigurator){
859
            nameConfig = (NameDeletionConfigurator) config;
860
        }else{
861
             result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
862
             result.setError();
863
             return result;
864
        }
865

    
866
        if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
867
            HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
868

    
869
            if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
870
                result.addException(new Exception( "Name can't be deleted as it is a basionym."));
871
                result.setAbort();
872
            }
873
            if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){
874
                result.addException(new Exception( "Name can't be deleted as it has a basionym."));
875
                result.setAbort();
876
            }
877
            Set<NameRelationship> relationships = name.getNameRelations();
878
            for (NameRelationship rel: relationships){
879
                if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
880
                    result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
881
                    result.setAbort();
882
                    break;
883
                }
884
            }
885
        }
886

    
887
        //concepts
888
        if (! name.getTaxonBases().isEmpty()){
889
            result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
890
            result.setAbort();
891
        }
892

    
893
        //hybrid relationships
894
        if (name.isNonViral()){
895
            INonViralName nvn = name;
896
            Set<HybridRelationship> parentHybridRelations = nvn.getHybridParentRelations();
897
            //Hibernate.initialize(parentHybridRelations);
898
            if (! parentHybridRelations.isEmpty()){
899
                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."));
900
                result.setAbort();
901
            }
902
        }
903
        Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);
904
        for (CdmBase referencingObject : referencingObjects){
905
            //DerivedUnit?.storedUnder
906
            if (referencingObject.isInstanceOf(DerivedUnit.class)){
907
                String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
908
                message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());
909
                result.addException(new ReferencedObjectUndeletableException(message));
910
                result.addRelatedObject(referencingObject);
911
                result.setAbort();
912
            }
913
            //DescriptionElementSource#nameUsedInSource
914
            else if (referencingObject.isInstanceOf(DescriptionElementSource.class)){
915
                String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
916
                result.addException(new ReferencedObjectUndeletableException(message));
917
                result.addRelatedObject(referencingObject);
918
                result.setAbort();
919
            }
920
            //NameTypeDesignation#typeName
921
            else if (referencingObject.isInstanceOf(NameTypeDesignation.class)){
922
                NameTypeDesignation typeDesignation = HibernateProxyHelper.deproxy(referencingObject, NameTypeDesignation.class);
923

    
924
                if (typeDesignation.getTypeName().equals(name)){
925
                    String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
926
                    result.addException(new ReferencedObjectUndeletableException(message));
927
                    result.addRelatedObject(referencingObject);
928
                    result.setAbort();
929
                }
930
            }
931
            //DeterminationEvent#taxonName
932
            else if (referencingObject.isInstanceOf(DeterminationEvent.class)){
933
                String message = "Name can't be deleted as it is used as a determination event";
934
                result.addException(new ReferencedObjectUndeletableException(message));
935
                result.addRelatedObject(referencingObject);
936
                result.setAbort();
937
        }
938
        }
939

    
940
        //TODO inline references
941

    
942

    
943
        if (!nameConfig.isIgnoreIsReplacedSynonymFor() && name.isReplacedSynonym()){
944
            String message = "Name can't be deleted as it is a replaced synonym.";
945
            result.addException(new Exception(message));
946
            result.setAbort();
947
        }
948
        if (!nameConfig.isIgnoreHasReplacedSynonym() && (name.getReplacedSynonyms().size()>0)){
949
            String message = "Name can't be deleted as it has a replaced synonym.";
950
            result.addException(new Exception(message));
951
            result.setAbort();
952
        }
953
        return result;
954

    
955
    }
956

    
957

    
958
    @Override
959
    public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config){
960
        TaxonName name = this.load(nameUUID);
961
        return isDeletable(name, config);
962
    }
963

    
964
    @Override
965
    @Transactional(readOnly = true)
966
    public UpdateResult setAsGroupsBasionym(UUID nameUuid) {
967
        TaxonName name = dao.load(nameUuid);
968
        UpdateResult result = new UpdateResult();
969
        name.setAsGroupsBasionym();
970
        result.addUpdatedObject(name);
971
        return result;
972

    
973
    }
974

    
975
    @Override
976
    public List<HashMap<String,String>> getNameRecords(){
977
		return dao.getNameRecords();
978

    
979
    }
980

    
981
}
(79-79/103)