Project

General

Profile

Download (40.8 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.description.DescriptionElementSource;
61
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
62
import eu.etaxonomy.cdm.model.name.HybridRelationship;
63
import eu.etaxonomy.cdm.model.name.HybridRelationshipType;
64
import eu.etaxonomy.cdm.model.name.INonViralName;
65
import eu.etaxonomy.cdm.model.name.NameRelationship;
66
import eu.etaxonomy.cdm.model.name.NameRelationshipType;
67
import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
68
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
69
import eu.etaxonomy.cdm.model.name.Rank;
70
import eu.etaxonomy.cdm.model.name.Registration;
71
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
72
import eu.etaxonomy.cdm.model.name.TaxonName;
73
import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
74
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
75
import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
76
import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
77
import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
78
import eu.etaxonomy.cdm.persistence.dao.common.IReferencedEntityDao;
79
import eu.etaxonomy.cdm.persistence.dao.common.ITermVocabularyDao;
80
import eu.etaxonomy.cdm.persistence.dao.name.IHomotypicalGroupDao;
81
import eu.etaxonomy.cdm.persistence.dao.name.INomenclaturalStatusDao;
82
import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
83
import eu.etaxonomy.cdm.persistence.dao.name.ITypeDesignationDao;
84
import eu.etaxonomy.cdm.persistence.dto.TaxonNameParts;
85
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
86
import eu.etaxonomy.cdm.persistence.query.MatchMode;
87
import eu.etaxonomy.cdm.persistence.query.OrderHint;
88
import eu.etaxonomy.cdm.strategy.cache.TaggedText;
89
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
90
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
91

    
92

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

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

    
118
    /**
119
     * Constructor
120
     */
121
    public NameServiceImpl(){}
122

    
123
//********************* METHODS ****************************************************************//
124

    
125
    @Override
126
    @Transactional(readOnly = false)
127
    public DeleteResult delete(UUID nameUUID){
128
        NameDeletionConfigurator config = new NameDeletionConfigurator();
129
        DeleteResult result = delete(nameUUID, config);
130
        return result;
131
    }
132

    
133
    @Override
134
    public DeleteResult delete(TaxonName name){
135
        return delete(name.getUuid());
136
    }
137

    
138
    @Override
139
    @Transactional(readOnly = false)
140
    public DeleteResult delete(TaxonName name, NameDeletionConfigurator config) {
141
        DeleteResult result = new DeleteResult();
142

    
143
        if (name == null){
144
            result.setAbort();
145
            return result;
146
        }
147

    
148
        try{
149
            result = this.isDeletable(name, config);
150
        }catch(Exception e){
151
            result.addException(e);
152
            result.setError();
153
            return result;
154
        }
155
        if (result.isOk()){
156
        //remove references to this name
157
            removeNameRelationshipsByDeleteConfig(name, config);
158

    
159
           //remove name from homotypical group
160
            HomotypicalGroup homotypicalGroup = name.getHomotypicalGroup();
161
            if (homotypicalGroup != null){
162
                homotypicalGroup.removeTypifiedName(name, false);
163
            }
164

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

    
173
            try{
174
                dao.delete(name);
175
                result.addDeletedObject(name);
176
            }catch(Exception e){
177
                result.addException(e);
178
                result.setError();
179
            }
180
            return result;
181
        }
182

    
183
        return result;
184
    }
185

    
186
    @Override
187
    @Transactional(readOnly = false)
188
    public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {
189

    
190
        TaxonName name = dao.load(nameUUID);
191
        return delete(name, config);
192
    }
193

    
194
    @Override
195
    @Transactional
196
    public DeleteResult deleteTypeDesignation(TaxonName name, TypeDesignationBase typeDesignation){
197
    	if(typeDesignation != null && typeDesignation .isPersited()){
198
    		typeDesignation = HibernateProxyHelper.deproxy(referencedEntityDao.load(typeDesignation.getUuid()), TypeDesignationBase.class);
199
    	}
200

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

    
227

    
228
    @Override
229
    @Transactional(readOnly = false)
230
    public DeleteResult deleteTypeDesignation(UUID nameUuid, UUID typeDesignationUuid){
231
        TaxonName nameBase = load(nameUuid);
232
        TypeDesignationBase<?> typeDesignation = HibernateProxyHelper.deproxy(referencedEntityDao.load(typeDesignationUuid), TypeDesignationBase.class);
233
        return deleteTypeDesignation(nameBase, typeDesignation);
234
    }
235

    
236
    /**
237
     * @param name
238
     * @param typeDesignation
239
     */
240
    @Transactional
241
    private void removeSingleDesignation(TaxonName name, TypeDesignationBase typeDesignation) {
242
        name.removeTypeDesignation(typeDesignation);
243
        if (typeDesignation.getTypifiedNames().isEmpty()){
244
            typeDesignation.removeType();
245
            if (!typeDesignation.getRegistrations().isEmpty()){
246
                for(Object reg: typeDesignation.getRegistrations()){
247
                    if (reg instanceof Registration){
248
                        ((Registration)reg).removeTypeDesignation(typeDesignation);
249
                    }
250
                }
251
            }
252
            typeDesignationDao.delete(typeDesignation);
253

    
254
        }
255
    }
256

    
257

    
258

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

    
290
            }
291
        } catch (Exception e) {
292
            throw new RuntimeException(e);
293
        }
294
    }
295

    
296
    /**
297
     * @param name
298
     * @return
299
     */
300
    private Set<NameRelationship> getModifiableSet(Set<NameRelationship> relations) {
301
        Set<NameRelationship> rels = new HashSet<NameRelationship>();
302
        for (NameRelationship rel : relations){
303
            rels.add(rel);
304
        }
305
        return rels;
306
    }
307

    
308
//********************* METHODS ****************************************************************//
309

    
310

    
311
    /**
312
     * TODO candidate for harmonization
313
     * new name findByName
314
     */
315
    @Override
316
    @Deprecated
317
    public List<TaxonName> getNamesByNameCache(String nameCache){
318
        boolean includeAuthors = false;
319
        List result = dao.findByName(includeAuthors, nameCache, MatchMode.EXACT, null, null, null, null);
320
        return result;
321
    }
322

    
323
    /**
324
     * TODO candidate for harmonization
325
     * new name saveHomotypicalGroups
326
     *
327
     * findByTitle
328
     */
329
    @Override
330
    public List<TaxonName> findNamesByTitleCache(String titleCache, MatchMode matchMode, List<String> propertyPaths){
331
        List result = dao.findByTitle(titleCache, matchMode, null, null, null, propertyPaths);
332
        return result;
333
    }
334

    
335
    /**
336
     * TODO candidate for harmonization
337
     * new name saveHomotypicalGroups
338
     *
339
     * findByTitle
340
     */
341
    @Override
342
    public List<TaxonName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){
343
        List<TaxonName> result = dao.findByName(false, nameCache, matchMode, null, null, null , propertyPaths);
344
        return result;
345
    }
346

    
347
    @Override
348
    public Pager<TaxonNameParts> findTaxonNameParts(Optional<String> genusOrUninomial,
349
            Optional<String> infraGenericEpithet, Optional<String> specificEpithet,
350
            Optional<String> infraSpecificEpithet, Rank rank, Set<UUID> excludedNamesUuids,
351
            Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
352

    
353

    
354
        long count = dao.countTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraGenericEpithet, rank, excludedNamesUuids);
355

    
356
        List<TaxonNameParts> results;
357
        if(AbstractPagerImpl.hasResultsInRange(count, pageIndex, pageSize)){
358
            results = dao.findTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
359
                    rank, excludedNamesUuids,
360
                    pageSize, pageIndex, orderHints);
361
        } else {
362
            results = new ArrayList<>();
363
        }
364

    
365
        return new DefaultPagerImpl<TaxonNameParts>(pageIndex, count, pageSize, results);
366
    }
367

    
368
    /**
369
     * {@inheritDoc}
370
     */
371
    @Override
372
    public Pager<TaxonNameParts> findTaxonNameParts(TaxonNamePartsFilter filter, String namePartQueryString,
373
            Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
374

    
375
        return findTaxonNameParts(
376
                filter.uninomialQueryString(namePartQueryString),
377
                filter.infraGenericEpithet(namePartQueryString),
378
                filter.specificEpithet(namePartQueryString),
379
                filter.infraspecificEpithet(namePartQueryString),
380
                filter.getRank(),
381
                filter.getExludedNamesUuids(),
382
                pageSize, pageIndex, orderHints);
383
    }
384

    
385
    /**
386
     * TODO candidate for harmonization
387
     * new name saveHomotypicalGroups
388
     */
389
    @Override
390
    @Transactional(readOnly = false)
391
    public Map<UUID, HomotypicalGroup> saveAllHomotypicalGroups(Collection<HomotypicalGroup> homotypicalGroups){
392
        return homotypicalGroupDao.saveAll(homotypicalGroups);
393
    }
394

    
395
    /**
396
     * TODO candidate for harmonization
397
     * new name saveTypeDesignations
398
     */
399
    @Override
400
    @Transactional(readOnly = false)
401
    public Map<UUID, TypeDesignationBase> saveTypeDesignationAll(Collection<TypeDesignationBase> typeDesignationCollection){
402
        return typeDesignationDao.saveAll(typeDesignationCollection);
403
    }
404

    
405
    /**
406
     * TODO candidate for harmonization
407
     * new name saveReferencedEntities
408
     */
409
    @Override
410
    @Transactional(readOnly = false)
411
    public Map<UUID, ReferencedEntityBase> saveReferencedEntitiesAll(Collection<ReferencedEntityBase> referencedEntityCollection){
412
        return referencedEntityDao.saveAll(referencedEntityCollection);
413
    }
414

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

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

    
433
    @Override
434
    public TypeDesignationBase loadTypeDesignation(int id, List<String> propertyPaths){
435
        return typeDesignationDao.load(id, propertyPaths);
436
    }
437

    
438
    @Override
439
    public TypeDesignationBase loadTypeDesignation(UUID uuid, List<String> propertyPaths){
440
        return typeDesignationDao.load(uuid, propertyPaths);
441
    }
442

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

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

    
462

    
463

    
464
    @Override
465
    @Autowired
466
    protected void setDao(ITaxonNameDao dao) {
467
        this.dao = dao;
468
    }
469

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

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

    
479
        return new DefaultPagerImpl<HybridRelationship>(pageNumber, numberOfResults, pageSize, results);
480
    }
481

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

    
486
        Integer numberOfResults = dao.countNameRelationships(name, direction, type);
487

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

    
495

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

    
505

    
506
        Builder finalQueryBuilder = new Builder();
507
        finalQueryBuilder.setDisableCoord(false);
508
        Builder textQueryBuilder = new Builder();
509
        textQueryBuilder.setDisableCoord(false);
510

    
511
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
512
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
513

    
514
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
515
//    	luceneSearch.setSortFields(sortFields);
516

    
517
        // ---- search criteria
518
        luceneSearch.setCdmTypRestriction(clazz);
519

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

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

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

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

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

    
555
        textQueryBuilder.add(fltq, Occur.MUST);
556

    
557
        BooleanQuery textQuery = textQueryBuilder.build();
558
        finalQueryBuilder.add(textQuery, Occur.MUST);
559

    
560
        luceneSearch.setQuery(finalQueryBuilder.build());
561

    
562
        if(highlightFragments){
563
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
564
        }
565
        return luceneSearch;
566
    }
567

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

    
575
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
576
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
577

    
578
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
579
//    	luceneSearch.setSortFields(sortFields);
580

    
581
        // ---- search criteria
582
        luceneSearch.setCdmTypRestriction(clazz);
583
        FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
584

    
585
        fltq.addTerms(name, "nameCache", accuracy, 3);
586

    
587
         BooleanQuery finalQuery = new BooleanQuery(false);
588

    
589
         finalQuery.add(fltq, Occur.MUST);
590

    
591
        luceneSearch.setQuery(finalQuery);
592

    
593
        if(highlightFragments){
594
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
595
        }
596
        return luceneSearch;
597
    }
598

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

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

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

    
612
        // ---- search criteria
613
        luceneSearch.setCdmTypRestriction(clazz);
614

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

    
623
        luceneSearch.setQuery(textQueryBuilder.build());
624

    
625
        if(highlightFragments){
626
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
627
        }
628
        return luceneSearch;
629
    }
630

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

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

    
649
        // --- execute search
650
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
651

    
652

    
653
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
654
        idFieldMap.put(CdmBaseType.NONVIRALNAME, "id");
655

    
656
        // --- initialize taxa, highlight matches ....
657
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
658

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

    
663
        return searchResults;
664

    
665
    }
666

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

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

    
684
        // --- execute search
685
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
686

    
687
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
688

    
689
        // --- initialize taxa, highlight matches ....
690
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
691

    
692
        @SuppressWarnings("rawtypes")
693
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
694

    
695
        return searchResults;
696
    }
697

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

    
706
        logger.info("Name to fuzzy search for : " + name);
707

    
708
        LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);
709

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

    
714
        // --- initialize taxa, highlight matches ....
715
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
716

    
717
        @SuppressWarnings("rawtypes")
718
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
719

    
720
        return searchResults;
721
    }
722

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

    
731
        logger.info("Name to exact search for : " + name);
732

    
733
        LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);
734

    
735
        // --- execute search
736

    
737

    
738
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
739

    
740
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
741

    
742
        // --- initialize taxa, highlight matches ....
743
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
744

    
745
        @SuppressWarnings("rawtypes")
746
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
747

    
748
        return searchResults;
749
    }
750

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

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

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

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

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

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

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

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

    
796
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
797
    }
798

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

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

    
813
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
814
    }
815

    
816
    @Override
817
    public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames(Integer limit, String pattern) {
818
        return dao.getUuidAndTitleCacheOfNames(limit, pattern);
819
    }
820

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

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

    
831
         return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
832
    }
833

    
834
    @Override
835
    public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
836
        return homotypicalGroupDao.findByUuid(uuid);
837
    }
838

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

    
848

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

    
856

    
857
    public DeleteResult isDeletable(TaxonName name, DeleteConfiguratorBase config){
858
        DeleteResult result = new DeleteResult();
859

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

    
869
        if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
870
            HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
871

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

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

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

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

    
943
        //TODO inline references
944

    
945

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

    
958
    }
959

    
960

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

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

    
976
    }
977

    
978
    @Override
979
    public List<HashMap<String,String>> getNameRecords(){
980
		return dao.getNameRecords();
981

    
982
    }
983

    
984
}
(79-79/103)