Project

General

Profile

Download (40.4 KB) Statistics
| Branch: | Tag: | Revision:
1
// $Id$
2
/**
3
* Copyright (C) 2007 EDIT
4
* European Distributed Institute of Taxonomy
5
* http://www.e-taxonomy.eu
6
*
7
* The contents of this file are subject to the Mozilla Public License Version 1.1
8
* See LICENSE.TXT at the top of this package for the full license terms.
9
*/
10

    
11
package eu.etaxonomy.cdm.api.service;
12

    
13
import java.io.IOException;
14
import java.util.ArrayList;
15
import java.util.Collection;
16
import java.util.HashMap;
17
import java.util.HashSet;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.Set;
21
import java.util.UUID;
22

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

    
39
import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
40
import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
41
import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
42
import eu.etaxonomy.cdm.api.service.pager.Pager;
43
import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
44
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
45
import eu.etaxonomy.cdm.api.service.search.DocumentSearchResult;
46
import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
47
import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
48
import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
49
import eu.etaxonomy.cdm.api.service.search.QueryFactory;
50
import eu.etaxonomy.cdm.api.service.search.SearchResult;
51
import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
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.NameRelationship;
65
import eu.etaxonomy.cdm.model.name.NameRelationshipType;
66
import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
67
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
68
import eu.etaxonomy.cdm.model.name.NonViralName;
69
import eu.etaxonomy.cdm.model.name.Rank;
70
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
71
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
72
import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
73
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
74
import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
75
import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
76
import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
77
import eu.etaxonomy.cdm.persistence.dao.common.IReferencedEntityDao;
78
import eu.etaxonomy.cdm.persistence.dao.common.ITermVocabularyDao;
79
import eu.etaxonomy.cdm.persistence.dao.name.IHomotypicalGroupDao;
80
import eu.etaxonomy.cdm.persistence.dao.name.INomenclaturalStatusDao;
81
import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
82
import eu.etaxonomy.cdm.persistence.dao.name.ITypeDesignationDao;
83
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
84
import eu.etaxonomy.cdm.persistence.query.MatchMode;
85
import eu.etaxonomy.cdm.persistence.query.OrderHint;
86
import eu.etaxonomy.cdm.strategy.cache.TaggedText;
87
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
88
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
89

    
90

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

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

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

    
121
//********************* METHODS ****************************************************************//
122

    
123
    /* (non-Javadoc)
124
     * @see eu.etaxonomy.cdm.api.service.ServiceBase#delete(eu.etaxonomy.cdm.model.common.CdmBase)
125
     */
126
    @Override
127
    @Transactional(readOnly = false)
128
    public DeleteResult delete(UUID nameUUID){
129
        NameDeletionConfigurator config = new NameDeletionConfigurator();
130
        DeleteResult result = delete(nameUUID, config);
131
        return result;
132
    }
133

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

    
139
    /* (non-Javadoc)
140
     * @see eu.etaxonomy.cdm.api.service.INameService#delete(eu.etaxonomy.cdm.model.name.TaxonNameBase, eu.etaxonomy.cdm.api.service.NameDeletionConfigurator)
141
     */
142
    @Override
143
    @Transactional(readOnly = false)
144
    public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {
145
    	DeleteResult result = new DeleteResult();
146
       	TaxonNameBase name = dao.load(nameUUID);
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

    
179
	        try{
180
	        UUID nameUuid = dao.delete(name);
181

    
182
	        }catch(Exception e){
183
	        	result.addException(e);
184
	        	result.setError();
185
	        }
186
	        return result;
187
        }
188

    
189

    
190

    
191
        return result;
192
    }
193

    
194
    /* (non-Javadoc)
195
     * @see eu.etaxonomy.cdm.api.service.INameService#deleteTypeDesignation(eu.etaxonomy.cdm.model.name.TaxonNameBase, eu.etaxonomy.cdm.model.name.TypeDesignationBase)
196
     */
197
    @Override
198
    public DeleteResult deleteTypeDesignation(TaxonNameBase 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<TaxonNameBase> nameSet = new HashSet<TaxonNameBase>(typeDesignation.getTypifiedNames());
217
            for (Object o : nameSet){
218
                TaxonNameBase singleName = CdmBase.deproxy(o, TaxonNameBase.class);
219
                removeSingleDesignation(singleName, typeDesignation);
220
            }
221
        }
222
        result.addUpdatedObject(name);
223
        return result;
224
    }
225

    
226

    
227
    @Override
228
    public DeleteResult deleteTypeDesignation(UUID nameUuid, UUID typeDesignationUuid){
229
        TaxonNameBase nameBase = load(nameUuid);
230
        TypeDesignationBase typeDesignation = HibernateProxyHelper.deproxy(referencedEntityDao.load(typeDesignationUuid), TypeDesignationBase.class);
231
        return deleteTypeDesignation(nameBase, typeDesignation);
232
    }
233

    
234
    /**
235
     * @param name
236
     * @param typeDesignation
237
     */
238
    private void removeSingleDesignation(TaxonNameBase name, TypeDesignationBase typeDesignation) {
239
        name.removeTypeDesignation(typeDesignation);
240
        if (typeDesignation.getTypifiedNames().isEmpty()){
241
            typeDesignation.removeType();
242
            typeDesignationDao.delete(typeDesignation);
243
        }
244
    }
245

    
246

    
247

    
248
    /**
249
     * @param name
250
     * @param config
251
     */
252
    private void removeNameRelationshipsByDeleteConfig(TaxonNameBase<?,?> name, NameDeletionConfigurator config) {
253
        try {
254
            if (config.isRemoveAllNameRelationships()){
255
                Set<NameRelationship> rels = getModifiableSet(name.getNameRelations());
256
                for (NameRelationship rel : rels){
257
                    name.removeNameRelationship(rel);
258
                }
259
            }else{
260
                //relations to this name
261
                Set<NameRelationship> rels = getModifiableSet(name.getRelationsToThisName());
262
                for (NameRelationship rel : rels){
263
                    if (config.isIgnoreHasBasionym() && NameRelationshipType.BASIONYM().equals(rel.getType() )){
264
                            name.removeNameRelationship(rel);
265
                    }else if (config.isIgnoreHasReplacedSynonym() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
266
                        name.removeNameRelationship(rel);
267
                    }
268
                }
269
                //relations from this name
270
                rels = getModifiableSet(name.getRelationsFromThisName());
271
                for (NameRelationship rel : rels){
272
                    if (config.isIgnoreIsBasionymFor() && NameRelationshipType.BASIONYM().equals(rel.getType())  ){
273
                        name.removeNameRelationship(rel);
274
                    }else if (config.isIgnoreIsReplacedSynonymFor() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
275
                        name.removeNameRelationship(rel);
276
                    }
277
                }
278

    
279
            }
280
        } catch (Exception e) {
281
            throw new RuntimeException(e);
282
        }
283
    }
284

    
285
    /**
286
     * @param name
287
     * @return
288
     */
289
    private Set<NameRelationship> getModifiableSet(Set<NameRelationship> relations) {
290
        Set<NameRelationship> rels = new HashSet<NameRelationship>();
291
        for (NameRelationship rel : relations){
292
            rels.add(rel);
293
        }
294
        return rels;
295
    }
296

    
297
//********************* METHODS ****************************************************************//
298

    
299
    /**
300
     * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions
301
     * duplicate of findByName
302
     */
303
    @Override
304
    @Deprecated
305
    public List getNamesByName(String name){
306
        return super.findCdmObjectsByTitle(name);
307
    }
308

    
309
    /**
310
     * TODO candidate for harmonization
311
     * new name findByName
312
     */
313
    @Override
314
    public List<NonViralName> getNamesByNameCache(String nameCache){
315
        List result = dao.findByName(nameCache, MatchMode.EXACT, null, null, null, null);
316
        return result;
317
    }
318

    
319

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

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

    
344
    /**
345
     * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions
346
     * Replace by load(UUID, propertyPaths)
347
     */
348
    @Override
349
    @Deprecated
350
    public NonViralName findNameByUuid(UUID uuid, List<String> propertyPaths){
351
        return (NonViralName)dao.findByUuid(uuid, null ,propertyPaths);
352
    }
353

    
354
    /**
355
     * TODO candidate for harmonization
356
     */
357
    @Override
358
    public List getNamesByName(String name, CdmBase sessionObject){
359
        return super.findCdmObjectsByTitle(name, sessionObject);
360
    }
361

    
362
    /**
363
     * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions
364
     * duplicate of findByTitle(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths)
365
     */
366
    @Override
367
    @Deprecated
368
    public List findNamesByTitle(String title){
369
        return super.findCdmObjectsByTitle(title);
370
    }
371

    
372
    /**
373
     * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions
374
     * duplicate of findByTitle()
375
     */
376
    @Override
377
    @Deprecated
378
    public List findNamesByTitle(String title, CdmBase sessionObject){
379
        return super.findCdmObjectsByTitle(title, sessionObject);
380
    }
381

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

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

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

    
412
    /**
413
     * TODO candidate for harmonization
414
     * new name getNames
415
     */
416
    public List<TaxonNameBase> getAllNames(int limit, int start){
417
        return dao.list(limit, start);
418
    }
419

    
420
    /**
421
     * TODO candidate for harmonization
422
     * new name getNomenclaturalStatus
423
     */
424
    @Override
425
    public List<NomenclaturalStatus> getAllNomenclaturalStatus(int limit, int start){
426
        return nomStatusDao.list(limit, start);
427
    }
428

    
429
    /**
430
     * TODO candidate for harmonization
431
     * new name getTypeDesignations
432
     */
433
    @Override
434
    public List<TypeDesignationBase> getAllTypeDesignations(int limit, int start){
435
        return typeDesignationDao.getAllTypeDesignations(limit, start);
436
    }
437
      /**
438
     * FIXME Candidate for harmonization
439
     * homotypicalGroupService.list
440
     */
441
    @Override
442
    public List<HomotypicalGroup> getAllHomotypicalGroups(int limit, int start){
443
        return homotypicalGroupDao.list(limit, start);
444
    }
445

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

    
456

    
457

    
458
    @Override
459
    @Autowired
460
    protected void setDao(ITaxonNameDao dao) {
461
        this.dao = dao;
462
    }
463

    
464
    @Override
465
    public Pager<HybridRelationship> getHybridNames(NonViralName name,	HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
466
        Integer numberOfResults = dao.countHybridNames(name, type);
467

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

    
473
        return new DefaultPagerImpl<HybridRelationship>(pageNumber, numberOfResults, pageSize, results);
474
    }
475

    
476
    @Override
477
    public List<NameRelationship> listNameRelationships(TaxonNameBase name,	Direction direction, NameRelationshipType type, Integer pageSize,
478
            Integer pageNumber, List<OrderHint> orderHints,	List<String> propertyPaths) {
479

    
480
        Integer numberOfResults = dao.countNameRelationships(name, direction, type);
481

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

    
489

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

    
499

    
500
        Builder finalQueryBuilder = new Builder();
501
        finalQueryBuilder.setDisableCoord(false);
502
        Builder textQueryBuilder = new Builder();
503
        textQueryBuilder.setDisableCoord(false);
504

    
505
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
506
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
507

    
508
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
509
//    	luceneSearch.setSortFields(sortFields);
510

    
511
        // ---- search criteria
512
        luceneSearch.setCdmTypRestriction(clazz);
513

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

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

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

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

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

    
549
        textQueryBuilder.add(fltq, Occur.MUST);
550

    
551
        BooleanQuery textQuery = textQueryBuilder.build();
552
        finalQueryBuilder.add(textQuery, Occur.MUST);
553

    
554
        luceneSearch.setQuery(finalQueryBuilder.build());
555

    
556
        if(highlightFragments){
557
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
558
        }
559
        return luceneSearch;
560
    }
561

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

    
569
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
570
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
571

    
572
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
573
//    	luceneSearch.setSortFields(sortFields);
574

    
575
        // ---- search criteria
576
        luceneSearch.setCdmTypRestriction(clazz);
577
        FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
578

    
579
        fltq.addTerms(name, "nameCache", accuracy, 3);
580

    
581
         BooleanQuery finalQuery = new BooleanQuery(false);
582

    
583
         finalQuery.add(fltq, Occur.MUST);
584

    
585
        luceneSearch.setQuery(finalQuery);
586

    
587
        if(highlightFragments){
588
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
589
        }
590
        return luceneSearch;
591
    }
592

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

    
600
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
601
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
602

    
603
//    	SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
604
//    	luceneSearch.setSortFields(sortFields);
605

    
606
        // ---- search criteria
607
        luceneSearch.setCdmTypRestriction(clazz);
608

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

    
617
        luceneSearch.setQuery(textQueryBuilder.build());
618

    
619
        if(highlightFragments){
620
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
621
        }
622
        return luceneSearch;
623
    }
624

    
625
    @Override
626
    public List<SearchResult<TaxonNameBase>> findByNameFuzzySearch(
627
            String name,
628
            float accuracy,
629
            List<Language> languages,
630
            boolean highlightFragments,
631
            List<String> propertyPaths,
632
            int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
633

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

    
643
        // --- execute search
644
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
645

    
646

    
647
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
648
        idFieldMap.put(CdmBaseType.NONVIRALNAME, "id");
649

    
650
        // --- initialize taxa, highlight matches ....
651
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
652

    
653
        @SuppressWarnings("rawtypes")
654
        List<SearchResult<TaxonNameBase>> searchResults = searchResultBuilder.createResultSet(
655
                topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
656

    
657
        return searchResults;
658

    
659
    }
660

    
661
    @Override
662
    public List<DocumentSearchResult> findByNameFuzzySearch(
663
            String name,
664
            float accuracy,
665
            List<Language> languages,
666
            boolean highlightFragments,
667
            int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
668

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

    
678
        // --- execute search
679
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
680

    
681
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
682

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

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

    
689
        return searchResults;
690
    }
691

    
692
    @Override
693
    public List<DocumentSearchResult> findByFuzzyNameCacheSearch(
694
            String name,
695
            float accuracy,
696
            List<Language> languages,
697
            boolean highlightFragments,
698
            int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
699

    
700
        logger.info("Name to fuzzy search for : " + name);
701

    
702
        LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);
703

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

    
708
        // --- initialize taxa, highlight matches ....
709
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
710

    
711
        @SuppressWarnings("rawtypes")
712
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
713

    
714
        return searchResults;
715
    }
716

    
717
    @Override
718
    public List<DocumentSearchResult> findByNameExactSearch(
719
            String name,
720
            boolean wildcard,
721
            List<Language> languages,
722
            boolean highlightFragments,
723
            int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
724

    
725
        logger.info("Name to exact search for : " + name);
726

    
727
        LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);
728

    
729
        // --- execute search
730

    
731

    
732
        TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
733

    
734
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
735

    
736
        // --- initialize taxa, highlight matches ....
737
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
738

    
739
        @SuppressWarnings("rawtypes")
740
        List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
741

    
742
        return searchResults;
743
    }
744

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

    
752
    @Override
753
    public List<NameRelationship> listFromNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
754
        return listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
755
    }
756

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

    
763
    @Override
764
    public List<NameRelationship> listToNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
765
        return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
766
    }
767

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

    
774
    @Override
775
    public Pager<TypeDesignationBase> getTypeDesignations(TaxonNameBase name, SpecimenTypeDesignationStatus status,
776
            Integer pageSize, Integer pageNumber) {
777
        return getTypeDesignations(name, status, pageSize, pageNumber, null);
778
    }
779

    
780
    @Override
781
    public Pager<TypeDesignationBase> getTypeDesignations(TaxonNameBase name, SpecimenTypeDesignationStatus status,
782
                Integer pageSize, Integer pageNumber, List<String> propertyPaths){
783
        Integer numberOfResults = dao.countTypeDesignations(name, status);
784

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

    
790
        return new DefaultPagerImpl<TypeDesignationBase>(pageNumber, numberOfResults, pageSize, results);
791
    }
792

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

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

    
807
        return new DefaultPagerImpl<TaxonNameBase>(pageNumber, numberOfResults, pageSize, results);
808
    }
809

    
810
    @Override
811
    public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames(Integer limit, String pattern) {
812
        return dao.getUuidAndTitleCacheOfNames(limit, pattern);
813
    }
814

    
815
    @Override
816
    public Pager<TaxonNameBase> findByName(Class<? extends TaxonNameBase> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria, Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
817
        Integer numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
818

    
819
         List<TaxonNameBase> results = new ArrayList<TaxonNameBase>();
820
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
821
                results = dao.findByName(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
822
         }
823

    
824
          return new DefaultPagerImpl<TaxonNameBase>(pageNumber, numberOfResults, pageSize, results);
825
    }
826

    
827
    @Override
828
    public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
829
        return homotypicalGroupDao.findByUuid(uuid);
830
    }
831

    
832
    @Override
833
    @Transactional(readOnly = false)
834
    public void updateTitleCache(Class<? extends TaxonNameBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonNameBase> cacheStrategy, IProgressMonitor monitor) {
835
        if (clazz == null){
836
            clazz = TaxonNameBase.class;
837
        }
838
        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
839
    }
840

    
841

    
842
    @Override
843
    protected void setOtherCachesNull(TaxonNameBase name) {
844
        if (name.isInstanceOf(NonViralName.class)){
845
            NonViralName<?> nvn = CdmBase.deproxy(name, NonViralName.class);
846
            if (! nvn.isProtectedNameCache()){
847
                nvn.setNameCache(null, false);
848
            }
849
            if (! nvn.isProtectedAuthorshipCache()){
850
                nvn.setAuthorshipCache(null, false);
851
            }
852
            if (! nvn.isProtectedFullTitleCache()){
853
                nvn.setFullTitleCache(null, false);
854
            }
855
        }
856
    }
857

    
858
    @Override
859
    public List<TaggedText> getTaggedName(UUID uuid) {
860
        TaxonNameBase<?,?> taxonNameBase = dao.load(uuid);
861
        List<TaggedText> taggedName = taxonNameBase.getTaggedName();
862
        return taggedName;
863
    }
864

    
865
    @Override
866
    public DeleteResult isDeletable(TaxonNameBase name, DeleteConfiguratorBase config){
867
    	DeleteResult result = new DeleteResult();
868
    	//name = this.load(name.getUuid());
869
    	NameDeletionConfigurator nameConfig = null;
870
    	if (config instanceof NameDeletionConfigurator){
871
    		nameConfig = (NameDeletionConfigurator) config;
872
    	}else{
873
    		 result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
874
    		 result.setError();
875
    		 return result;
876
    	}
877

    
878
    	if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
879
    		HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
880

    
881
    		if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
882
       		 	result.addException(new Exception( "Name can't be deleted as it is a basionym."));
883
       		 	result.setAbort();
884
            }
885
            if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){
886
            	result.addException(new Exception( "Name can't be deleted as it has a basionym."));
887
            	result.setAbort();
888
            }
889
            Set<NameRelationship> relationships = name.getNameRelations();
890
            for (NameRelationship rel: relationships){
891
            	if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
892
            		result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
893
            		result.setAbort();
894
            		break;
895
            	}
896
            }
897
        }
898

    
899
        //concepts
900
        if (! name.getTaxonBases().isEmpty()){
901
        	result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
902
        	result.setAbort();
903
        }
904

    
905
        //hybrid relationships
906
        if (name.isInstanceOf(NonViralName.class)){
907
            NonViralName nvn = CdmBase.deproxy(name, NonViralName.class);
908
            if (! nvn.getHybridParentRelations().isEmpty()){
909
            	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."));
910
            	result.setAbort();
911
            }
912
        }
913
     	Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);
914
        for (CdmBase referencingObject : referencingObjects){
915
            //DerivedUnit?.storedUnder
916
            if (referencingObject.isInstanceOf(DerivedUnit.class)){
917
                String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
918
                message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());
919
                result.addException(new ReferencedObjectUndeletableException(message));
920
                result.addRelatedObject(referencingObject);
921
                result.setAbort();
922
            }
923
            //DescriptionElementSource#nameUsedInSource
924
            else if (referencingObject.isInstanceOf(DescriptionElementSource.class)){
925
                String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
926
                result.addException(new ReferencedObjectUndeletableException(message));
927
                result.addRelatedObject(referencingObject);
928
                result.setAbort();
929
            }
930
            //NameTypeDesignation#typeName
931
            else if (referencingObject.isInstanceOf(NameTypeDesignation.class)){
932
                String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
933
                result.addException(new ReferencedObjectUndeletableException(message));
934
                result.addRelatedObject(referencingObject);
935
                result.setAbort();
936
            }
937
            //DeterminationEvent#taxonName
938
            else if (referencingObject.isInstanceOf(DeterminationEvent.class)){
939
                String message = "Name can't be deleted as it is used as a determination event";
940
                result.addException(new ReferencedObjectUndeletableException(message));
941
                result.addRelatedObject(referencingObject);
942
                result.setAbort();
943
        }
944
        }
945

    
946
        //TODO inline references
947

    
948

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

    
961
    }
962

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

    
972
    }
973

    
974
    @Override
975
    public List<HashMap<String,String>> getNameRecords(){
976

    
977
		return dao.getNameRecords();
978

    
979
    }
980

    
981
}
(75-75/98)