Project

General

Profile

Download (39.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.Set;
20
import java.util.UUID;
21

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

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

    
89

    
90
@Service
91
@Transactional(readOnly = true)
92
public class NameServiceImpl extends IdentifiableServiceBase<TaxonName,ITaxonNameDao> implements INameService {
93
    static private final Logger logger = Logger.getLogger(NameServiceImpl.class);
94

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

    
113
    /**
114
     * Constructor
115
     */
116
    public NameServiceImpl(){
117
        if (logger.isDebugEnabled()) { logger.debug("Load NameService Bean"); }
118
    }
119

    
120
//********************* METHODS ****************************************************************//
121

    
122
    /* (non-Javadoc)
123
     * @see eu.etaxonomy.cdm.api.service.ServiceBase#delete(eu.etaxonomy.cdm.model.common.CdmBase)
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

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

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

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

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

    
174

    
175
            try{
176
                UUID nameUuid = dao.delete(name);
177
                result.addDeletedObject(name);
178
            }catch(Exception e){
179
                result.addException(e);
180
                result.setError();
181
            }
182
            return result;
183
        }
184

    
185
        return result;
186
    }
187

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

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

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

    
203
        DeleteResult result = new DeleteResult();
204
        if (name == null && typeDesignation == null){
205
            result.setError();
206
            return result;
207
        }else if (name != null && typeDesignation != null){
208
            removeSingleDesignation(name, typeDesignation);
209
        }else if (name != null){
210
            Set<TypeDesignationBase> designationSet = new HashSet<TypeDesignationBase>(name.getTypeDesignations());
211
            for (Object o : designationSet){
212
                TypeDesignationBase desig = CdmBase.deproxy(o, TypeDesignationBase.class);
213
                removeSingleDesignation(name, desig);
214
            }
215
        }else if (typeDesignation != null){
216
            Set<TaxonName> nameSet = new HashSet<TaxonName>(typeDesignation.getTypifiedNames());
217
            for (Object o : nameSet){
218
                TaxonName singleName = CdmBase.deproxy(o, TaxonName.class);
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
    /**
325
     * TODO candidate for harmonization
326
     * new name saveHomotypicalGroups
327
     *
328
     * findByTitle
329
     */
330
    @Override
331
    public List<TaxonName> findNamesByTitleCache(String titleCache, MatchMode matchMode, List<String> propertyPaths){
332
        List result = dao.findByTitle(titleCache, matchMode, null, null, null ,propertyPaths);
333
        return result;
334
    }
335

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

    
348
    /**
349
     * TODO candidate for harmonization
350
     */
351
    @Override
352
    public List getNamesByName(String name, CdmBase sessionObject){
353
        return super.findCdmObjectsByTitle(name, sessionObject);
354
    }
355

    
356
    /**
357
     * TODO candidate for harmonization
358
     * new name saveHomotypicalGroups
359
     */
360
    @Override
361
    @Transactional(readOnly = false)
362
    public Map<UUID, HomotypicalGroup> saveAllHomotypicalGroups(Collection<HomotypicalGroup> homotypicalGroups){
363
        return homotypicalGroupDao.saveAll(homotypicalGroups);
364
    }
365

    
366
    /**
367
     * TODO candidate for harmonization
368
     * new name saveTypeDesignations
369
     */
370
    @Override
371
    @Transactional(readOnly = false)
372
    public Map<UUID, TypeDesignationBase> saveTypeDesignationAll(Collection<TypeDesignationBase> typeDesignationCollection){
373
        return typeDesignationDao.saveAll(typeDesignationCollection);
374
    }
375

    
376
    /**
377
     * TODO candidate for harmonization
378
     * new name saveReferencedEntities
379
     */
380
    @Override
381
    @Transactional(readOnly = false)
382
    public Map<UUID, ReferencedEntityBase> saveReferencedEntitiesAll(Collection<ReferencedEntityBase> referencedEntityCollection){
383
        return referencedEntityDao.saveAll(referencedEntityCollection);
384
    }
385

    
386
    /**
387
     * TODO candidate for harmonization
388
     * new name getNomenclaturalStatus
389
     */
390
    @Override
391
    public List<NomenclaturalStatus> getAllNomenclaturalStatus(int limit, int start){
392
        return nomStatusDao.list(limit, start);
393
    }
394

    
395
    /**
396
     * TODO candidate for harmonization
397
     * new name getTypeDesignations
398
     */
399
    @Override
400
    public List<TypeDesignationBase> getAllTypeDesignations(int limit, int start){
401
        return typeDesignationDao.getAllTypeDesignations(limit, start);
402
    }
403

    
404
    @Override
405
    public TypeDesignationBase loadTypeDesignation(int id, List<String> propertyPaths){
406
        return typeDesignationDao.load(id, propertyPaths);
407
    }
408

    
409
    @Override
410
    public TypeDesignationBase loadTypeDesignation(UUID uuid, List<String> propertyPaths){
411
        return typeDesignationDao.load(uuid, propertyPaths);
412
    }
413

    
414
    /**
415
     * FIXME Candidate for harmonization
416
     * homotypicalGroupService.list
417
     */
418
    @Override
419
    public List<HomotypicalGroup> getAllHomotypicalGroups(int limit, int start){
420
        return homotypicalGroupDao.list(limit, start);
421
    }
422

    
423
    /**
424
     * FIXME Candidate for harmonization
425
     * remove
426
     */
427
    @Override
428
    @Deprecated
429
    public List<RelationshipBase> getAllRelationships(int limit, int start){
430
        return dao.getAllRelationships(limit, start);
431
    }
432

    
433

    
434

    
435
    @Override
436
    @Autowired
437
    protected void setDao(ITaxonNameDao dao) {
438
        this.dao = dao;
439
    }
440

    
441
    @Override
442
    public Pager<HybridRelationship> getHybridNames(INonViralName name,	HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
443
        Integer numberOfResults = dao.countHybridNames(name, type);
444

    
445
        List<HybridRelationship> results = new ArrayList<HybridRelationship>();
446
        if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
447
            results = dao.getHybridNames(name, type, pageSize, pageNumber,orderHints,propertyPaths);
448
        }
449

    
450
        return new DefaultPagerImpl<HybridRelationship>(pageNumber, numberOfResults, pageSize, results);
451
    }
452

    
453
    @Override
454
    public List<NameRelationship> listNameRelationships(TaxonName name,	Direction direction, NameRelationshipType type, Integer pageSize,
455
            Integer pageNumber, List<OrderHint> orderHints,	List<String> propertyPaths) {
456

    
457
        Integer numberOfResults = dao.countNameRelationships(name, direction, type);
458

    
459
        List<NameRelationship> results = new ArrayList<NameRelationship>();
460
        if (AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
461
            results = dao.getNameRelationships(name, direction, type, pageSize,	pageNumber, orderHints, propertyPaths);
462
        }
463
        return results;
464
    }
465

    
466

    
467
    protected LuceneSearch prepareFindByFuzzyNameSearch(Class<? extends CdmBase> clazz,
468
            INonViralName nvn,
469
            float accuracy,
470
            int maxNoOfResults,
471
            List<Language> languages,
472
            boolean highlightFragments) {
473
        String similarity = Float.toString(accuracy);
474
        String searchSuffix = "~" + similarity;
475

    
476

    
477
        Builder finalQueryBuilder = new Builder();
478
        finalQueryBuilder.setDisableCoord(false);
479
        Builder textQueryBuilder = new Builder();
480
        textQueryBuilder.setDisableCoord(false);
481

    
482
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
483
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
484

    
485
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
486
//    	luceneSearch.setSortFields(sortFields);
487

    
488
        // ---- search criteria
489
        luceneSearch.setCdmTypRestriction(clazz);
490

    
491
        FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
492
        if(nvn.getGenusOrUninomial() != null && !nvn.getGenusOrUninomial().equals("")) {
493
            fltq.addTerms(nvn.getGenusOrUninomial().toLowerCase(), "genusOrUninomial", accuracy, 3);
494
        } else {
495
            //textQuery.add(new RegexQuery (new Term ("genusOrUninomial", "^[a-zA-Z]*")), Occur.MUST_NOT);
496
            textQueryBuilder.add(queryFactory.newTermQuery("genusOrUninomial", "_null_", false), Occur.MUST);
497
        }
498

    
499
        if(nvn.getInfraGenericEpithet() != null && !nvn.getInfraGenericEpithet().equals("")){
500
            fltq.addTerms(nvn.getInfraGenericEpithet().toLowerCase(), "infraGenericEpithet", accuracy, 3);
501
        } else {
502
            //textQuery.add(new RegexQuery (new Term ("infraGenericEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
503
            textQueryBuilder.add(queryFactory.newTermQuery("infraGenericEpithet", "_null_", false), Occur.MUST);
504
        }
505

    
506
        if(nvn.getSpecificEpithet() != null && !nvn.getSpecificEpithet().equals("")){
507
            fltq.addTerms(nvn.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy, 3);
508
        } else {
509
            //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
510
            textQueryBuilder.add(queryFactory.newTermQuery("specificEpithet", "_null_", false), Occur.MUST);
511
        }
512

    
513
        if(nvn.getInfraSpecificEpithet() != null && !nvn.getInfraSpecificEpithet().equals("")){
514
            fltq.addTerms(nvn.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy, 3);
515
        } else {
516
            //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
517
            textQueryBuilder.add(queryFactory.newTermQuery("infraSpecificEpithet", "_null_", false), Occur.MUST);
518
        }
519

    
520
        if(nvn.getAuthorshipCache() != null && !nvn.getAuthorshipCache().equals("")){
521
            fltq.addTerms(nvn.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy, 3);
522
        } else {
523
            //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
524
        }
525

    
526
        textQueryBuilder.add(fltq, Occur.MUST);
527

    
528
        BooleanQuery textQuery = textQueryBuilder.build();
529
        finalQueryBuilder.add(textQuery, Occur.MUST);
530

    
531
        luceneSearch.setQuery(finalQueryBuilder.build());
532

    
533
        if(highlightFragments){
534
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
535
        }
536
        return luceneSearch;
537
    }
538

    
539
    protected LuceneSearch prepareFindByFuzzyNameCacheSearch(Class<? extends CdmBase> clazz,
540
            String name,
541
            float accuracy,
542
            int maxNoOfResults,
543
            List<Language> languages,
544
            boolean highlightFragments) {
545

    
546
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
547
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
548

    
549
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
550
//    	luceneSearch.setSortFields(sortFields);
551

    
552
        // ---- search criteria
553
        luceneSearch.setCdmTypRestriction(clazz);
554
        FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
555

    
556
        fltq.addTerms(name, "nameCache", accuracy, 3);
557

    
558
         BooleanQuery finalQuery = new BooleanQuery(false);
559

    
560
         finalQuery.add(fltq, Occur.MUST);
561

    
562
        luceneSearch.setQuery(finalQuery);
563

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

    
570
    protected LuceneSearch prepareFindByExactNameSearch(Class<? extends CdmBase> clazz,
571
            String name,
572
            boolean wildcard,
573
            List<Language> languages,
574
            boolean highlightFragments) {
575
        Builder textQueryBuilder = new Builder();
576

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

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

    
583
        // ---- search criteria
584
        luceneSearch.setCdmTypRestriction(clazz);
585

    
586
        if(name != null && !name.equals("")) {
587
            if(wildcard) {
588
                textQueryBuilder.add(new WildcardQuery(new Term("nameCache", name + "*")), Occur.MUST);
589
            } else {
590
                textQueryBuilder.add(queryFactory.newTermQuery("nameCache", name, false), Occur.MUST);
591
            }
592
        }
593

    
594
        luceneSearch.setQuery(textQueryBuilder.build());
595

    
596
        if(highlightFragments){
597
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
598
        }
599
        return luceneSearch;
600
    }
601

    
602
    @Override
603
    public List<SearchResult<TaxonName>> findByNameFuzzySearch(
604
            String name,
605
            float accuracy,
606
            List<Language> languages,
607
            boolean highlightFragments,
608
            List<String> propertyPaths,
609
            int maxNoOfResults) throws IOException, LuceneParseException {
610

    
611
        logger.info("Name to fuzzy search for : " + name);
612
        // parse the input name
613
        NonViralNameParserImpl parser = new NonViralNameParserImpl();
614
        INonViralName nvn = parser.parseFullName(name);
615
        if(name != null && !name.equals("") && nvn == null) {
616
            throw new LuceneParseException("Could not parse name " + name);
617
        }
618
        LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
619

    
620
        // --- execute search
621
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
622

    
623

    
624
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
625
        idFieldMap.put(CdmBaseType.NONVIRALNAME, "id");
626

    
627
        // --- initialize taxa, highlight matches ....
628
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
629

    
630
        @SuppressWarnings("rawtypes")
631
        List<SearchResult<TaxonName>> searchResults = searchResultBuilder.createResultSet(
632
                topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
633

    
634
        return searchResults;
635

    
636
    }
637

    
638
    @Override
639
    public List<DocumentSearchResult> findByNameFuzzySearch(
640
            String name,
641
            float accuracy,
642
            List<Language> languages,
643
            boolean highlightFragments,
644
            int maxNoOfResults) throws IOException, LuceneParseException {
645

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

    
655
        // --- execute search
656
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
657

    
658
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
659

    
660
        // --- initialize taxa, highlight matches ....
661
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
662

    
663
        @SuppressWarnings("rawtypes")
664
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
665

    
666
        return searchResults;
667
    }
668

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

    
677
        logger.info("Name to fuzzy search for : " + name);
678

    
679
        LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);
680

    
681
        // --- execute search
682
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
683
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
684

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

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

    
691
        return searchResults;
692
    }
693

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

    
702
        logger.info("Name to exact search for : " + name);
703

    
704
        LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);
705

    
706
        // --- execute search
707

    
708

    
709
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
710

    
711
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
712

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

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

    
719
        return searchResults;
720
    }
721

    
722
    @Override
723
    public Pager<NameRelationship> pageNameRelationships(TaxonName name, Direction direction, NameRelationshipType type, Integer pageSize,
724
            Integer pageNumber, List<OrderHint> orderHints,	List<String> propertyPaths) {
725
        List<NameRelationship> results = listNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
726
        return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
727
    }
728

    
729
    @Override
730
    public List<NameRelationship> listFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
731
        return listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
732
    }
733

    
734
    @Override
735
    public Pager<NameRelationship> pageFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
736
        List<NameRelationship> results = listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
737
        return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
738
    }
739

    
740
    @Override
741
    public List<NameRelationship> listToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
742
        return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
743
    }
744

    
745
    @Override
746
    public Pager<NameRelationship> pageToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
747
        List<NameRelationship> results = listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
748
        return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
749
    }
750

    
751
    @Override
752
    public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
753
            Integer pageSize, Integer pageNumber) {
754
        return getTypeDesignations(name, status, pageSize, pageNumber, null);
755
    }
756

    
757
    @Override
758
    public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
759
                Integer pageSize, Integer pageNumber, List<String> propertyPaths){
760
        Integer numberOfResults = dao.countTypeDesignations(name, status);
761

    
762
        List<TypeDesignationBase> results = new ArrayList<TypeDesignationBase>();
763
        if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) {
764
            results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);
765
        }
766

    
767
        return new DefaultPagerImpl<TypeDesignationBase>(pageNumber, numberOfResults, pageSize, results);
768
    }
769

    
770
    /**
771
     * FIXME Candidate for harmonization
772
     * rename search
773
     */
774
    @Override
775
    public Pager<TaxonName> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,	Integer pageNumber, List<OrderHint> orderHints,
776
            List<String> propertyPaths) {
777
        Integer numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
778

    
779
        List<TaxonName> results = new ArrayList<TaxonName>();
780
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
781
            results = dao.searchNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber, orderHints, propertyPaths);
782
        }
783

    
784
        return new DefaultPagerImpl<TaxonName>(pageNumber, numberOfResults, pageSize, results);
785
    }
786

    
787
    @Override
788
    public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames(Integer limit, String pattern) {
789
        return dao.getUuidAndTitleCacheOfNames(limit, pattern);
790
    }
791

    
792
    @Override
793
    public Pager<TaxonName> findByName(Class<? extends TaxonName> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria, Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
794
         Long numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
795

    
796
         List<TaxonName> results = new ArrayList<>();
797
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
798
                results = dao.findByName(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
799
         }
800

    
801
         return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
802
    }
803

    
804
    @Override
805
    public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
806
        return homotypicalGroupDao.findByUuid(uuid);
807
    }
808

    
809
    @Override
810
    @Transactional(readOnly = false)
811
    public void updateTitleCache(Class<? extends TaxonName> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonName> cacheStrategy, IProgressMonitor monitor) {
812
        if (clazz == null){
813
            clazz = TaxonName.class;
814
        }
815
        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
816
    }
817

    
818

    
819
    @Override
820
    protected void setOtherCachesNull(TaxonName name) {
821
         if (! name.isProtectedNameCache()){
822
             name.setNameCache(null, false);
823
        }
824
        if (! name.isProtectedAuthorshipCache()){
825
            name.setAuthorshipCache(null, false);
826
        }
827
        if (! name.isProtectedFullTitleCache()){
828
            name.setFullTitleCache(null, false);
829
        }
830

    
831
    }
832

    
833
    @Override
834
    public List<TaggedText> getTaggedName(UUID uuid) {
835
        TaxonName taxonName = dao.load(uuid);
836
        List<TaggedText> taggedName = taxonName.getTaggedName();
837
        return taggedName;
838
    }
839

    
840

    
841
    public DeleteResult isDeletable(TaxonName name, DeleteConfiguratorBase config){
842
        DeleteResult result = new DeleteResult();
843

    
844
        NameDeletionConfigurator nameConfig = null;
845
        if (config instanceof NameDeletionConfigurator){
846
            nameConfig = (NameDeletionConfigurator) config;
847
        }else{
848
             result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
849
             result.setError();
850
             return result;
851
        }
852

    
853
        if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
854
            HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
855

    
856
            if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
857
                result.addException(new Exception( "Name can't be deleted as it is a basionym."));
858
                result.setAbort();
859
            }
860
            if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){
861
                result.addException(new Exception( "Name can't be deleted as it has a basionym."));
862
                result.setAbort();
863
            }
864
            Set<NameRelationship> relationships = name.getNameRelations();
865
            for (NameRelationship rel: relationships){
866
                if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
867
                    result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
868
                    result.setAbort();
869
                    break;
870
                }
871
            }
872
        }
873

    
874
        //concepts
875
        if (! name.getTaxonBases().isEmpty()){
876
            result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
877
            result.setAbort();
878
        }
879

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

    
911
                if (typeDesignation.getTypeName().equals(name)){
912
                    String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
913
                    result.addException(new ReferencedObjectUndeletableException(message));
914
                    result.addRelatedObject(referencingObject);
915
                    result.setAbort();
916
                }
917
            }
918
            //DeterminationEvent#taxonName
919
            else if (referencingObject.isInstanceOf(DeterminationEvent.class)){
920
                String message = "Name can't be deleted as it is used as a determination event";
921
                result.addException(new ReferencedObjectUndeletableException(message));
922
                result.addRelatedObject(referencingObject);
923
                result.setAbort();
924
        }
925
        }
926

    
927
        //TODO inline references
928

    
929

    
930
        if (!nameConfig.isIgnoreIsReplacedSynonymFor() && name.isReplacedSynonym()){
931
            String message = "Name can't be deleted as it is a replaced synonym.";
932
            result.addException(new Exception(message));
933
            result.setAbort();
934
        }
935
        if (!nameConfig.isIgnoreHasReplacedSynonym() && (name.getReplacedSynonyms().size()>0)){
936
            String message = "Name can't be deleted as it has a replaced synonym.";
937
            result.addException(new Exception(message));
938
            result.setAbort();
939
        }
940
        return result;
941

    
942
    }
943

    
944

    
945
    @Override
946
    public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config){
947
        TaxonName name = this.load(nameUUID);
948
        return isDeletable(name, config);
949
    }
950

    
951
    @Override
952
    @Transactional(readOnly = true)
953
    public UpdateResult setAsGroupsBasionym(UUID nameUuid) {
954
        TaxonName name = dao.load(nameUuid);
955
        UpdateResult result = new UpdateResult();
956
        name.setAsGroupsBasionym();
957
        result.addUpdatedObject(name);
958
        return result;
959

    
960
    }
961

    
962
    @Override
963
    public List<HashMap<String,String>> getNameRecords(){
964

    
965
		return dao.getNameRecords();
966

    
967
    }
968

    
969
}
(77-77/105)